Files
2019/days/12.go
Parnic b29ea5d936 Day 12 solution
Okay, I had to seek help on this one. The orbital period + least-common-multiple solution was not coming to me.
2022-06-10 21:59:06 -05:00

157 lines
3.2 KiB
Go

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)
}