181 lines
3.0 KiB
Go
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
|
|
}
|