diff --git a/README.md b/README.md index 804bf4e..9a7dd0f 100644 --- a/README.md +++ b/README.md @@ -173,3 +173,12 @@ The solution for "sixteen" is: The sum of the version numbers is 1012 The output of the encoded input is 2223947372407 ``` + +### Day seventeen + +```sh +$ ./aoc2021 --seventeen +The solution for "seventeen" is: +The max height is 35511 +There are 3282 initial velocities that will hit the target +``` diff --git a/main.go b/main.go index 586cf5c..75d9a9c 100644 --- a/main.go +++ b/main.go @@ -14,6 +14,7 @@ import ( "unsupervised.ca/aoc2021/nine" "unsupervised.ca/aoc2021/one" "unsupervised.ca/aoc2021/seven" + "unsupervised.ca/aoc2021/seventeen" "unsupervised.ca/aoc2021/six" "unsupervised.ca/aoc2021/sixteen" "unsupervised.ca/aoc2021/ten" @@ -70,6 +71,8 @@ func main() { day = fifteen.Init("fifteen/input.txt") case "sixteen": day = sixteen.Init("sixteen/input.txt") + case "seventeen": + day = seventeen.Init("seventeen/input.txt") default: fmt.Printf("%q does not have a solution.\n", flagParts[1]) help() diff --git a/seventeen/input.txt b/seventeen/input.txt new file mode 100644 index 0000000..c6f8206 --- /dev/null +++ b/seventeen/input.txt @@ -0,0 +1 @@ +target area: x=14..50, y=-267..-225 \ No newline at end of file diff --git a/seventeen/main.go b/seventeen/main.go new file mode 100644 index 0000000..f35d5aa --- /dev/null +++ b/seventeen/main.go @@ -0,0 +1,28 @@ +package seventeen + +import "fmt" + +type Seventeen struct { + probe probe +} + +func Init(filepath string) *Seventeen { + seventeen := &Seventeen{ + probe: probe{}, + } + + seventeen.probe.load(filepath) + return seventeen +} + +func (d *Seventeen) Answer() string { + ivs := d.probe.intialVelocities() + + return fmt.Sprintf("The max height is %d", maxHeight(d.probe, ivs)) +} + +func (d *Seventeen) FollowUp() string { + ivs := d.probe.intialVelocities() + + return fmt.Sprintf("There are %d initial velocities that will hit the target", len(ivs)) +} diff --git a/seventeen/probe.go b/seventeen/probe.go new file mode 100644 index 0000000..63a0565 --- /dev/null +++ b/seventeen/probe.go @@ -0,0 +1,180 @@ +package seventeen + +import ( + "bufio" + "os" + "strconv" + "strings" +) + +type target struct { + xMin int + xMax int + yMin int + yMax int +} + +type velocity struct { + x int + y int +} + +func (v *velocity) step() velocity { + newVelocity := velocity{} + + if v.x > 0 { + newVelocity.x = v.x - 1 + } else if v.x < 0 { + newVelocity.x = v.x + 1 + } + + newVelocity.y = v.y - 1 + + return newVelocity +} + +type position struct { + x int + y int +} + +func (p *position) step(v velocity) position { + newPosition := position{ + x: p.x + v.x, + y: p.y + v.y, + } + + return newPosition +} + +type probe struct { + target target + v velocity + p position +} + +func (p *probe) load(filename string) error { + file, err := os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + p.target = target{} + + scanner := bufio.NewScanner(file) + for scanner.Scan() { + parts := strings.Split(scanner.Text(), "=") + + xParts := strings.Split(strings.Split(parts[1], ",")[0], "..") + xMin, err := strconv.Atoi(xParts[0]) + if err != nil { + return err + } + xMax, err := strconv.Atoi(xParts[1]) + if err != nil { + return err + } + + yParts := strings.Split(parts[2], "..") + yMin, err := strconv.Atoi(yParts[0]) + if err != nil { + return err + } + yMax, err := strconv.Atoi(yParts[1]) + if err != nil { + return err + } + + p.target.xMin = xMin + p.target.xMax = xMax + p.target.yMin = yMin + p.target.yMax = yMax + } + return nil +} + +func (p *probe) intialVelocities() []velocity { + velocitiesToTarget := []velocity{} + for x := 0; x <= p.target.xMax; x++ { + if maxDist(x) <= p.target.xMin { + continue + } + + for y := 0; y <= (p.target.yMin * -1); y++ { + // Test +y + v := velocity{x: x, y: y} + if p.hitTarget(v) { + velocitiesToTarget = append(velocitiesToTarget, v) + } + + // Test -y + if y == 0 { + continue + } + v = velocity{x: x, y: (-1 * y)} + if p.hitTarget(v) { + velocitiesToTarget = append(velocitiesToTarget, v) + } + } + } + + return velocitiesToTarget +} + +func (p *probe) pathToTarget(iv velocity) []position { + v := velocity{x: iv.x, y: iv.y} + pos := position{x: 0, y: 0} + path := []position{pos} + for pos.x < p.target.xMax && pos.y > p.target.yMin { + pos = pos.step(v) + path = append(path, pos) + v = v.step() + + // In x range! + if pos.x >= p.target.xMin && pos.x <= p.target.xMax { + // In y range! + if pos.y <= p.target.yMax && pos.y >= p.target.yMin { + break + } + } + } + + return path +} + +func (p *probe) hitTarget(iv velocity) bool { + v := velocity{x: iv.x, y: iv.y} + position := position{x: 0, y: 0} + for position.x < p.target.xMax && position.y > p.target.yMin { + position = position.step(v) + v = v.step() + + // In x range! + if position.x >= p.target.xMin && position.x <= p.target.xMax { + // In y range! + if position.y <= p.target.yMax && position.y >= p.target.yMin { + return true + } + } + } + return false +} + +func maxDist(x int) int { + return x * (x + 1) / 2 +} + +func maxHeight(p probe, ivs []velocity) int { + max := 0 + for _, v := range ivs { + path := p.pathToTarget(v) + for _, pos := range path { + if pos.y > max { + max = pos.y + } + } + } + + return max +} diff --git a/seventeen/probe_test.go b/seventeen/probe_test.go new file mode 100644 index 0000000..c53643f --- /dev/null +++ b/seventeen/probe_test.go @@ -0,0 +1,58 @@ +package seventeen + +import ( + "testing" +) + +func Test_read(t *testing.T) { + p := probe{} + if err := p.load("test_input.txt"); err != nil { + t.Log(err) + t.FailNow() + } + + if p.target.xMin != 20 { + t.Logf("Expected 20, found %d", p.target.xMin) + t.Fail() + } + + if p.target.xMax != 30 { + t.Logf("Expected 30, found %d", p.target.xMax) + t.Fail() + } + + if p.target.yMax != -5 { + t.Logf("Expected 20, found %d", p.target.xMin) + t.Fail() + } + + if p.target.yMin != -10 { + t.Logf("Expected 30, found %d", p.target.xMax) + t.Fail() + } +} + +func Test_initialVelocities(t *testing.T) { + p := probe{} + p.load("test_input.txt") + + ivs := p.intialVelocities() + + if len(ivs) != 112 { + t.Logf("Expected 112 initial velocities to target, found %d", len(ivs)) + t.Fail() + } +} + +func Test_maxHeight(t *testing.T) { + p := probe{} + p.load("test_input.txt") + + ivs := p.intialVelocities() + max := maxHeight(p, ivs) + + if max != 45 { + t.Logf("Expected max height of 45, found %d", max) + t.Fail() + } +} diff --git a/seventeen/test_input.txt b/seventeen/test_input.txt new file mode 100644 index 0000000..f40609b --- /dev/null +++ b/seventeen/test_input.txt @@ -0,0 +1 @@ +target area: x=20..30, y=-10..-5 \ No newline at end of file