test="""<x=-1, y=0, z=2>
<x=2, y=-10, z=-7>
<x=4, y=-8, z=8>
<x=3, y=5, z=-1>"""
test_posns = [[-1, 0, 2], [2, -10, -7], [4, -8, 8], [3, 5, -1]]
actual="""<x=5, y=13, z=-3>
<x=18, y=-7, z=13>
<x=16, y=3, z=4>
<x=0, y=8, z=8>
"""
posns = [[5, 13, -3], [18, -7, 13], [16, 3, 4], [0, 8, 8]]
test2="""<x=-8, y=-10, z=0>
<x=5, y=5, z=10>
<x=2, y=-7, z=3>
<x=9, y=-8, z=-3>"""
test2_posns= [[-8,-10,0], [5,5,10], [2,-7,3], [9,-8,-3]]
def eval_moons(arg_moons, steps=10):
moons = []
moons_v = []
for i in range(4):
moons.append(arg_moons[i][:])
moons_v.append([0] * 3)
for step in range(steps + 1):
# print(f"After {step} steps")
# for i, moon in enumerate(moons):
# moon_v = moons_v[i]
# print(f"pos=<x={moon[0]}, y={moon[1]}, z={moon[2]}, vel=<x={moon_v[0]}, y={moon_v[1]}, z={moon_v[2]}")
for i, moon in enumerate(moons):
for j, moon in enumerate(moons):
if i >= j:
continue
for k in range(3):
if moons[i][k] == moons[j][k]:
continue
moons_v[i][k] += 1 if moons[i][k] < moons[j][k] else -1
moons_v[j][k] += 1 if moons[j][k] < moons[i][k] else -1
for i, moon in enumerate(moons):
for k in range(3):
moons[i][k] += moons_v[i][k]
# print()
return moons, moons_v
newmoons = eval_moons(posns, 999)
def energy(moons, moons_v):
total = 0
for i in range(4):
total += sum(map(abs, moons[i])) * sum(map(abs, moons_v[i]))
return total
energy(*newmoons)
from copy import deepcopy
def sim(arg_moons):
moons = []
moons_v = []
for i in range(4):
moons.append(arg_moons[i][:])
moons_v.append([0] * 3)
while True:
for i, moon in enumerate(moons):
for j, moon in enumerate(moons):
if i >= j:
continue
for k in range(3):
if moons[i][k] == moons[j][k]:
continue
moons_v[i][k] += 1 if moons[i][k] < moons[j][k] else -1
moons_v[j][k] += 1 if moons[j][k] < moons[i][k] else -1
for i, moon in enumerate(moons):
for k in range(3):
moons[i][k] += moons_v[i][k]
yield deepcopy(moons), None
def find_cycle(v, start=1):
l = len(v)
for i in range(start, l // 2):
if all(v[i + j] == v[j] for j in range(i)):
return i
return None
from math import gcd
def lcm(l):
lcm = l[0]
for a in l[1:]:
lcm = lcm * a // gcd(lcm, a)
return lcm
%%time
testsim = sim(posns)
coords = []
cycles = [None] * 12
for _ in range(4):
coords.append([])
for _ in range(3):
coords[-1].append([])
steps = 0
while not all(cycles):
steps += 1
if steps % 50000 == 0:
print(steps, cycles)
n = next(testsim)
for i in range(4):
for j in range(3):
coords[i][j].append(n[0][i][j])
index = i * 3 + j
if not cycles[index]:
cycles[index] = find_cycle(coords[i][j], start=max(1, (steps - 1) // 2))
print(cycles)
print(lcm(cycles))
TODO: Come back and have fun optimizing this a little; removing one of the deep-copies cut runtime by 10%, there's a lot i could speed up here.