package main import ( "fmt" h "git.bullercodeworks.com/brian/adventofcode/helpers" "golang.org/x/exp/slices" ) func main() { inp := h.StdinToStringSlice() part1(inp) fmt.Println() part2(inp) } func part1(inp []string) { //testMin, testMax := float64(7), float64(27) // Test Input testMin, testMax := float64(200000000000000), float64(400000000000000) var all []*hailstone for _, i := range inp { all = append(all, NewHailstone(i)) } var count int for i := 0; i < len(all)-1; i++ { for j := i + 1; j < len(all); j++ { x, y := findIntersection(all[i], all[j]) if x >= testMin && x <= testMax && y >= testMin && y <= testMax { count++ } } } fmt.Println("# Part 1") fmt.Println(count) } func part2(inp []string) { var all []*hailstone for _, i := range inp { all = append(all, NewHailstone(i)) } fmt.Println("# Part 2") fmt.Println(findThrow(all)) } func findThrow(all []*hailstone) int { maybeX, maybeY, maybeZ := []int{}, []int{}, []int{} for i := 0; i < len(all)-1; i++ { for j := i + 1; j < len(all); j++ { a, b := all[i], all[j] if a.DX == b.DX { nextMaybe := findMatchingVelocity(int(b.X-a.X), int(a.DX)) if len(maybeX) == 0 { maybeX = nextMaybe } else { maybeX = getIntersect(maybeX, nextMaybe) } } if a.DY == b.DY { nextMaybe := findMatchingVelocity(int(b.Y-a.Y), int(a.DY)) if len(maybeY) == 0 { maybeY = nextMaybe } else { maybeY = getIntersect(maybeY, nextMaybe) } } if a.DZ == b.DZ { nextMaybe := findMatchingVelocity(int(b.Z-a.Z), int(a.DZ)) if len(maybeZ) == 0 { maybeZ = nextMaybe } else { maybeZ = getIntersect(maybeZ, nextMaybe) } } } } if len(maybeX) == len(maybeY) && len(maybeY) == len(maybeZ) && len(maybeZ) == 1 { rockVel := hailstone{ X: float64(maybeX[0]), Y: float64(maybeY[0]), Z: float64(maybeZ[0]), } hailA, hailB := all[0], all[1] mA := (hailA.DY - rockVel.Y) / (hailA.DX - rockVel.X) mB := (hailB.DY - rockVel.Y) / (hailB.DX - rockVel.X) cA := hailA.Y - (mA * hailA.X) cB := hailB.Y - (mB * hailB.X) xPos := (cB - cA) / (mA - mB) yPos := mA*xPos + cA time := (xPos - hailA.X) / (hailA.DX - rockVel.X) zPos := hailA.Z + (hailA.DZ-rockVel.Z)*time return int(xPos + yPos + zPos) } return h.MIN_INT } func getIntersect(a, b []int) []int { result := []int{} for _, val := range a { if slices.Contains(b, val) { result = append(result, val) } } return result } func findMatchingVelocity(diff, vel int) []int { match := []int{} for v := -1000; v < 1000; v++ { if v != vel && diff%(v-vel) == 0 { match = append(match, v) } } return match } func slope(h *hailstone) float64 { return h.DY / h.DX } func findIntersection(i, j *hailstone) (float64, float64) { if slope(i) == slope(j) { return -1, -1 } cx := (j.Y - (j.DY/j.DX)*j.X - (i.Y - (i.DY/i.DX)*i.X)) / (i.DY/i.DX - j.DY/j.DX) perp := !((cx > i.X) == (i.DX > 0) && (cx > j.X) == (j.DX > 0)) if perp { return -1, -1 } y := ((-i.DY/j.DY)*(j.X*j.DY-j.Y*j.DX) + i.X*i.DY - i.Y*i.DX) / ((j.DX * i.DY / j.DY) - i.DX) x := y*j.DX/j.DY + (j.X*j.DY-j.Y*j.DX)/j.DY return x, y } type hailstone struct { X, Y, Z float64 DX, DY, DZ float64 } func NewHailstone(inp string) *hailstone { h := hailstone{} fmt.Sscanf(inp, "%f, %f, %f @ %f, %f, %f", &h.X, &h.Y, &h.Z, &h.DX, &h.DY, &h.DZ) return &h } func (h hailstone) String() string { return fmt.Sprintf("%f, %f, %f @ %f, %f, %f", h.X, h.Y, h.Z, h.DX, h.DY, h.DZ) }