Solution for Day 17
This commit is contained in:
@@ -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
|
||||
```
|
||||
|
3
main.go
3
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()
|
||||
|
1
seventeen/input.txt
Normal file
1
seventeen/input.txt
Normal file
@@ -0,0 +1 @@
|
||||
target area: x=14..50, y=-267..-225
|
28
seventeen/main.go
Normal file
28
seventeen/main.go
Normal file
@@ -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))
|
||||
}
|
180
seventeen/probe.go
Normal file
180
seventeen/probe.go
Normal file
@@ -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
|
||||
}
|
58
seventeen/probe_test.go
Normal file
58
seventeen/probe_test.go
Normal file
@@ -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()
|
||||
}
|
||||
}
|
1
seventeen/test_input.txt
Normal file
1
seventeen/test_input.txt
Normal file
@@ -0,0 +1 @@
|
||||
target area: x=20..30, y=-10..-5
|
Reference in New Issue
Block a user