advent2020/twentythree.py

158 lines
4.8 KiB
Python
Executable File

#!/usr/bin/env python3
from functools import reduce
from itertools import cycle
import re, sys
import math
import numpy as np
def dprint(*args):
sep = False
for arg in args:
if sep:
print('--------')
sep = True
for a, b in arg.items():
print('%10s: %s' % (a, ','.join(b)))
print()
def cup(base, rest):
# find index of next cup to pick up
trial = chr(ord(base) - 1) if base > '1' else '9'
try:
return rest.index(trial)
except: # not there
return cup(trial, rest)
def getoff(circle, offset):
# undo our offset
return (circle + circle)[9 - offset:18 - offset]
def printoff(circle, offset):
print('%s (%d)' % (getoff(circle, offset), offset))
def printafterone(circle):
# lol we don't even need the offset
oneidx = circle.index('1')
print(circle[oneidx+1:] + circle[:oneidx])
def printnice(first, three, six, dest, i):
# ugh
print('-- move %d --' % i)
print('cups: (%s)%s' % (first, ''.join(' %s ' % c for c in three + six)))
print('pick up: %s' % ', '.join(three))
print('destination: %s' % dest)
print()
def move(circle, offset, i=1):
# don't keep track of the selected; just keep it at the left
# instead keep track of how far we've shifted the circle left
# (modulo 9, as if you care)
# ANYWAY, our selected cup is always in first position, so grab the three next
selected, pickup, remain = circle[0], circle[1:4], circle[4:]
dest = cup(selected, remain)
printnice(selected, pickup, remain, remain[dest], i) # jfc
# build the new circle
return (''.join(
[*remain[:dest],
remain[dest],
*pickup,
*remain[dest+1:],
selected,
]),
(offset + 1 + dest) % 9)
def subone(sel, three, size):
ret = sel - 1
if ret == 0:
ret = size
if ret in three:
return subone(ret, three, size)
else:
return ret
def messwith(l, r, sel, size=1000000):
#print('selected:', sel)
three = r[sel], r[r[sel]], r[r[r[sel]]]
#print('pick up:', three)
# DANGLING: r[sel], l[r[sel]] (change in opposite order of course),
# ... r[three[-1]], l[ibid],
# ALSO MODIFY: r[dest], l[r[dest]] (again change in opposite order)
dest = subone(sel, three, size)
#print('destination:', dest)
# i do not like python
#print('r[%s] = %s' % (sel, r[three[-1]]))
#print('l[%s] = %s' % (r[sel], dest))
#print('r[%s] = %s' % (three[-1], r[dest]))
#print('l[%s] = %s' % (r[three[-1]], sel))
#print('r[%s] = %s' % (dest, three[0]))
#print('l[%s] = %s' % (r[dest], three[-1]))
#print('set this to 1, please:', l[r[dest]], 'btw', r[dest])
src_r_idx = sel
src_l_idx = r[three[-1]]
dst_r_idx = dest
dst_l_idx = r[dest]
three_r_idx = three[-1]
three_l_idx = three[0] # which indeed is r[sel], yes
r[src_r_idx] = src_l_idx
l[src_l_idx] = src_r_idx
r[dst_r_idx] = three_l_idx
l[dst_l_idx] = three_r_idx
r[three_r_idx] = dst_l_idx
l[three_l_idx] = dst_r_idx
#print()
#print('left:', l)
return r[sel]
def rightof(r, i, rem):
if rem == 0:
return []
return [r[i], ] + rightof(r, r[i], rem - 1)
def printall(r, sel, size):
print('cups:', ''.join('%s%s%s' % (
'(' if x == sel else ' ',
str(x),
')' if x == sel else ' ')
for x in rightof(r, 1, size)))
fn = sys.argv[1] if len(sys.argv) > 1 else 'input%s' % 23
with open(fn) as f:
offset, circle = 0, f.read().strip()
finito = reduce(lambda a, b: move(a[0], a[1], i=b+1), range(100), (circle, offset)) # lol
printoff(*finito)
printafterone(finito[0])
# well, we'll have to be more creative here
size = int(sys.argv[2] if len(sys.argv) > 2 else 1e6)
# first, redo part 1
orbit = np.linspace(1, size, size, dtype=int)
for i, c in enumerate(circle):
orbit[i] = int(c)
print(orbit)
left = np.zeros(size + 1, dtype=int) # 1-indexed, so "pad"
right = np.zeros(size + 1, dtype=int)
for i, c in enumerate(orbit):
left[c] = orbit[(i+size-1)%size]
right[c] = orbit[(i+1)%size]
print(left[1:11], '<>', right[1:11])
print()
selidx = orbit[0]
rounds = int(sys.argv[3] if len(sys.argv) > 2 else 1e7)
for i in range(rounds):
#print('-- round %d --' % (i + 1))
#printall(right, selidx, size)
selidx = messwith(left, right, selidx, size=size)
if not (i % math.ceil(rounds / 10)):
print(left[1:], '<>', right[1:])
print('%d to go' % (rounds - i))
print('-- final --')
print(left[1:], '<>', right[1:])
#printall(right, selidx, size)
# ok, now find cups on SIDE.
bill = right[1]
bob = right[bill]
print('%d * %d =\n%d' % (bill, bob, (bill * bob)))