#!/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)))