Files
aoc2021/seventeen/probe.go
2021-12-18 19:50:49 +00:00

181 lines
3.0 KiB
Go

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
}