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 }