2023-12-27 17:47:38 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
h "git.bullercodeworks.com/brian/adventofcode/helpers"
|
2023-12-28 16:46:00 +00:00
|
|
|
"golang.org/x/exp/slices"
|
2023-12-27 17:47:38 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
func main() {
|
|
|
|
inp := h.StdinToStringSlice()
|
|
|
|
part1(inp)
|
|
|
|
fmt.Println()
|
|
|
|
part2(inp)
|
|
|
|
}
|
|
|
|
|
|
|
|
func part1(inp []string) {
|
2023-12-28 15:15:31 +00:00
|
|
|
//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++
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-27 17:47:38 +00:00
|
|
|
fmt.Println("# Part 1")
|
2023-12-28 15:15:31 +00:00
|
|
|
fmt.Println(count)
|
2023-12-27 17:47:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func part2(inp []string) {
|
2023-12-28 15:15:31 +00:00
|
|
|
var all []*hailstone
|
|
|
|
for _, i := range inp {
|
|
|
|
all = append(all, NewHailstone(i))
|
|
|
|
}
|
2023-12-27 17:47:38 +00:00
|
|
|
fmt.Println("# Part 2")
|
2023-12-28 15:15:31 +00:00
|
|
|
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))
|
2023-12-28 16:46:00 +00:00
|
|
|
if len(maybeX) == 0 {
|
|
|
|
maybeX = nextMaybe
|
|
|
|
} else {
|
|
|
|
maybeX = getIntersect(maybeX, nextMaybe)
|
|
|
|
}
|
2023-12-28 15:15:31 +00:00
|
|
|
}
|
2023-12-28 16:46:00 +00:00
|
|
|
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]),
|
2023-12-28 15:15:31 +00:00
|
|
|
}
|
2023-12-28 16:46:00 +00:00
|
|
|
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)
|
2023-12-28 15:15:31 +00:00
|
|
|
}
|
2023-12-28 16:46:00 +00:00
|
|
|
return h.MIN_INT
|
2023-12-28 15:15:31 +00:00
|
|
|
}
|
2023-12-28 16:46:00 +00:00
|
|
|
|
|
|
|
func getIntersect(a, b []int) []int {
|
|
|
|
result := []int{}
|
|
|
|
for _, val := range a {
|
|
|
|
if slices.Contains(b, val) {
|
|
|
|
result = append(result, val)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2023-12-28 15:15:31 +00:00
|
|
|
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)
|
2023-12-27 17:47:38 +00:00
|
|
|
}
|