Day 12 solution
Okay, I had to seek some advice on this one. The orbital period + least-common-multiple solution was not coming to me naturally.
This commit is contained in:
156
days/12.go
Normal file
156
days/12.go
Normal file
@ -0,0 +1,156 @@
|
|||||||
|
package days
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
u "parnic.com/aoc2019/utilities"
|
||||||
|
)
|
||||||
|
|
||||||
|
type moonData struct {
|
||||||
|
pos u.Vec3[int]
|
||||||
|
vel u.Vec3[int]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *moonData) applyGravity(other *moonData) {
|
||||||
|
applyGravityAxis := func(pos1, pos2, vel1, vel2 *int) {
|
||||||
|
if *pos1 < *pos2 {
|
||||||
|
*vel1++
|
||||||
|
*vel2--
|
||||||
|
} else if *pos1 > *pos2 {
|
||||||
|
*vel1--
|
||||||
|
*vel2++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
applyGravityAxis(&m.pos.X, &other.pos.X, &m.vel.X, &other.vel.X)
|
||||||
|
applyGravityAxis(&m.pos.Y, &other.pos.Y, &m.vel.Y, &other.vel.Y)
|
||||||
|
applyGravityAxis(&m.pos.Z, &other.pos.Z, &m.vel.Z, &other.vel.Z)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *moonData) applyVelocity() {
|
||||||
|
m.pos.Add(m.vel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m moonData) getPotentialEnergy() int {
|
||||||
|
return int(math.Abs(float64(m.pos.X))) +
|
||||||
|
int(math.Abs(float64(m.pos.Y))) +
|
||||||
|
int(math.Abs(float64(m.pos.Z)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m moonData) getKineticEnergy() int {
|
||||||
|
return int(math.Abs(float64(m.vel.X))) +
|
||||||
|
int(math.Abs(float64(m.vel.Y))) +
|
||||||
|
int(math.Abs(float64(m.vel.Z)))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m moonData) getTotalEnergy() int {
|
||||||
|
return m.getPotentialEnergy() * m.getKineticEnergy()
|
||||||
|
}
|
||||||
|
|
||||||
|
type Day12 struct {
|
||||||
|
moons []*moonData
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Day12) Parse() {
|
||||||
|
lines := u.GetStringLines("12p")
|
||||||
|
d.moons = make([]*moonData, len(lines))
|
||||||
|
for i, line := range lines {
|
||||||
|
trimmed := line[1 : len(line)-1]
|
||||||
|
vals := strings.Split(trimmed, ", ")
|
||||||
|
x, _ := strconv.Atoi(vals[0][2:])
|
||||||
|
y, _ := strconv.Atoi(vals[1][2:])
|
||||||
|
z, _ := strconv.Atoi(vals[2][2:])
|
||||||
|
d.moons[i] = &moonData{
|
||||||
|
pos: u.Vec3[int]{X: x, Y: y, Z: z},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Day12) Num() int {
|
||||||
|
return 12
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Day12) copyMoons() []*moonData {
|
||||||
|
moons := make([]*moonData, len(d.moons))
|
||||||
|
for i, moon := range d.moons {
|
||||||
|
moonCopy := *moon
|
||||||
|
moons[i] = &moonCopy
|
||||||
|
}
|
||||||
|
|
||||||
|
return moons
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllEnergy(moons ...*moonData) int {
|
||||||
|
energy := 0
|
||||||
|
for _, moon := range moons {
|
||||||
|
energy += moon.getTotalEnergy()
|
||||||
|
}
|
||||||
|
return energy
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Day12) Part1() string {
|
||||||
|
moons := d.copyMoons()
|
||||||
|
|
||||||
|
numSteps := 1000
|
||||||
|
|
||||||
|
for i := 0; i < numSteps; i++ {
|
||||||
|
for i, moon1 := range moons {
|
||||||
|
for _, moon2 := range moons[i+1:] {
|
||||||
|
moon1.applyGravity(moon2)
|
||||||
|
}
|
||||||
|
|
||||||
|
moon1.applyVelocity()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("Total energy after %d steps: %s%d%s", numSteps, u.TextBold, getAllEnergy(moons...), u.TextReset)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Day12) Part2() string {
|
||||||
|
moons := d.copyMoons()
|
||||||
|
|
||||||
|
orig := make([]u.Vec3[int], len(moons))
|
||||||
|
for i, moon := range moons {
|
||||||
|
orig[i] = moon.pos
|
||||||
|
}
|
||||||
|
period := u.Vec3[int]{}
|
||||||
|
|
||||||
|
for loops := 0; period.X == 0 || period.Y == 0 || period.Z == 0; loops++ {
|
||||||
|
for i, moon1 := range moons {
|
||||||
|
for _, moon2 := range moons[i+1:] {
|
||||||
|
moon1.applyGravity(moon2)
|
||||||
|
}
|
||||||
|
moon1.applyVelocity()
|
||||||
|
}
|
||||||
|
|
||||||
|
foundX := true
|
||||||
|
foundY := true
|
||||||
|
foundZ := true
|
||||||
|
for i, moon := range moons {
|
||||||
|
if moon.pos.X != orig[i].X || moon.vel.X != 0 {
|
||||||
|
foundX = false
|
||||||
|
}
|
||||||
|
if moon.pos.Y != orig[i].Y || moon.vel.Y != 0 {
|
||||||
|
foundY = false
|
||||||
|
}
|
||||||
|
if moon.pos.Z != orig[i].Z || moon.vel.Z != 0 {
|
||||||
|
foundZ = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if foundX && period.X == 0 {
|
||||||
|
period.X = loops + 1
|
||||||
|
}
|
||||||
|
if foundY && period.Y == 0 {
|
||||||
|
period.Y = loops + 1
|
||||||
|
}
|
||||||
|
if foundZ && period.Z == 0 {
|
||||||
|
period.Z = loops + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stepsRequired := u.LCM(period.X, period.Y, period.Z)
|
||||||
|
return fmt.Sprintf("Iterations to reach a previous state: %s%d%s", u.TextBold, stepsRequired, u.TextReset)
|
||||||
|
}
|
4
inputs/12p.txt
Normal file
4
inputs/12p.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<x=14, y=4, z=5>
|
||||||
|
<x=12, y=10, z=8>
|
||||||
|
<x=1, y=7, z=-10>
|
||||||
|
<x=16, y=-5, z=3>
|
4
inputs/12s1.txt
Normal file
4
inputs/12s1.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<x=-1, y=0, z=2>
|
||||||
|
<x=2, y=-10, z=-7>
|
||||||
|
<x=4, y=-8, z=8>
|
||||||
|
<x=3, y=5, z=-1>
|
4
inputs/12s2.txt
Normal file
4
inputs/12s2.txt
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
<x=-8, y=-10, z=0>
|
||||||
|
<x=5, y=5, z=10>
|
||||||
|
<x=2, y=-7, z=3>
|
||||||
|
<x=9, y=-8, z=-3>
|
1
main.go
1
main.go
@ -42,6 +42,7 @@ var dayMap = []day{
|
|||||||
&days.Day09{},
|
&days.Day09{},
|
||||||
&days.Day10{},
|
&days.Day10{},
|
||||||
&days.Day11{},
|
&days.Day11{},
|
||||||
|
&days.Day12{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
27
utilities/math.go
Normal file
27
utilities/math.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package utilities
|
||||||
|
|
||||||
|
func GCD[T Integer](a, b T) T {
|
||||||
|
if b == 0 {
|
||||||
|
return a
|
||||||
|
}
|
||||||
|
return GCD(b, a%b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LCM[T Integer](nums ...T) uint64 {
|
||||||
|
num := len(nums)
|
||||||
|
if num == 0 {
|
||||||
|
return 0
|
||||||
|
} else if num == 1 {
|
||||||
|
return uint64(nums[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
ret := lcm(nums[0], nums[1])
|
||||||
|
for i := 2; i < len(nums); i++ {
|
||||||
|
ret = lcm(uint64(nums[i]), ret)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func lcm[T Integer](a, b T) uint64 {
|
||||||
|
return uint64(a*b) / uint64(GCD(a, b))
|
||||||
|
}
|
@ -7,6 +7,12 @@ type Vec2[T Number] struct {
|
|||||||
Y T
|
Y T
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Vec3[T Number] struct {
|
||||||
|
X T
|
||||||
|
Y T
|
||||||
|
Z T
|
||||||
|
}
|
||||||
|
|
||||||
func (v Vec2[T]) Dot(other Vec2[T]) T {
|
func (v Vec2[T]) Dot(other Vec2[T]) T {
|
||||||
return (v.X * other.X) + (v.Y * other.Y)
|
return (v.X * other.X) + (v.Y * other.Y)
|
||||||
}
|
}
|
||||||
@ -37,3 +43,27 @@ func VecBetween[T Number](a, b Vec2[T]) Vec2[T] {
|
|||||||
Y: a.Y - b.Y,
|
Y: a.Y - b.Y,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v Vec3[T]) Dot(other Vec3[T]) T {
|
||||||
|
return (v.X * other.X) + (v.Y * other.Y) + (v.Z * other.Z)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec3[T]) Len() T {
|
||||||
|
return T(math.Sqrt(float64(v.LenSquared())))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec3[T]) LenSquared() T {
|
||||||
|
return (v.X * v.X) + (v.Y * v.Y) + (v.Z * v.Z)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *Vec3[T]) Add(other Vec3[T]) {
|
||||||
|
v.X += other.X
|
||||||
|
v.Y += other.Y
|
||||||
|
v.Z += other.Z
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vec3[T]) Equals(other Vec3[T]) bool {
|
||||||
|
return v.X == other.X &&
|
||||||
|
v.Y == other.Y &&
|
||||||
|
v.Z == other.Z
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user