package main
import (
"bufio"
"fmt"
"log"
"os"
"strings"
)
func abs(a int) int {
if a < 0 {
a = -a
}
return a
}
type Moon struct {
x, y, z int
vx, vy, vz int
}
func (m *Moon) CalcVelocity(self int, moons []Moon) {
for i := range moons {
if i == self {
continue
}
if m.x > moons[i].x {
m.vx--
} else if m.x < moons[i].x {
m.vx++
}
if m.y > moons[i].y {
m.vy--
} else if m.y < moons[i].y {
m.vy++
}
if m.z > moons[i].z {
m.vz--
} else if m.z < moons[i].z {
m.vz++
}
}
}
func (m *Moon) Step() {
m.x += m.vx
m.y += m.vy
m.z += m.vz
}
func (m *Moon) Energy() int {
pot := abs(m.x) + abs(m.y) + abs(m.z)
kin := abs(m.vx) + abs(m.vy) + abs(m.vz)
return pot * kin
}
func IterateMoons(moons []Moon, steps int, debug bool) int {
for i := 0; i < steps; i++ {
if debug {
fmt.Printf("Step %d\n", i+1)
}
for j := range moons {
moons[j].CalcVelocity(j, moons)
}
for j := range moons {
moons[j].Step()
if debug {
fmt.Printf("%+v\n", moons[j])
}
}
if debug {
fmt.Println()
}
}
var energy int
for i := range moons {
energy += moons[i].Energy()
}
return energy
}
func gcd(a, b int) int {
for b != 0 {
a, b = b, a%b
/*t := b
b = a % b
a = t*/
}
return a
}
func lcm(a, b, c int) int {
result := a * b / gcd(a, b)
result = result * c / gcd(result, c)
return result
}
func FindSteps(moons []Moon, debug bool) int {
initial := make([]Moon, len(moons))
var xFound, yFound, zFound bool
xSteps, ySteps, zSteps := 1, 1, 1
copy(initial, moons)
for {
for i := range moons {
moons[i].CalcVelocity(i, moons)
}
xLooped := true
yLooped := true
zLooped := true
for i := range moons {
moons[i].Step()
if moons[i].x != initial[i].x || moons[i].vx != initial[i].vx {
xLooped = false
}
if moons[i].y != initial[i].y || moons[i].vy != initial[i].vy {
yLooped = false
}
if moons[i].z != initial[i].z || moons[i].vz != initial[i].vz {
zLooped = false
}
}
if xLooped {
xFound = true
}
if yLooped {
yFound = true
}
if zLooped {
zFound = true
}
if !xFound {
xSteps++
}
if !yFound {
ySteps++
}
if !zFound {
zSteps++
}
if xFound && yFound && zFound {
break
}
}
return lcm(xSteps, ySteps, zSteps)
}
func main() {
input, err := os.Open("input.txt")
if err != nil {
log.Fatal(fmt.Errorf("could not open input file: %w", err))
}
defer input.Close()
scanner := bufio.NewScanner(input)
scanner.Split(bufio.ScanLines)
moons := make([]Moon, 0)
for scanner.Scan() {
var moon Moon
line := strings.TrimSpace(scanner.Text())
fmt.Sscanf(line, "", &moon.x, &moon.y, &moon.z)
moons = append(moons, moon)
}
fmt.Printf("Part 1: %d\n", IterateMoons(moons, 1000, false))
fmt.Printf("Part 2: %d\n", FindSteps(moons, false))
}