├── 2019 ├── day1.py ├── day10.py ├── day11.py ├── day12.py ├── day13.py ├── day14.py ├── day15.py ├── day16.py ├── day17.py ├── day18.py ├── day19.py ├── day2.py ├── day20.py ├── day20fast.py ├── day21.py ├── day22.py ├── day23.py ├── day23b.py ├── day24.py ├── day25.py ├── day25fast.py ├── day3.py ├── day4.py ├── day5.py ├── day6.py ├── day7.py ├── day8.py └── day9.py ├── 2020 ├── day01.py ├── day02.py ├── day03.py ├── day04.py ├── day05.py ├── day06.py ├── day07.py ├── day08.py ├── day09.py ├── day10.py ├── day11.py ├── day12.py ├── day13.py ├── day14.py ├── day15.py ├── day16.py ├── day17.py ├── day18.py ├── day19.py ├── day20.py ├── day21.py ├── day22.py ├── day23.py ├── day24.py └── day25.py └── README.md /2019/day1.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import re 3 | import sys 4 | 5 | import sortedcollections 6 | 7 | with open(sys.argv[1]) as f: 8 | lines = [l.rstrip('\n') for l in f] 9 | lines = [[int(i) for i in re.findall(r'-?\d+', l)] for l in lines] 10 | print(lines) 11 | 12 | fuel = 0 13 | for line in lines: 14 | f = line[0] 15 | while True: 16 | f = f // 3 - 2 17 | if f > 0: 18 | fuel += f 19 | else: 20 | break 21 | print(fuel) -------------------------------------------------------------------------------- /2019/day10.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import itertools 3 | import cmath 4 | import math 5 | import re 6 | import sys 7 | 8 | import sortedcollections 9 | 10 | with open(sys.argv[1]) as f: 11 | lines = [l.rstrip('\n') for l in f] 12 | 13 | # part 1 14 | maxcnt = 0 15 | best = None 16 | for i in range(len(lines)): 17 | for j in range(len(lines[0])): 18 | if lines[i][j] == '.': 19 | continue 20 | cnt = 0 21 | s = set() 22 | for x in range(len(lines)): 23 | for y in range(len(lines[0])): 24 | if lines[x][y] == '.': 25 | continue 26 | gc = int(abs(math.gcd(x - i, y - j))) 27 | no = False 28 | #print(x,y, gc) 29 | for q in range(1, gc): 30 | if lines[i + (x - i) // gc * q][j + (y - j) // gc * q] == '#': 31 | no = True 32 | break 33 | if not no: 34 | s.add((x, y)) 35 | cnt += 1 36 | #print(i, j, cnt, sorted(s)) 37 | maxcnt = max(cnt, maxcnt) 38 | if cnt == maxcnt: 39 | best = i, j 40 | print(maxcnt, best) 41 | 42 | # part 2 43 | allast = set() 44 | for i in range(len(lines)): 45 | for j in range(len(lines[0])): 46 | if lines[i][j] == '#': 47 | allast.add((i, j)) 48 | phase = lambda xy: cmath.phase((xy[0] - best[0]) + (xy[1] - best[1]) * 1j) 49 | so = sorted(allast, key=phase,reverse=True) 50 | for i, (direc, asts) in enumerate(itertools.groupby(so, key=phase)): 51 | if i + 1 == 200: 52 | print(i + 1, direc, list(asts)) 53 | 54 | # part 2 appendix: I didn't write this during the timed contest but if I 55 | # had to loop around to get the answer (i.e. if there were fewer than 200 56 | # different directions) it might've looked something like this: 57 | grouped = [] 58 | for direc, asts in itertools.groupby(so, key=phase): 59 | grouped.append(list(asts)) 60 | i = 0 61 | def pop(): 62 | global i 63 | oi = i 64 | while not grouped[i]: 65 | i = (i + 1) % len(grouped) 66 | if i == oi: 67 | return None 68 | closest = min(grouped[i], key=lambda xy: math.hypot(xy[0] - best[0], xy[1] - best[1])) 69 | grouped[i][:] = [p for p in grouped[i] if p != closest] 70 | i = (i + 1) % len(grouped) 71 | return closest 72 | for _ in range(200 - 1): 73 | pop() 74 | print(pop()) 75 | -------------------------------------------------------------------------------- /2019/day11.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | def tryprog(prog, name=''): 9 | pc = 0 10 | outputs = [] 11 | 12 | progd = collections.defaultdict(int) 13 | for i, v in enumerate(prog): 14 | progd[i] = v 15 | prog = progd 16 | 17 | relbase = 0 18 | 19 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 20 | def parse(): 21 | nonlocal pc 22 | op = prog[pc] 23 | pc += 1 24 | vals = [] 25 | locs = [] 26 | for i in range(arity[op % 100]): 27 | mode = (op // (10 ** (2 + i))) % 10 28 | vals.append(prog[pc] if mode == 1 else prog[prog[pc] + relbase] if mode == 2 else prog[prog[pc]]) 29 | locs.append(None if mode == 1 else prog[pc] + relbase if mode == 2 else prog[pc]) 30 | pc += 1 31 | return op % 100, vals, locs 32 | 33 | while prog[pc] != 99: 34 | opc = pc 35 | op, vals, locs = parse() 36 | #print(name,opc,op) 37 | if op == 1: 38 | prog[locs[2]] = vals[0] + vals[1] 39 | elif op == 2: 40 | prog[locs[2]] = vals[0] * vals[1] 41 | elif op == 3: 42 | #print(pc, prog, inputs) 43 | prog[locs[0]] = yield 'needinput' 44 | #print(name, 'got', prog[locs[0]]) 45 | assert prog[locs[0]] is not None 46 | elif op == 4: 47 | #print('out:', vals[0]) 48 | #outputs.append(vals[0]) 49 | #print(name, 'returning', prog[locs[0]]) 50 | yield vals[0] 51 | elif op == 5: 52 | if vals[0] != 0: 53 | pc = vals[1] 54 | elif op == 6: 55 | if vals[0] == 0: 56 | pc = vals[1] 57 | elif op == 7: 58 | prog[locs[2]] = int(vals[0] < vals[1]) 59 | elif op == 8: 60 | prog[locs[2]] = int(vals[0] == vals[1]) 61 | elif op == 9: 62 | relbase += vals[0] 63 | else: 64 | assert False 65 | 66 | #print(name,'done') 67 | return prog[0], outputs[0] if outputs else None 68 | 69 | with open(sys.argv[1]) as f: 70 | lines = [l.rstrip('\n') for l in f] 71 | lines = [[int(i) for i in l.split(',')] for l in lines] 72 | 73 | p = tryprog(lines[0][:]) 74 | px, py = 0, 0 75 | dx, dy = 0, 1 76 | white_panels = set() 77 | white_panels.add((0, 0)) # part 2 only 78 | painted = set() 79 | nin = None 80 | while True: 81 | try: 82 | o = p.send(nin) 83 | except StopIteration: 84 | break 85 | if o == 'needinput': 86 | nin = 1 if (px, py) in white_panels else 0 87 | continue 88 | 89 | if o == 0: 90 | if (px, py) in white_panels: 91 | white_panels.remove((px, py)) 92 | painted.add((px, py)) 93 | elif o == 1: 94 | white_panels.add((px, py)) 95 | painted.add((px, py)) 96 | else: 97 | assert False 98 | 99 | o2 = next(p) 100 | if o2 == 0: 101 | dx, dy = -dy, dx 102 | elif o2 == 1: 103 | dx, dy = dy, -dx 104 | else: 105 | assert False 106 | px += dx 107 | py += dy 108 | 109 | #print(len(painted)) 110 | 111 | graphic = [[' '] * 40 for i in range(10)] 112 | 113 | for x, y in white_panels: 114 | graphic[2 - y][x] = '#' 115 | 116 | for ll in graphic: 117 | print(''.join(c*2 for c in ll)) 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /2019/day12.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | def step(moons): 9 | outmoons = [m[:] for m in moons] 10 | for i, moon in enumerate(moons): 11 | for j, moon2 in enumerate(moons): 12 | if i >= j: 13 | continue 14 | for q in (0, 1, 2): 15 | if moon[q] < moon2[q]: 16 | outmoons[i][q+3] += 1 17 | outmoons[j][q+3] -= 1 18 | elif moon[q] > moon2[q]: 19 | outmoons[i][q+3] -= 1 20 | outmoons[j][q+3] += 1 21 | for moon in outmoons: 22 | moon[0] += moon[3] 23 | moon[1] += moon[4] 24 | moon[2] += moon[5] 25 | return outmoons 26 | 27 | 28 | with open(sys.argv[1]) as f: 29 | lines = [l.rstrip('\n') for l in f] 30 | lines = [[int(i) for i in re.findall(r'-?\d+', l)] for l in lines] 31 | 32 | # part 1 33 | moons = [line + [0,0,0] for line in lines] 34 | for i in range(1000): 35 | moons = step(moons) 36 | kes = [(abs(a)+abs(b)+abs(c)) * (abs(x) + abs(y) + abs(z)) for a,b,c,x,y,z in moons] 37 | print(sum(kes)) 38 | 39 | # part 2 40 | moons = [line + [0,0,0] for line in lines] 41 | seenx = set() 42 | repx = None 43 | seeny = set() 44 | repy = None 45 | seenz = set() 46 | repz = None 47 | for i in range(1000000): 48 | if repx and repy and repz: 49 | break 50 | moons = step(moons) 51 | if not repx: 52 | xk = str([[m[0], m[3]] for m in moons]) 53 | if xk in seenx: 54 | repx = i 55 | seenx.add(xk) 56 | if not repy: 57 | yk = str([[m[1], m[4]] for m in moons]) 58 | if yk in seeny: 59 | repy = i 60 | seeny.add(yk) 61 | if not repz: 62 | zk = str([[m[2], m[5]] for m in moons]) 63 | if zk in seenz: 64 | repz = i 65 | seenz.add(zk) 66 | print(repx, repy, repz) 67 | 68 | # part 2 appendix: didn't write this during the contest (I just copied my 69 | # three into Wolfram Alpha after typing "lcm of") but it would've been: 70 | def lcm(x, y): 71 | return x // math.gcd(x, y) * y 72 | print(lcm(lcm(repx, repy), repz)) 73 | -------------------------------------------------------------------------------- /2019/day13.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | def tryprog(prog, name=''): 8 | pc = 0 9 | outputs = [] 10 | 11 | progd = collections.defaultdict(int) 12 | for i, v in enumerate(prog): 13 | progd[i] = v 14 | prog = progd 15 | 16 | relbase = 0 17 | 18 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 19 | def parse(): 20 | nonlocal pc 21 | op = prog[pc] 22 | pc += 1 23 | vals = [] 24 | locs = [] 25 | for i in range(arity[op % 100]): 26 | mode = (op // (10 ** (2 + i))) % 10 27 | vals.append(prog[pc] if mode == 1 else prog[prog[pc] + relbase] if mode == 2 else prog[prog[pc]]) 28 | locs.append(None if mode == 1 else prog[pc] + relbase if mode == 2 else prog[pc]) 29 | pc += 1 30 | return op % 100, vals, locs 31 | 32 | while prog[pc] != 99: 33 | opc = pc 34 | op, vals, locs = parse() 35 | #print(name,opc,op) 36 | if op == 1: 37 | prog[locs[2]] = vals[0] + vals[1] 38 | elif op == 2: 39 | prog[locs[2]] = vals[0] * vals[1] 40 | elif op == 3: 41 | #print(pc, prog, inputs) 42 | prog[locs[0]] = yield 'needinput' 43 | #print(name, 'got', prog[locs[0]]) 44 | assert prog[locs[0]] is not None 45 | elif op == 4: 46 | #print('out:', vals[0]) 47 | #outputs.append(vals[0]) 48 | #print(name, 'returning', prog[locs[0]]) 49 | yield vals[0] 50 | elif op == 5: 51 | if vals[0] != 0: 52 | pc = vals[1] 53 | elif op == 6: 54 | if vals[0] == 0: 55 | pc = vals[1] 56 | elif op == 7: 57 | prog[locs[2]] = int(vals[0] < vals[1]) 58 | elif op == 8: 59 | prog[locs[2]] = int(vals[0] == vals[1]) 60 | elif op == 9: 61 | relbase += vals[0] 62 | else: 63 | assert False 64 | 65 | #print(name,'done') 66 | return prog[0], outputs[0] if outputs else None 67 | 68 | with open(sys.argv[1]) as f: 69 | lines = [l.rstrip('\n') for l in f] 70 | lines = [[int(i) for i in l.split(',')] for l in lines] 71 | 72 | grid = {} 73 | p = tryprog(lines[0]) 74 | try: 75 | while True: 76 | a = next(p) 77 | b = next(p) 78 | c = next(p) 79 | grid[a,b] = c 80 | except StopIteration: 81 | pass 82 | print(len([v for k, v in grid.items() if v == 2])) 83 | 84 | lines[0][0] = 2 85 | p = tryprog(lines[0]) 86 | ballx = 0 87 | padx = 0 88 | sco = 0 89 | try: 90 | while True: 91 | a = next(p) 92 | while a == 'needinput': 93 | a = p.send(1 if ballx > padx else 0 if ballx == padx else -1) 94 | b = next(p) 95 | c = next(p) 96 | if (a, b) == (-1, 0): 97 | sco = c 98 | if c == 3: 99 | padx = a 100 | if c == 4: 101 | ballx = a 102 | except StopIteration: 103 | pass 104 | print(sco) 105 | 106 | 107 | -------------------------------------------------------------------------------- /2019/day14.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | with open(sys.argv[1]) as f: 9 | lines = [l.rstrip('\n') for l in f] 10 | lines = [[i for i in re.findall(r'-?\d+|[A-Z]+', l)] for l in lines] 11 | 12 | outs = {} 13 | for line in lines: 14 | co, cq = line[-2:] 15 | co = int(co) 16 | 17 | ins = [] 18 | for a, b in zip(line[:-2][::2], line[:-2][1::2]): 19 | ins.append((int(a), b)) 20 | assert cq not in outs 21 | outs[cq] = (co, ins) 22 | #print(outs) 23 | 24 | 25 | def tryfuel(fuel): 26 | need = {'FUEL': fuel} 27 | have = collections.defaultdict(int) 28 | while True: 29 | try: 30 | nk = next(n for n in need if n != 'ORE') 31 | except StopIteration: 32 | break 33 | quant, ins = outs[nk] 34 | 35 | # For part 1, I did it the lazy way (one reaction at a time): 36 | # 37 | # if need[nk] < quant: 38 | # have[nk] += quant - need[nk] 39 | # del need[nk] 40 | # elif need[nk] == quant: 41 | # del need[nk] 42 | # else: 43 | # need[nk] -= quant 44 | 45 | # For part 2, more efficient: 46 | d, m = divmod(need[nk], quant) 47 | if m == 0: 48 | del need[nk] 49 | else: 50 | del need[nk] 51 | have[nk] = quant - m 52 | d += 1 53 | 54 | for a, b in ins: 55 | need[b] = need.get(b, 0) + d * a - have[b] 56 | del have[b] 57 | return need['ORE'] 58 | 59 | # part 1 60 | # (I hadn't extracted it to a function during the contest, but otherwise 61 | # this is what I wrote) 62 | print(tryfuel(1)) 63 | 64 | # part 2 65 | # I didn't feel like writing a binary search so I literally tried different 66 | # numbers while rerunning this program and did a binary-esque search by hand: 67 | print(10 ** 12) 68 | print(tryfuel(2074843)) 69 | 70 | # part 2 appendix: probably should've done it like this in the first place 71 | a, b = 1, 2 72 | while tryfuel(b) < 10**12: 73 | a, b = b, b * 2 74 | while b - a >= 2: 75 | half = a + (b - a) // 2 76 | if tryfuel(half) > 10**12: 77 | b = half 78 | else: 79 | a = half 80 | print(a) 81 | -------------------------------------------------------------------------------- /2019/day15.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import collections 3 | import math 4 | import re 5 | import sys 6 | 7 | import sortedcollections 8 | 9 | def tryprog(prog, pc=0, name=''): 10 | outputs = [] 11 | 12 | if isinstance(prog, collections.defaultdict): 13 | prog = collections.defaultdict(int, prog) 14 | else: 15 | progd = collections.defaultdict(int) 16 | for i, v in enumerate(prog): 17 | progd[i] = v 18 | prog = progd 19 | 20 | relbase = 0 21 | 22 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 23 | def parse(): 24 | nonlocal pc 25 | op = prog[pc] 26 | pc += 1 27 | vals = [] 28 | locs = [] 29 | for i in range(arity[op % 100]): 30 | mode = (op // (10 ** (2 + i))) % 10 31 | vals.append(prog[pc] if mode == 1 else prog[prog[pc] + relbase] if mode == 2 else prog[prog[pc]]) 32 | locs.append(None if mode == 1 else prog[pc] + relbase if mode == 2 else prog[pc]) 33 | pc += 1 34 | return op % 100, vals, locs 35 | 36 | while prog[pc] != 99: 37 | opc = pc 38 | op, vals, locs = parse() 39 | #print(name,opc,op) 40 | if op == 1: 41 | prog[locs[2]] = vals[0] + vals[1] 42 | elif op == 2: 43 | prog[locs[2]] = vals[0] * vals[1] 44 | elif op == 3: 45 | #print(pc, prog, inputs) 46 | prog[locs[0]] = yield ('needinput', prog, opc) 47 | #print(name, 'got', prog[locs[0]]) 48 | assert prog[locs[0]] is not None 49 | elif op == 4: 50 | #print('out:', vals[0]) 51 | #outputs.append(vals[0]) 52 | #print(name, 'returning', prog[locs[0]]) 53 | yield vals[0] 54 | elif op == 5: 55 | if vals[0] != 0: 56 | pc = vals[1] 57 | elif op == 6: 58 | if vals[0] == 0: 59 | pc = vals[1] 60 | elif op == 7: 61 | prog[locs[2]] = int(vals[0] < vals[1]) 62 | elif op == 8: 63 | prog[locs[2]] = int(vals[0] == vals[1]) 64 | elif op == 9: 65 | relbase += vals[0] 66 | else: 67 | assert False 68 | 69 | #print(name,'done') 70 | return prog[0], outputs[0] if outputs else None 71 | 72 | with open(sys.argv[1]) as f: 73 | lines = [l.rstrip('\n') for l in f] 74 | lines = [[int(i) for i in l.split(',')] for l in lines] 75 | 76 | op = tryprog(lines[0]) 77 | _, prog, pc = next(op) 78 | seen = { 79 | (0, 0): (1, prog, pc, 0), 80 | } 81 | dirs = {1: (0, 1), 2: (0, -1), 3: (-1, 0), 4: (1, 0)} 82 | bfs = collections.deque([(0, 0)]) 83 | otank = None 84 | while bfs: 85 | x, y = bfs.popleft() 86 | _, prog, pc, omd = seen[x, y] 87 | for dic, dixy in dirs.items(): 88 | nxy = (x + dixy[0], y + dixy[1]) 89 | if nxy in seen: 90 | continue 91 | p = tryprog(prog, pc=pc) 92 | assert next(p)[0] == 'needinput' 93 | ans = p.send(dic) 94 | a, prog2, pc2 = next(p) 95 | assert a == 'needinput' 96 | seen[nxy] = (ans, prog2, pc2, omd + 1) 97 | if ans == 2: 98 | otank = nxy 99 | print(otank, omd+1) 100 | if ans != 0: 101 | bfs.append(nxy) 102 | 103 | clean = {otank: 0} 104 | bfs = collections.deque([otank]) 105 | while bfs: 106 | x, y = bfs.popleft() 107 | for dic, dixy in dirs.items(): 108 | nxy = (x + dixy[0], y + dixy[1]) 109 | if nxy in clean: 110 | continue 111 | if seen[nxy][0] == 1: 112 | clean[nxy] = clean[x, y] + 1 113 | bfs.append(nxy) 114 | 115 | print(max(clean.values())) 116 | -------------------------------------------------------------------------------- /2019/day16.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import array 3 | import math 4 | import re 5 | import sys 6 | 7 | import sortedcollections 8 | 9 | with open(sys.argv[1]) as f: 10 | lines = [l.rstrip('\n') for l in f] 11 | 12 | digits = array.array('b', [int(c) for c in list(lines[0])]* 10000) 13 | offset = int(''.join(str(c) for c in digits[:7])) 14 | 15 | q = [0, 1, 0, -1] 16 | # pds = [None] 17 | # for x in range(2, len(digits) + 10): 18 | # pd = {} 19 | 20 | # ox = x - 1 21 | 22 | # op = ox - 1 23 | # oc = 0 24 | # np = x - 1 25 | # nc = 0 26 | 27 | # while op < len(digits) or np < len(digits): 28 | # if op <= np: 29 | # oc += 1 30 | # oc %= 4 31 | # if q[nc] - q[oc]: 32 | # for i in range(op, min(np, len(digits))): 33 | # pd[i] = q[nc] - q[oc] 34 | # op += ox 35 | # else: 36 | # nc += 1 37 | # nc %= 4 38 | # if q[nc] - q[oc]: 39 | # for i in range(np, min(op, len(digits))): 40 | # pd[i] = q[nc] - q[oc] 41 | # np += x 42 | 43 | # #for y in range(0, len(digits)): 44 | # # dif = q[(y + 1) // x % 4] - (q[(y + 1) // (x-1) % 4] if x>1 else 0) 45 | # # if dif != 0: 46 | # # pd[y] = dif 47 | # pds.append(pd) 48 | # print(x, len(pd)) 49 | # #print(pds) 50 | # print([len(pd) for pd in pds if pd is not None]) 51 | # if i <= 2: 52 | # n = sum( 53 | # q[(j + 1) // i % 4] * b 54 | # for j, b in enumerate(digits) 55 | # if (j+1) // i % 2 56 | # ) 57 | # #print(',', i, digits, n) 58 | # else: 59 | # pd = pds[i-1] 60 | # n += sum( 61 | # digits[idx] * mul 62 | # for idx, mul in pd.items() 63 | # ) 64 | # #print(',', i, digits, pd, n) 65 | 66 | ld = len(digits) 67 | for x in range(100): 68 | ps = array.array('l', [0]) 69 | pss = 0 70 | for d in digits: 71 | pss += d 72 | ps.append(pss) 73 | assert len(ps) == ld+1 74 | 75 | ds = array.array('b',[]) 76 | n = None 77 | for i, d in enumerate(digits, 1): 78 | n = 0 79 | #print(digits) 80 | for startof1 in range(i-1, ld, 4*i): 81 | endof1 = startof1 + i 82 | n += ps[min(endof1, ld)] - ps[startof1] 83 | #print('+', startof1, endof1, '=', ps[min(endof1, ld)] - ps[startof1]) 84 | for startofn1 in range(3*i-1, ld, 4*i): 85 | endofn1 = startofn1 + i 86 | n -= ps[min(endofn1, ld)] - ps[startofn1] 87 | #print('-', startofn1, endofn1, '=', ps[min(endofn1, ld)] - ps[startofn1]) 88 | ds.append(abs(n) % 10) 89 | #print('=>', n, 'from', ds[-1]) 90 | 91 | digits = ds 92 | #print(ds) 93 | print(x, ''.join(str(c) for c in ds[0:0+8])) 94 | 95 | print(''.join(str(c) for c in ds[0:0+8])) 96 | print(''.join(str(c) for c in ds[offset:offset+8])) 97 | -------------------------------------------------------------------------------- /2019/day17.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | def tryprog(prog, pc=0, name=''): 8 | outputs = [] 9 | 10 | if isinstance(prog, collections.defaultdict): 11 | prog = collections.defaultdict(int, prog) 12 | else: 13 | progd = collections.defaultdict(int) 14 | for i, v in enumerate(prog): 15 | progd[i] = v 16 | prog = progd 17 | 18 | relbase = 0 19 | 20 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 21 | def parse(): 22 | nonlocal pc 23 | op = prog[pc] 24 | pc += 1 25 | vals = [] 26 | locs = [] 27 | for i in range(arity[op % 100]): 28 | mode = (op // (10 ** (2 + i))) % 10 29 | vals.append(prog[pc] if mode == 1 else prog[prog[pc] + relbase] if mode == 2 else prog[prog[pc]]) 30 | locs.append(None if mode == 1 else prog[pc] + relbase if mode == 2 else prog[pc]) 31 | pc += 1 32 | return op % 100, vals, locs 33 | 34 | while prog[pc] != 99: 35 | opc = pc 36 | op, vals, locs = parse() 37 | #print(name,opc,op) 38 | if op == 1: 39 | prog[locs[2]] = vals[0] + vals[1] 40 | elif op == 2: 41 | prog[locs[2]] = vals[0] * vals[1] 42 | elif op == 3: 43 | #print(pc, prog, inputs) 44 | prog[locs[0]] = yield ('needinput', prog, opc) 45 | #print(name, 'got', prog[locs[0]]) 46 | assert prog[locs[0]] is not None 47 | elif op == 4: 48 | #print('out:', vals[0]) 49 | #outputs.append(vals[0]) 50 | #print(name, 'returning', prog[locs[0]]) 51 | yield vals[0] 52 | elif op == 5: 53 | if vals[0] != 0: 54 | pc = vals[1] 55 | elif op == 6: 56 | if vals[0] == 0: 57 | pc = vals[1] 58 | elif op == 7: 59 | prog[locs[2]] = int(vals[0] < vals[1]) 60 | elif op == 8: 61 | prog[locs[2]] = int(vals[0] == vals[1]) 62 | elif op == 9: 63 | relbase += vals[0] 64 | else: 65 | assert False 66 | 67 | #print(name,'done') 68 | return prog[0], outputs[0] if outputs else None 69 | 70 | 71 | 72 | with open(sys.argv[1]) as f: 73 | lines = [l.rstrip('\n') for l in f] 74 | if lines[0][0] != '#': 75 | # real program 76 | lines = [[int(i) for i in l.split(',')] for l in lines] 77 | prog = lines[0] 78 | grid = ''.join(chr(c) for c in tryprog(lines[0])).strip().split('\n') 79 | x, y = 0, 2 80 | else: 81 | # demo grid 82 | grid = lines 83 | x, y = 6, 0 84 | print('\n'.join(grid)) 85 | 86 | # part 1 87 | s = 0 88 | for ax in range(1, len(grid) - 1): 89 | for ay in range(1, len(grid[0]) - 1): 90 | if ( 91 | grid[ax][ay] == '#' and 92 | grid[ax-1][ay] == '#' and 93 | grid[ax+1][ay] == '#' and 94 | grid[ax][ay-1] == '#' and 95 | grid[ax][ay+1] == '#' 96 | ): 97 | s += ax * ay 98 | print(s) 99 | 100 | # part 2 101 | turnR = { 102 | (0, 1): (1, 0), 103 | (1, 0): (0, -1), 104 | (0, -1): (-1, 0), 105 | (-1, 0): (0, 1), 106 | } 107 | turnL = { 108 | (0, 1): (-1, 0), 109 | (-1, 0): (0, -1), 110 | (0, -1): (1, 0), 111 | (1, 0): (0, 1), 112 | } 113 | direc = 0, +1 114 | path = ['R', 0] 115 | while True: 116 | nx, ny = x + direc[0], y + direc[1] 117 | if 0 <= nx <= len(grid) - 1 and 0 <= ny <= len(grid[0]) - 1 and grid[nx][ny] == '#': 118 | x, y = nx, ny 119 | path[-1] += 1 120 | else: 121 | tryR = turnR[direc] 122 | nx, ny = x + tryR[0], y + tryR[1] 123 | if 0 <= nx <= len(grid) - 1 and 0 <= ny <= len(grid[0]) - 1 and grid[nx][ny] == '#': 124 | x, y = nx, ny 125 | direc = tryR 126 | path.append('R') 127 | path.append(1) 128 | else: 129 | tryL = turnL[direc] 130 | nx, ny = x + tryL[0], y + tryL[1] 131 | if 0 <= nx <= len(grid) - 1 and 0 <= ny <= len(grid[0]) - 1 and grid[nx][ny] == '#': 132 | x, y = nx, ny 133 | direc = tryL 134 | path.append('L') 135 | path.append(1) 136 | else: 137 | break 138 | 139 | # compress this by hand, then continue: 140 | print(','.join(str(s) for s in path)) 141 | prog[0] = 2 142 | p = tryprog(prog) 143 | cs = 'A,B,B,A,C,A,C,A,C,B\nR,6,R,6,R,8,L,10,L,4\nR,6,L,10,R,8\nL,4,L,12,R,6,L,10\nn\n' 144 | while True: 145 | try: 146 | r = next(p) 147 | except StopIteration: 148 | break 149 | while isinstance(r, tuple) and r[0] == 'needinput': 150 | r = p.send(ord(cs[0])) 151 | cs = cs[1:] 152 | print(r) 153 | 154 | -------------------------------------------------------------------------------- /2019/day18.py: -------------------------------------------------------------------------------- 1 | import string 2 | import collections 3 | import math 4 | import re 5 | import sys 6 | 7 | import sortedcollections 8 | 9 | def reachablekeys(grid, start, havekeys): 10 | bfs = collections.deque([start]) 11 | distance = {start: 0} 12 | keys = {} 13 | while bfs: 14 | h = bfs.popleft() 15 | for pt in [ 16 | (h[0] + 1, h[1]), 17 | (h[0] - 1, h[1]), 18 | (h[0], h[1] + 1), 19 | (h[0], h[1] - 1), 20 | ]: 21 | if not (0 <= pt[0] < len(grid) and 0 <= pt[1] < len(grid[0])): 22 | continue 23 | ch = grid[pt[0]][pt[1]] 24 | if ch == '#': 25 | continue 26 | if pt in distance: 27 | continue 28 | distance[pt] = distance[h] + 1 29 | if 'A' <= ch <= 'Z' and ch.lower() not in havekeys: 30 | continue 31 | if 'a' <= ch <= 'z' and ch not in havekeys: 32 | keys[ch] = distance[pt], pt 33 | else: 34 | bfs.append(pt) 35 | return keys 36 | 37 | 38 | def reachable4(grid, starts, havekeys): 39 | keys = {} 40 | for i, start in enumerate(starts): 41 | for ch, (dist, pt) in reachablekeys(grid, start, havekeys).items(): 42 | keys[ch] = dist, pt, i 43 | return keys 44 | 45 | 46 | seen = {} 47 | def minwalk(grid, starts, havekeys): 48 | hks = ''.join(sorted(havekeys)) 49 | if (starts, hks) in seen: 50 | return seen[starts, hks] 51 | if len(seen) % 10 == 0: 52 | print(hks) 53 | keys = reachable4(grid, starts, havekeys) 54 | if len(keys) == 0: 55 | # done! 56 | ans = 0 57 | else: 58 | poss = [] 59 | for ch, (dist, pt, roi) in keys.items(): 60 | nstarts = tuple(pt if i == roi else p for i, p in enumerate(starts)) 61 | poss.append(dist + minwalk(grid, nstarts, havekeys + ch)) 62 | ans = min(poss) 63 | seen[starts, hks] = ans 64 | return ans 65 | 66 | 67 | with open(sys.argv[1]) as f: 68 | grid = [l.rstrip('\n') for l in f] 69 | 70 | starts = [] 71 | for i in range(len(grid)): 72 | for j in range(len(grid[0])): 73 | if grid[i][j] == '@': 74 | starts.append((i,j)) 75 | 76 | print(minwalk(grid, tuple(starts), '')) 77 | -------------------------------------------------------------------------------- /2019/day19.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | demo = ''' 9 | #....................................... 10 | .#...................................... 11 | ..##.................................... 12 | ...###.................................. 13 | ....###................................. 14 | .....####............................... 15 | ......#####............................. 16 | ......######............................ 17 | .......#######.......................... 18 | ........########........................ 19 | .........#########...................... 20 | ..........#########..................... 21 | ...........##########................... 22 | ...........############................. 23 | ............############................ 24 | .............#############.............. 25 | ..............##############............ 26 | ...............###############.......... 27 | ................###############......... 28 | ................#################....... 29 | .................########OOOOOOOOOO..... 30 | ..................#######OOOOOOOOOO#.... 31 | ...................######OOOOOOOOOO###.. 32 | ....................#####OOOOOOOOOO##### 33 | .....................####OOOOOOOOOO##### 34 | .....................####OOOOOOOOOO##### 35 | ......................###OOOOOOOOOO##### 36 | .......................##OOOOOOOOOO##### 37 | ........................#OOOOOOOOOO##### 38 | .........................OOOOOOOOOO##### 39 | ..........................############## 40 | ..........................############## 41 | ...........................############# 42 | ............................############ 43 | .............................########### 44 | '''.split() 45 | 46 | def tryprog(prog, pc=0, name=''): 47 | outputs = [] 48 | if isinstance(prog, collections.defaultdict): 49 | prog = collections.defaultdict(int, prog) 50 | else: 51 | progd = collections.defaultdict(int) 52 | for i, v in enumerate(prog): 53 | progd[i] = v 54 | prog = progd 55 | relbase = 0 56 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 57 | def parse(): 58 | nonlocal pc 59 | op = prog[pc] 60 | pc += 1 61 | vals = [] 62 | locs = [] 63 | for i in range(arity[op % 100]): 64 | mode = (op // (10 ** (2 + i))) % 10 65 | vals.append(prog[pc] if mode == 1 else prog[prog[pc] + relbase] if mode == 2 else prog[prog[pc]]) 66 | locs.append(None if mode == 1 else prog[pc] + relbase if mode == 2 else prog[pc]) 67 | pc += 1 68 | return op % 100, vals, locs 69 | while prog[pc] != 99: 70 | opc = pc 71 | op, vals, locs = parse() 72 | #print(name,opc,op) 73 | if op == 1: 74 | prog[locs[2]] = vals[0] + vals[1] 75 | elif op == 2: 76 | prog[locs[2]] = vals[0] * vals[1] 77 | elif op == 3: 78 | #print(pc, prog, inputs) 79 | prog[locs[0]] = yield ('needinput', prog, opc) 80 | #print(name, 'got', prog[locs[0]]) 81 | assert prog[locs[0]] is not None 82 | elif op == 4: 83 | #print('out:', vals[0]) 84 | #outputs.append(vals[0]) 85 | #print(name, 'returning', prog[locs[0]]) 86 | yield vals[0] 87 | elif op == 5: 88 | if vals[0] != 0: 89 | pc = vals[1] 90 | elif op == 6: 91 | if vals[0] == 0: 92 | pc = vals[1] 93 | elif op == 7: 94 | prog[locs[2]] = int(vals[0] < vals[1]) 95 | elif op == 8: 96 | prog[locs[2]] = int(vals[0] == vals[1]) 97 | elif op == 9: 98 | relbase += vals[0] 99 | else: 100 | assert False 101 | #print(name,'done') 102 | return prog[0], outputs[0] if outputs else None 103 | 104 | with open(sys.argv[1]) as f: 105 | lines = [l.rstrip('\n') for l in f] 106 | prog = [[int(i) for i in l.split(',')] for l in lines][0] 107 | 108 | demomode = 0 109 | def ck(i, j): 110 | if demomode: 111 | return int(demo[i][j] != '.') 112 | p = tryprog(prog) 113 | next(p) 114 | p.send(i) 115 | o = p.send(j) 116 | return o 117 | 118 | s = 0 119 | for i in range(50): 120 | #for i in range(1820, 1930): 121 | print('%02d' % i, end=' ') 122 | for j in range(50): 123 | #for j in range(1980, 2090): 124 | p = tryprog(prog) 125 | next(p) 126 | p.send(i) 127 | o = p.send(j) 128 | s += o 129 | print('#' if o else '.', end='') 130 | print() 131 | print(s) 132 | 133 | tryto = 10000 134 | sqsi = 100 - 1 135 | if demomode: 136 | tryto = 35 137 | sqsi = 10 - 1 138 | distfromleftedge = {} 139 | j = 0 140 | for i in range(10, tryto): 141 | while ck(i, j) == 0: 142 | j += 1 143 | distfromleftedge[i] = j 144 | if i % 100 == 0: 145 | print(i, j) 146 | 147 | i = 0 148 | for j in range(10, tryto): 149 | while ck(i, j) == 0: 150 | i += 1 151 | if j % 100 == 0: 152 | print(i, j, '--', distfromleftedge.get(i + sqsi, 1e99) , j - sqsi) 153 | if distfromleftedge.get(i + sqsi, 1e99) <= j - sqsi: 154 | print(i, 'yes', distfromleftedge.get(i+sqsi)) 155 | print('tr corner', i, j) 156 | print('bl corner', i + sqsi, distfromleftedge[i+sqsi]) 157 | print(10000 * distfromleftedge.get(i+sqsi) + i) 158 | break 159 | #pts[j,i] = True 160 | 161 | -------------------------------------------------------------------------------- /2019/day2.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | def tryrun(prog, n, v): 9 | pc = 0 10 | prog[1] = n 11 | prog[2] = v 12 | 13 | while prog[pc] != 99: 14 | if prog[pc] == 1: 15 | a = prog[pc + 1] 16 | b = prog[pc + 2] 17 | c = prog[pc + 3] 18 | prog[c] = prog[a] + prog[b] 19 | pc += 4 20 | elif prog[pc] == 2: 21 | a = prog[pc + 1] 22 | b = prog[pc + 2] 23 | c = prog[pc + 3] 24 | prog[c] = prog[a] * prog[b] 25 | pc += 4 26 | return prog[0] 27 | 28 | 29 | with open(sys.argv[1]) as f: 30 | lines = [l.rstrip('\n') for l in f] 31 | lines = [[int(i) for i in re.findall(r'-?\d+', l)] for l in lines] 32 | 33 | pc = 0 34 | prog = lines[0] 35 | 36 | for n in range(12, 20000): 37 | x = tryrun(prog[:], n, 49) 38 | print(n, x) 39 | if x >= 19690720: 40 | break -------------------------------------------------------------------------------- /2019/day20.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | with open(sys.argv[1]) as f: 9 | lines = [l.rstrip('\n') for l in f] 10 | 11 | grid = lines 12 | 13 | warpa = {} 14 | warpb = {} 15 | for i in range(2, len(grid) - 2): 16 | for j in range(2, len(grid[0]) - 2): 17 | if grid[i][j] != '.': 18 | continue 19 | for pair in [ 20 | grid[i][j-2:j], 21 | grid[i][j+1:j+3], 22 | grid[i-2][j]+grid[i-1][j], 23 | grid[i+1][j]+grid[i+2][j], 24 | ]: 25 | if pair.isalpha(): 26 | if pair in warpa: 27 | warpb[i,j] = warpa[pair] 28 | warpa[i,j] = pair 29 | warpb[warpa[pair]] = i,j 30 | else: 31 | warpa[pair] = i,j 32 | warpa[i,j] = pair 33 | continue 34 | 35 | bfs = collections.deque([(warpa['AA'], 0, 0, '')]) 36 | seen = {} 37 | while bfs: 38 | pt, dist, depth, debug = bfs.popleft() 39 | if (pt, depth) in seen: 40 | continue 41 | seen[pt, depth] = dist 42 | if warpa['ZZ'] == pt and depth == 0: 43 | print(dist, debug) 44 | break 45 | i,j = pt 46 | for si,sj in [ 47 | (i,j+1), 48 | (i,j-1), 49 | (i+1,j), 50 | (i-1,j), 51 | ]: 52 | if grid[si][sj] == '.': 53 | bfs.append(((si, sj), dist+1, depth, debug)) 54 | if pt in warpb: 55 | # if 6 <= i <= 30 and 6 <= j <= 40: 56 | if 25 <= i <= 105 and 20 <= j <= 110: 57 | # inner 58 | bfs.append((warpb[pt], dist+1, depth+1, debug + ' +' + warpa[pt])) 59 | elif depth >= 1: 60 | # outer 61 | bfs.append((warpb[pt], dist+1, depth-1, debug + ' -' + warpa[pt])) 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /2019/day20fast.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import heapq 3 | import math 4 | import re 5 | import sys 6 | 7 | import sortedcollections 8 | 9 | with open(sys.argv[1]) as f: 10 | lines = [l.rstrip('\n') for l in f] 11 | 12 | grid = lines 13 | 14 | warpa = {} 15 | warpb = {} 16 | for i in range(2, len(grid) - 2): 17 | for j in range(2, len(grid[0]) - 2): 18 | if grid[i][j] != '.': 19 | continue 20 | for pair in [ 21 | grid[i][j-2:j], 22 | grid[i][j+1:j+3], 23 | grid[i-2][j]+grid[i-1][j], 24 | grid[i+1][j]+grid[i+2][j], 25 | ]: 26 | if pair.isalpha(): 27 | if pair in warpa: 28 | warpb[i,j] = warpa[pair] 29 | warpa[i,j] = pair 30 | warpb[warpa[pair]] = i,j 31 | else: 32 | warpa[pair] = i,j 33 | warpa[i,j] = pair 34 | continue 35 | 36 | interesting = set(pt for pt in warpa.keys() if type(pt) == tuple) 37 | bfs = [(0, pt, pt) for pt in interesting] 38 | seen = {} 39 | dists = collections.defaultdict(list) 40 | while bfs: 41 | dist, start, pt = heapq.heappop(bfs) 42 | if (start, pt) in seen: 43 | continue 44 | if pt in interesting: 45 | dists[start].append((pt, dist)) 46 | seen[start, pt] = dist 47 | i,j = pt 48 | for si,sj in [ 49 | (i,j+1), 50 | (i,j-1), 51 | (i+1,j), 52 | (i-1,j), 53 | ]: 54 | if grid[si][sj] == '.': 55 | heapq.heappush(bfs, (dist + 1, start, (si,sj))) 56 | 57 | bfs = [(0, warpa['AA'], 0, '')] 58 | seen = {} 59 | part1done = False 60 | while bfs: 61 | dist, pt, depth, debug = heapq.heappop(bfs) 62 | if (pt, depth) in seen: 63 | continue 64 | seen[pt, depth] = dist 65 | if not part1done and warpa['ZZ'] == pt: 66 | part1done = True 67 | print(dist, debug) 68 | if warpa['ZZ'] == pt and depth == 0: 69 | print(dist, debug) 70 | break 71 | i,j = pt 72 | if pt in dists: 73 | for pt2, d in dists[pt]: 74 | heapq.heappush(bfs, (dist+d, pt2, depth, debug)) 75 | if pt in warpb: 76 | if 3 <= i < len(grid) - 3 and 3 <= j < len(grid[0]) - 3: 77 | # inner 78 | heapq.heappush(bfs, (dist+1, warpb[pt], depth+1, debug + ' +' + warpa[pt])) 79 | elif depth >= 1: 80 | # outer 81 | heapq.heappush(bfs, (dist+1, warpb[pt], depth-1, debug + ' -' + warpa[pt])) 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /2019/day21.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import itertools 4 | import re 5 | import sys 6 | 7 | import sortedcollections 8 | import z3 9 | 10 | def tryprog(prog, pc=0, name=''): 11 | outputs = [] 12 | if isinstance(prog, collections.defaultdict): 13 | prog = collections.defaultdict(int, prog) 14 | else: 15 | progd = collections.defaultdict(int) 16 | for i, v in enumerate(prog): 17 | progd[i] = v 18 | prog = progd 19 | relbase = 0 20 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 21 | def parse(): 22 | nonlocal pc 23 | op = prog[pc] 24 | pc += 1 25 | vals = [] 26 | locs = [] 27 | for i in range(arity[op % 100]): 28 | mode = (op // (10 ** (2 + i))) % 10 29 | vals.append(prog[pc] if mode == 1 else prog[prog[pc] + relbase] if mode == 2 else prog[prog[pc]]) 30 | locs.append(None if mode == 1 else prog[pc] + relbase if mode == 2 else prog[pc]) 31 | pc += 1 32 | return op % 100, vals, locs 33 | while prog[pc] != 99: 34 | opc = pc 35 | op, vals, locs = parse() 36 | #print(name,opc,op) 37 | if op == 1: 38 | prog[locs[2]] = vals[0] + vals[1] 39 | elif op == 2: 40 | prog[locs[2]] = vals[0] * vals[1] 41 | elif op == 3: 42 | #print(pc, prog, inputs) 43 | prog[locs[0]] = yield ('needinput', prog, opc) 44 | #print(name, 'got', prog[locs[0]]) 45 | assert prog[locs[0]] is not None 46 | elif op == 4: 47 | #print('out:', vals[0]) 48 | #outputs.append(vals[0]) 49 | #print(name, 'returning', prog[locs[0]]) 50 | yield vals[0] 51 | elif op == 5: 52 | if vals[0] != 0: 53 | pc = vals[1] 54 | elif op == 6: 55 | if vals[0] == 0: 56 | pc = vals[1] 57 | elif op == 7: 58 | prog[locs[2]] = int(vals[0] < vals[1]) 59 | elif op == 8: 60 | prog[locs[2]] = int(vals[0] == vals[1]) 61 | elif op == 9: 62 | relbase += vals[0] 63 | else: 64 | assert False 65 | #print(name,'done') 66 | return prog[0], outputs[0] if outputs else None 67 | 68 | 69 | def solve(outputs): 70 | s = z3.Solver() 71 | insts = [z3.Int(f'inst{i}') for i in range(15)] 72 | opas = [z3.Int(f'opa{i}') for i in range(15)] 73 | opbs = [z3.Int(f'opb{i}') for i in range(15)] 74 | steps = z3.Int('steps') 75 | A,B,C,D = z3.Bools('a b c d') 76 | for inst in insts: 77 | s.add(0 <= inst, inst <= 2) 78 | for opa in opas: 79 | s.add(0 <= opa, opa < 6) 80 | for opb in opas: 81 | s.add(4 <= opb, opb < 6) 82 | s.add(1 <= steps, steps <= 15) 83 | T = False # 4 84 | J = False # 5 85 | for i in range(15): 86 | firstarg = z3.If(opas[i] == 0, A, 87 | z3.If(opas[i] == 1, B, 88 | z3.If(opas[i] == 2, C, 89 | z3.If(opas[i] == 3, D, 90 | z3.If(opas[i] == 4, T, J))))) 91 | sndarg = z3.If(opbs[i] == 0, A, 92 | z3.If(opbs[i] == 1, B, 93 | z3.If(opbs[i] == 2, C, 94 | z3.If(opbs[i] == 3, D, 95 | z3.If(opbs[i] == 4, T, J))))) 96 | newout = z3.If( 97 | insts[i] == 0, z3.And(firstarg, sndarg), 98 | z3.If(insts[i] == 1, z3.Or(firstarg, sndarg), 99 | z3.Not(firstarg))) 100 | newT = z3.If(opbs[i] == 4, newout, T) 101 | newJ = z3.If(opbs[i] == 5, newout, J) 102 | T = z3.If(i + 1 <= steps, newT, T) 103 | J = z3.If(i + 1 <= steps, newJ, J) 104 | for j, (a,b,c,d) in enumerate(itertools.product((False,True), repeat=4)): 105 | res = bool(outputs[j]) 106 | s.add(z3.Implies(A == a and B == b and C == c and D == d, res)) 107 | if s.check() == z3.sat: 108 | mod = s.model() 109 | prog = '' 110 | print(outputs, mod) 111 | for i in range(mod[steps].as_long()): 112 | def k(f,z): 113 | return f.as_long() if f else z 114 | inst = k(mod[insts[i]],0) 115 | opa = k(mod[opas[i]],0) 116 | opb = k(mod[opbs[i]],4) 117 | prog += f'{["AND","OR","NOT"][inst]} {"ABCDTJ"[opa]} {"ABCDTJ"[opb]}\n' 118 | return prog 119 | 120 | 121 | with open(sys.argv[1]) as f: 122 | lines = [l.rstrip('\n') for l in f] 123 | #lines = [[int(i) for i in re.findall(r'-?\d+', l)] for l in lines] 124 | prog = [[int(i) for i in l.split(',')] for l in lines][0] 125 | 126 | #for out in itertools.product((0,1), repeat=16): 127 | # script = solve(out) 128 | # if script: 129 | # print(out, script.replace('\n',' ')) 130 | 131 | p = tryprog(prog) 132 | while True: 133 | o = next(p) 134 | if type(o) == tuple: 135 | break 136 | #for c in 'NOT T T\nAND A T\nAND B T\nAND C T\nNOT T J\nAND D J\nWALK\n': 137 | # (not A or not B or not C) and D and (H or E and I) 138 | for c in ''.join([l.strip() + '\n' for l in ''' 139 | NOT J J 140 | AND E J 141 | AND I J 142 | OR H J 143 | NOT T T 144 | AND A T 145 | AND B T 146 | AND C T 147 | NOT T T 148 | AND T J 149 | AND D J 150 | NOT A T 151 | OR T J 152 | RUN 153 | '''.strip().splitlines()]): 154 | o = p.send(ord(c)) 155 | if type(o) == int: 156 | print(f'{chr(o)}',end='') 157 | while True: 158 | try: 159 | o = p.send(None) 160 | except StopIteration: 161 | break 162 | if type(o) == int and o < 128: 163 | print(f'{chr(o)}',end='') 164 | else: 165 | print(f'\n{o}') 166 | 167 | -------------------------------------------------------------------------------- /2019/day22.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | with open(sys.argv[1]) as f: 9 | lines = [l.rstrip('\n') for l in f] 10 | nlines = [[int(i) for i in re.findall(r'-?\d+', l)] for l in lines] 11 | 12 | ld = 10007 13 | deck = list(range(ld)) 14 | for line, nline in zip(lines, nlines): 15 | if line.startswith('deal with increment'): 16 | ndeck = [0] * ld 17 | inc = nline[0] 18 | for i in range(ld): 19 | ndeck[i * inc % ld] = deck[i] 20 | assert len(set(ndeck)) == ld 21 | elif line.startswith('cut'): 22 | inc = nline[0] 23 | ndeck = deck[inc:] + deck[:inc] 24 | elif line == 'deal into new stack': 25 | ndeck = deck[::-1] 26 | else: 27 | assert False, line 28 | #print(line, deck.index(2019)) 29 | deck = ndeck 30 | print(deck.index(2019)) 31 | 32 | ld = 119315717514047 33 | card = 2020 34 | times = 101741582076661 35 | # ld, card, times = 10007, 7665, 1 36 | 37 | # q came from aq + b 38 | a = 1 39 | b = 0 40 | for line, nline in reversed(list(zip(lines, nlines))): 41 | if line.startswith('deal with increment'): 42 | inc = nline[0] 43 | #card = card * pow(inc, ld-2,ld) % ld 44 | p = pow(inc, ld-2,ld) 45 | a *= p 46 | b *= p 47 | elif line.startswith('cut'): 48 | inc = nline[0] 49 | #card = (card - inc + ld) % ld 50 | b += inc 51 | elif line == 'deal into new stack': 52 | #card = ld - 1 - card 53 | b += 1 54 | a *= -1 55 | b *= -1 56 | else: 57 | assert False, line 58 | a %= ld 59 | b %= ld 60 | # print(line, (a * card + b) % ld) 61 | 62 | # q 63 | # aq + b 64 | # a(aq+b) + b = a^2q + ab + b 65 | # a(a^2q + ab + b) = a^3q + a^2b + ab + b 66 | # ... 67 | # a^t q + b * (a^t - 1) / (a - 1) 68 | print(( 69 | pow(a, times, ld) * card + 70 | b * (pow(a, times, ld) +ld- 1) 71 | * (pow(a-1, ld - 2, ld)) 72 | ) % ld) -------------------------------------------------------------------------------- /2019/day23.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | def tryprog(prog, pc=0, name=''): 9 | outputs = [] 10 | if isinstance(prog, collections.defaultdict): 11 | prog = collections.defaultdict(int, prog) 12 | else: 13 | progd = collections.defaultdict(int) 14 | for i, v in enumerate(prog): 15 | progd[i] = v 16 | prog = progd 17 | relbase = 0 18 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 19 | def parse(): 20 | nonlocal pc 21 | op = prog[pc] 22 | pc += 1 23 | vals = [] 24 | locs = [] 25 | for i in range(arity[op % 100]): 26 | mode = (op // (10 ** (2 + i))) % 10 27 | vals.append(prog[pc] if mode == 1 else prog[prog[pc] + relbase] if mode == 2 else prog[prog[pc]]) 28 | locs.append(None if mode == 1 else prog[pc] + relbase if mode == 2 else prog[pc]) 29 | pc += 1 30 | return op % 100, vals, locs 31 | while prog[pc] != 99: 32 | opc = pc 33 | #print(name,pc) 34 | op, vals, locs = parse() 35 | #print(name,opc,op) 36 | if op == 1: 37 | prog[locs[2]] = vals[0] + vals[1] 38 | elif op == 2: 39 | prog[locs[2]] = vals[0] * vals[1] 40 | elif op == 3: 41 | #print(pc, prog, inputs) 42 | prog[locs[0]] = yield ('needinput', prog, opc) 43 | #print(name, 'got', prog[locs[0]]) 44 | assert prog[locs[0]] is not None 45 | elif op == 4: 46 | #print('out:', vals[0]) 47 | #outputs.append(vals[0]) 48 | #print(name, 'returning', prog[locs[0]]) 49 | yield vals[0] 50 | elif op == 5: 51 | if vals[0] != 0: 52 | pc = vals[1] 53 | elif op == 6: 54 | if vals[0] == 0: 55 | pc = vals[1] 56 | elif op == 7: 57 | prog[locs[2]] = int(vals[0] < vals[1]) 58 | elif op == 8: 59 | prog[locs[2]] = int(vals[0] == vals[1]) 60 | elif op == 9: 61 | relbase += vals[0] 62 | else: 63 | assert False 64 | #print(name,'done') 65 | return prog[0], outputs[0] if outputs else None 66 | 67 | 68 | with open(sys.argv[1]) as f: 69 | lines = [l.rstrip('\n') for l in f] 70 | #lines = [[int(i) for i in re.findall(r'-?\d+', l)] for l in lines] 71 | prog = [[int(i) for i in l.split(',')] for l in lines][0] 72 | 73 | qs = [[] for _ in range(50)] 74 | ps = [tryprog(prog, name=i) for i in range(50)] 75 | for p in ps: 76 | assert type(p.send(None)) == tuple 77 | ns = [i for i in range(50)] 78 | 79 | done = False 80 | part1 = True 81 | nat = None 82 | lastY = None 83 | while not done: 84 | if not any(qs) and all(a == -1 for a in ns) and nat: 85 | qs[0].append(nat) 86 | if lastY == nat[1]: 87 | print(nat[1]) 88 | break 89 | lastY = nat[1] 90 | nat = None 91 | for i in range(50): 92 | t = ps[i].send(ns[i]) 93 | if type(t) == tuple: 94 | # input 95 | if qs[i]: 96 | x, y = qs[i][0] 97 | del qs[i][0] 98 | ps[i].send(x) 99 | ns[i] = y 100 | else: 101 | ns[i] = -1 102 | else: 103 | add = t 104 | x = ps[i].send(None) 105 | y = ps[i].send(None) 106 | if add == 255: 107 | if part1: 108 | print(y) 109 | part1 = False 110 | nat = x, y 111 | continue 112 | assert 0 <= add <= 49 113 | qs[add].append((x,y)) 114 | -------------------------------------------------------------------------------- /2019/day23b.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import itertools 3 | import math 4 | import re 5 | import sys 6 | 7 | import sortedcollections 8 | 9 | class Intcode: 10 | def __init__(self, prog, pc=0, name=''): 11 | self.prog = prog[:] 12 | self.pc = pc 13 | self.name = name 14 | self.relbase = 0 15 | # stopped in out 16 | self.state = None 17 | self.outputq = None 18 | self.inputq = None 19 | self._run() 20 | def _pset(self, i, v): 21 | if i >= len(self.prog): 22 | self.prog.extend(0 for _ in range(1 + i - len(self.prog))) 23 | self.prog[i] = v 24 | def _pget(self, i): 25 | if i >= len(self.prog): 26 | self.prog.extend(0 for _ in range(1 + i - len(self.prog))) 27 | return self.prog[i] 28 | def outputs(self): 29 | while self.state == 'out': 30 | yield self.nextout() 31 | def nextout(self): 32 | assert self.state == 'out', self.state 33 | assert self.outputq is not None 34 | o = self.outputq 35 | self.outputq = None 36 | self.state = None 37 | self._run() 38 | return o 39 | def send(self, *inp): 40 | for v in inp: 41 | assert self.state == 'in', self.state 42 | self._pset(self.inputq, v) 43 | self.inputq = None 44 | self.state = None 45 | self._run() 46 | return self 47 | def _run(self): 48 | if self.state: 49 | raise ValueError(self.state) 50 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 51 | def parse(): 52 | op = self._pget(self.pc) 53 | self.pc += 1 54 | vals = [] 55 | locs = [] 56 | for i in range(arity[op % 100]): 57 | mode = (op // (10 ** (2 + i))) % 10 58 | vals.append( 59 | self._pget(self.pc) if mode == 1 else 60 | self._pget(self._pget(self.pc) + self.relbase) if mode == 2 else 61 | self._pget(self._pget(self.pc)) 62 | ) 63 | locs.append( 64 | None if mode == 1 else 65 | self._pget(self.pc) + self.relbase if mode == 2 else 66 | self._pget(self.pc) 67 | ) 68 | self.pc += 1 69 | return op % 100, vals, locs 70 | while True: 71 | if self._pget(self.pc) == 99: 72 | self.state = 'stopped' 73 | return 74 | opc = self.pc 75 | op, vals, locs = parse() 76 | #print(self.name,opc,op) 77 | if op == 1: 78 | self._pset(locs[2], vals[0] + vals[1]) 79 | elif op == 2: 80 | self._pset(locs[2], vals[0] * vals[1]) 81 | elif op == 3: 82 | assert self.inputq is None 83 | self.state = 'in' 84 | self.inputq = locs[0] 85 | return 86 | elif op == 4: 87 | #print(name, 'returning', self._pget(locs[0])) 88 | assert self.outputq is None 89 | self.state = 'out' 90 | self.outputq = vals[0] 91 | return 92 | elif op == 5: 93 | if vals[0] != 0: 94 | self.pc = vals[1] 95 | elif op == 6: 96 | if vals[0] == 0: 97 | self.pc = vals[1] 98 | elif op == 7: 99 | self._pset(locs[2], int(vals[0] < vals[1])) 100 | elif op == 8: 101 | self._pset(locs[2], int(vals[0] == vals[1])) 102 | elif op == 9: 103 | self.relbase += vals[0] 104 | else: 105 | assert False 106 | 107 | 108 | with open(sys.argv[1]) as f: 109 | lines = [l.rstrip('\n') for l in f] 110 | prog = [[int(i) for i in l.split(',')] for l in lines][0] 111 | 112 | qs = [[] for _ in range(50)] 113 | ps = [Intcode(prog, name=i).send(i) for i in range(50)] 114 | 115 | part1 = True 116 | nat = None 117 | lastY = None 118 | while True: 119 | if not any(qs) and nat: 120 | qs[0].append(nat) 121 | if lastY == nat[1]: 122 | print(nat[1]) 123 | break 124 | lastY = nat[1] 125 | nat = None 126 | for i in range(50): 127 | if ps[i].state == 'in': 128 | if qs[i]: 129 | x, y = qs[i].pop(0) 130 | ps[i].send(x, y) 131 | else: 132 | ps[i].send(-1) 133 | while ps[i].state == 'out': 134 | add = ps[i].nextout() 135 | x = ps[i].nextout() 136 | y = ps[i].nextout() 137 | if add == 255: 138 | if part1: 139 | print(y) 140 | part1 = False 141 | nat = x, y 142 | continue 143 | assert 0 <= add <= 49 144 | qs[add].append((x,y)) 145 | -------------------------------------------------------------------------------- /2019/day24.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | def step(grid): 9 | grid2 = [] 10 | def get(x, y): 11 | a = [] 12 | if x > 0: 13 | a.append(grid[x-1][y]) 14 | if x < len(grid)-1: 15 | a.append(grid[x+1][y]) 16 | if y > 0: 17 | a.append(grid[x][y-1]) 18 | if y < len(grid[0])-1: 19 | a.append(grid[x][y+1]) 20 | return a 21 | for i in range(len(grid)): 22 | line2 = '' 23 | for j in range(len(grid[0])): 24 | if grid[i][j] == '#': 25 | ch = '#' if get(i,j).count('#') == 1 else '.' 26 | else: 27 | ch = '#' if get(i,j).count('#') in (1,2) else '.' 28 | line2 += ch 29 | grid2.append(line2) 30 | return grid2 31 | 32 | 33 | def stepb(bugs): 34 | minlev = min(l for l, x, y in bugs) 35 | maxlev = max(l for l, x, y in bugs) 36 | 37 | def countadj(l, x, y): 38 | c = 0 39 | adj = [ 40 | (l, x-1,y), 41 | (l, x+1,y), 42 | (l, x,y-1), 43 | (l, x,y+1), 44 | ] 45 | actadj = [] 46 | for aa in adj: 47 | la, xa, ya = aa 48 | if xa == -1: 49 | actadj.append((la-1, 1, 2)) 50 | elif xa == 2 and ya == 2 and x == 1: 51 | actadj.append((la+1, 0, 0)) 52 | actadj.append((la+1, 0, 1)) 53 | actadj.append((la+1, 0, 2)) 54 | actadj.append((la+1, 0, 3)) 55 | actadj.append((la+1, 0, 4)) 56 | elif xa == 2 and ya == 2 and x == 3: 57 | actadj.append((la+1, 4, 0)) 58 | actadj.append((la+1, 4, 1)) 59 | actadj.append((la+1, 4, 2)) 60 | actadj.append((la+1, 4, 3)) 61 | actadj.append((la+1, 4, 4)) 62 | elif xa == 5: 63 | actadj.append((la-1, 3, 2)) 64 | elif ya == -1: 65 | actadj.append((la-1, 2, 1)) 66 | elif ya == 2 and xa == 2 and y == 1: 67 | actadj.append((la+1, 0, 0)) 68 | actadj.append((la+1, 1, 0)) 69 | actadj.append((la+1, 2, 0)) 70 | actadj.append((la+1, 3, 0)) 71 | actadj.append((la+1, 4, 0)) 72 | elif ya == 2 and xa == 2 and y == 3: 73 | actadj.append((la+1, 0, 4)) 74 | actadj.append((la+1, 1, 4)) 75 | actadj.append((la+1, 2, 4)) 76 | actadj.append((la+1, 3, 4)) 77 | actadj.append((la+1, 4, 4)) 78 | elif ya == 5: 79 | actadj.append((la-1, 2, 3)) 80 | else: 81 | actadj.append(aa) 82 | #print('actadj', l, x, y, sorted(actadj)) 83 | return sum(1 for aa in actadj if aa in bugs) 84 | 85 | nbugs = set() 86 | for lev in range(minlev - 1, maxlev + 2): 87 | for i in range(5): 88 | for j in range(5): 89 | if i==2 and j==2: 90 | continue 91 | if (lev,i,j) in bugs and countadj(lev,i,j) == 1: 92 | nbugs.add((lev,i,j)) 93 | if (lev,i,j) not in bugs and countadj(lev,i,j) in (1,2): 94 | nbugs.add((lev,i,j)) 95 | return nbugs 96 | 97 | 98 | with open(sys.argv[1]) as f: 99 | lines = [l.rstrip('\n') for l in f] 100 | 101 | grid = lines 102 | seengrid = set() 103 | while True: 104 | fgrid = ''.join(grid) 105 | if fgrid in seengrid: 106 | break 107 | seengrid.add(fgrid) 108 | grid = step(grid) 109 | print(sum(2 ** i for i, ch in enumerate(fgrid) if ch == '#')) 110 | 111 | bugs = set() 112 | for i in range(5): 113 | for j in range(5): 114 | if lines[i][j] == '#': 115 | bugs.add((0,i,j)) 116 | for i in range(200): 117 | bugs = stepb(bugs) 118 | print(len(bugs)) 119 | 120 | -------------------------------------------------------------------------------- /2019/day25.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import copy 3 | import math 4 | import re 5 | import sys 6 | 7 | import sortedcollections 8 | 9 | class Intcode: 10 | def __init__(self, prog, pc=0, name=''): 11 | self.prog = prog[:] 12 | self.pc = pc 13 | self.name = name 14 | self.relbase = 0 15 | # stopped in out 16 | self.state = None 17 | self.outputq = None 18 | self.inputq = None 19 | self._run() 20 | def _pset(self, i, v): 21 | if i >= len(self.prog): 22 | self.prog.extend(0 for _ in range(1 + i - len(self.prog))) 23 | self.prog[i] = v 24 | def _pget(self, i): 25 | if i >= len(self.prog): 26 | self.prog.extend(0 for _ in range(1 + i - len(self.prog))) 27 | return self.prog[i] 28 | def outputs(self): 29 | while self.state == 'out': 30 | yield self.nextout() 31 | def nextout(self): 32 | assert self.state == 'out', self.state 33 | assert self.outputq is not None 34 | o = self.outputq 35 | self.outputq = None 36 | self.state = None 37 | self._run() 38 | return o 39 | def send(self, *inp): 40 | for v in inp: 41 | assert self.state == 'in', self.state 42 | self._pset(self.inputq, v) 43 | self.inputq = None 44 | self.state = None 45 | self._run() 46 | return self 47 | def _run(self): 48 | if self.state: 49 | raise ValueError(self.state) 50 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 51 | def parse(): 52 | op = self._pget(self.pc) 53 | self.pc += 1 54 | vals = [] 55 | locs = [] 56 | for i in range(arity[op % 100]): 57 | mode = (op // (10 ** (2 + i))) % 10 58 | vals.append( 59 | self._pget(self.pc) if mode == 1 else 60 | self._pget(self._pget(self.pc) + self.relbase) if mode == 2 else 61 | self._pget(self._pget(self.pc)) 62 | ) 63 | locs.append( 64 | None if mode == 1 else 65 | self._pget(self.pc) + self.relbase if mode == 2 else 66 | self._pget(self.pc) 67 | ) 68 | self.pc += 1 69 | return op % 100, vals, locs 70 | while True: 71 | if self._pget(self.pc) == 99: 72 | self.state = 'stopped' 73 | return 74 | opc = self.pc 75 | op, vals, locs = parse() 76 | #print(self.name,opc,op) 77 | if op == 1: 78 | self._pset(locs[2], vals[0] + vals[1]) 79 | elif op == 2: 80 | self._pset(locs[2], vals[0] * vals[1]) 81 | elif op == 3: 82 | assert self.inputq is None 83 | self.state = 'in' 84 | self.inputq = locs[0] 85 | return 86 | elif op == 4: 87 | #print(name, 'returning', self._pget(locs[0])) 88 | assert self.outputq is None 89 | self.state = 'out' 90 | self.outputq = vals[0] 91 | return 92 | elif op == 5: 93 | if vals[0] != 0: 94 | self.pc = vals[1] 95 | elif op == 6: 96 | if vals[0] == 0: 97 | self.pc = vals[1] 98 | elif op == 7: 99 | self._pset(locs[2], int(vals[0] < vals[1])) 100 | elif op == 8: 101 | self._pset(locs[2], int(vals[0] == vals[1])) 102 | elif op == 9: 103 | self.relbase += vals[0] 104 | else: 105 | assert False 106 | 107 | with open(sys.argv[1]) as f: 108 | lines = [l.rstrip('\n') for l in f] 109 | #lines = [[int(i) for i in re.findall(r'-?\d+', l)] for l in lines] 110 | prog = [[int(i) for i in l.split(',')] for l in lines][0] 111 | 112 | p = Intcode(prog) 113 | 114 | bfs = collections.deque([(0, 0, frozenset(), p)]) 115 | seen = {} 116 | rooms = {} 117 | msgseen = set() 118 | while bfs: 119 | x, y, ic, p = bfs.popleft() 120 | #if (x,y, ic) in seen: 121 | # continue 122 | #seen[(x,y,ic)]=True 123 | state = ''.join(map(chr, p.outputs())).splitlines() 124 | if len(state) >= 5: 125 | roomname = state[4] 126 | if (roomname,ic) in seen: 127 | continue 128 | seen[(roomname,ic)] = True 129 | else: 130 | print('huh?', ic, state) 131 | break 132 | print(x, y, ic) 133 | state1 = ' '.join(state) 134 | if state1 not in msgseen: 135 | msgseen.add(state1) 136 | print(state1) 137 | if "You can't go that way." in state: 138 | continue 139 | #if '== Security Checkpoint ==' in state: 140 | # p.send(*(ord(c) for c in 'inv\n')) 141 | # its = frozenset([l[2:] for l in ''.join(map(chr, p.outputs())).splitlines() if l.startswith('- ')]) 142 | # print(ic == its) 143 | #if '== Arcade ==' in state and 'fuel cell' in ic: 144 | # p.send(*(ord(c) for c in 'drop fuel cell\n')) 145 | # print(''.join(map(chr, p.outputs())).splitlines()) 146 | def f(p, ic): 147 | if '- north' in state: 148 | bfs.append((x-1,y,ic, copy.deepcopy(p).send(*(ord(c) for c in 'north\n')))) 149 | if '- south' in state: 150 | bfs.append((x+1,y,ic, copy.deepcopy(p).send(*(ord(c) for c in 'south\n')))) 151 | if '- east' in state: 152 | # I typoed this line as 'west\n' and ended up never traversing the 'east' branches 153 | bfs.append((x,y+1,ic, copy.deepcopy(p).send(*(ord(c) for c in 'east\n')))) 154 | if '- west' in state: 155 | bfs.append((x,y-1,ic, copy.deepcopy(p).send(*(ord(c) for c in 'west\n')))) 156 | f(p, ic) 157 | if 'Items here:' in state: 158 | it = state[state.index('Items here:')+1][2:] 159 | assert not state[state.index('Items here:')+2].startswith('- ') 160 | if it not in ('escape pod', 'infinite loop', 'giant electromagnet', 'photons'): 161 | p2 = copy.deepcopy(p).send(*(ord(c) for c in f'take {it}\n')) 162 | tkm = ''.join(map(chr, p2.outputs())).splitlines() 163 | print('took', tkm) 164 | if p2.state != 'stopped': 165 | f(p2, ic.union([it])) 166 | #for it in ic: 167 | # p2 = copy.deepcopy(p).send(*(ord(c) for c in f'drop {it}\n')) 168 | # print('dropped', ''.join(map(chr, p2.outputs())).splitlines()) 169 | # if p2.state != 'stopped': 170 | # f(p2, ic.difference([it])) 171 | 172 | 173 | #print(state) 174 | #p.send(*(ord(c) for c in 'north\n')) 175 | #state = ''.join(map(chr, p.outputs())).splitlines() 176 | #print(state) 177 | #p.send(*(ord(c) for c in 'north\n')) 178 | #state = ''.join(map(chr, p.outputs())).splitlines() 179 | #print(state) 180 | -------------------------------------------------------------------------------- /2019/day25fast.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import copy 3 | import itertools 4 | import math 5 | import re 6 | import sys 7 | 8 | import sortedcollections 9 | 10 | class Intcode: 11 | def __init__(self, prog, pc=0, name=''): 12 | self.prog = prog[:] 13 | self.pc = pc 14 | self.name = name 15 | self.relbase = 0 16 | # stopped in out 17 | self.state = None 18 | self.outputq = None 19 | self.inputq = None 20 | self._run() 21 | def _pset(self, i, v): 22 | if i >= len(self.prog): 23 | self.prog.extend(0 for _ in range(1 + i - len(self.prog))) 24 | self.prog[i] = v 25 | def _pget(self, i): 26 | try: 27 | return self.prog[i] 28 | except IndexError: 29 | return 0 30 | def outputs(self): 31 | while self.state == 'out': 32 | yield self.nextout() 33 | def nextout(self): 34 | assert self.state == 'out', self.state 35 | assert self.outputq is not None 36 | o = self.outputq 37 | self.outputq = None 38 | self.state = None 39 | self._run() 40 | return o 41 | def send(self, *inp): 42 | for v in inp: 43 | assert self.state == 'in', self.state 44 | self._pset(self.inputq, v) 45 | self.inputq = None 46 | self.state = None 47 | self._run() 48 | return self 49 | def _run(self): 50 | if self.state: 51 | raise ValueError(self.state) 52 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 53 | def parse(): 54 | op = self._pget(self.pc) 55 | self.pc += 1 56 | vals = [] 57 | locs = [] 58 | for i in range(arity[op % 100]): 59 | mode = (op // (10 ** (2 + i))) % 10 60 | vals.append( 61 | self._pget(self.pc) if mode == 1 else 62 | self._pget(self._pget(self.pc) + self.relbase) if mode == 2 else 63 | self._pget(self._pget(self.pc)) 64 | ) 65 | locs.append( 66 | None if mode == 1 else 67 | self._pget(self.pc) + self.relbase if mode == 2 else 68 | self._pget(self.pc) 69 | ) 70 | self.pc += 1 71 | return op % 100, vals, locs 72 | while True: 73 | if self._pget(self.pc) == 99: 74 | self.state = 'stopped' 75 | return 76 | opc = self.pc 77 | op, vals, locs = parse() 78 | #print(self.name,opc,op) 79 | if op == 1: 80 | self._pset(locs[2], vals[0] + vals[1]) 81 | elif op == 2: 82 | self._pset(locs[2], vals[0] * vals[1]) 83 | elif op == 3: 84 | assert self.inputq is None 85 | self.state = 'in' 86 | self.inputq = locs[0] 87 | return 88 | elif op == 4: 89 | #print(name, 'returning', self._pget(locs[0])) 90 | assert self.outputq is None 91 | self.state = 'out' 92 | self.outputq = vals[0] 93 | return 94 | elif op == 5: 95 | if vals[0] != 0: 96 | self.pc = vals[1] 97 | elif op == 6: 98 | if vals[0] == 0: 99 | self.pc = vals[1] 100 | elif op == 7: 101 | self._pset(locs[2], int(vals[0] < vals[1])) 102 | elif op == 8: 103 | self._pset(locs[2], int(vals[0] == vals[1])) 104 | elif op == 9: 105 | self.relbase += vals[0] 106 | else: 107 | assert False 108 | 109 | def powerset(iterable): 110 | xs = list(iterable) 111 | return itertools.chain.from_iterable( 112 | itertools.combinations(xs,n) for n in range(len(xs)+1) 113 | ) 114 | 115 | with open(sys.argv[1]) as f: 116 | lines = [l.rstrip('\n') for l in f] 117 | prog = [[int(i) for i in l.split(',')] for l in lines][0] 118 | 119 | p = Intcode(prog) 120 | 121 | rooms = {} 122 | have = set() 123 | def traverse(path): 124 | global p 125 | state = ''.join(map(chr, p.outputs())).splitlines() 126 | roomname = state[3] 127 | assert roomname.startswith('== '), state 128 | if roomname in rooms: 129 | return 130 | rooms[roomname] = path 131 | 132 | if 'Items here:' in state: 133 | it = state[state.index('Items here:')+1][2:] 134 | assert not state[state.index('Items here:')+2].startswith('- ') 135 | if it not in ('escape pod', 'infinite loop', 'giant electromagnet', 'photons', 'molten lava'): 136 | p = p.send(*(ord(c) for c in f'take {it}\n')) 137 | tkm = ''.join(map(chr, p.outputs())).splitlines() 138 | print('took', tkm) 139 | assert p.state != 'stopped' 140 | have.add(it) 141 | 142 | for direc in ['north', 'south', 'east', 'west']: 143 | if f'- {direc}' in state: 144 | p.send(*(ord(c) for c in f'{direc}\n')) 145 | traverse(path + (direc,)) 146 | bw = { 147 | 'north': 'south', 148 | 'south': 'north', 149 | 'east': 'west', 150 | 'west': 'east', 151 | } 152 | p.send(*(ord(c) for c in f'{bw[direc]}\n')) 153 | list(p.outputs()) 154 | traverse(()) 155 | 156 | for step in rooms['== Security Checkpoint ==']: 157 | p.send(*(ord(c) for c in f'{step}\n')) 158 | list(p.outputs()) 159 | 160 | for it in have: 161 | p.send(*(ord(c) for c in f'drop {it}\n')) 162 | list(p.outputs()) 163 | 164 | for ss in powerset(have): 165 | p2 = copy.deepcopy(p) 166 | for it in ss: 167 | p2.send(*(ord(c) for c in f'take {it}\n')) 168 | list(p2.outputs()) 169 | p2.send(*(ord(c) for c in f'west\n')) 170 | o = ''.join(map(chr, p2.outputs())).splitlines() 171 | print(ss) 172 | if p2.state == 'stopped': 173 | print('\n'.join(o)) 174 | break 175 | -------------------------------------------------------------------------------- /2019/day3.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | with open(sys.argv[1]) as f: 9 | lines = [l.rstrip('\n') for l in f] 10 | lines = [l.split(',') for l in lines] 11 | 12 | grid = collections.defaultdict(lambda: '.') 13 | grid[(0, 0)] = 'o' 14 | ga = {} 15 | x, y = 0, 0 16 | steps = 0 17 | for ins in lines[0]: 18 | d, l = ins[0], int(ins[1:]) 19 | for i in range(l): 20 | if d == 'R': 21 | x += 1 22 | elif d == 'L': 23 | x -= 1 24 | elif d == 'U': 25 | y += 1 26 | elif d == 'D': 27 | y -= 1 28 | else: 29 | assert False 30 | grid[(x, y)] = '-' 31 | if (x, y) not in ga: 32 | ga[(x, y)] = steps 33 | steps += 1 34 | 35 | coll = {} 36 | 37 | x, y = 0, 0 38 | steps = 0 39 | for ins in lines[1]: 40 | d, l = ins[0], int(ins[1:]) 41 | for i in range(l): 42 | if d == 'R': 43 | x += 1 44 | elif d == 'L': 45 | x -= 1 46 | elif d == 'U': 47 | y += 1 48 | elif d == 'D': 49 | y -= 1 50 | else: 51 | assert False 52 | if grid[(x, y)] == '-': 53 | coll[(x, y)] = steps + ga[(x, y)] 54 | grid[(x, y)] = '+' 55 | else: 56 | grid[(x, y)] = '|' 57 | steps += 1 58 | 59 | 60 | 61 | print(coll) 62 | #print(sorted(coll, key=lambda xy: abs(xy[0]) + abs(xy[1]))) 63 | #print(min(abs(x) + abs(y) for x, y in coll)) 64 | m = min(coll.values()) 65 | print(m) 66 | #print([k, v for k, v in coll.items() if v == m]) -------------------------------------------------------------------------------- /2019/day4.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | import re 3 | 4 | uq = set() 5 | for x in range(231832,767346+1): 6 | two = False 7 | for g, l in itertools.groupby(list(str(x)), lambda x: x): 8 | q = len(list(l)) 9 | if q == 2: 10 | two = True 11 | # part 1: if re.match(r'.*(\d)\1', str(x)) and 12 | if two and sorted(str(x)) == list(str(x)): 13 | uq.add(x) 14 | 15 | print(sorted(uq)) 16 | print(len(uq)) 17 | 18 | -------------------------------------------------------------------------------- /2019/day5.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | def tryrun(prog): 9 | pc = 0 10 | inputs = [5] 11 | 12 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3} 13 | def parse(): 14 | nonlocal pc 15 | op = prog[pc] 16 | pc += 1 17 | vals = [] 18 | locs = [] 19 | for i in range(arity[op % 100]): 20 | mode = (op // (10 ** (2 + i))) % 10 21 | vals.append(prog[pc] if mode == 1 else prog[prog[pc]]) 22 | locs.append(None if mode == 1 else prog[pc]) 23 | pc += 1 24 | return op % 100, vals, locs 25 | 26 | while prog[pc] != 99: 27 | op, vals, locs = parse() 28 | if op == 1: 29 | prog[locs[2]] = vals[0] + vals[1] 30 | elif op == 2: 31 | prog[locs[2]] = vals[0] * vals[1] 32 | elif op == 3: 33 | prog[locs[0]] = inputs[0] 34 | del inputs[0] 35 | elif op == 4: 36 | print('out:', vals[0]) 37 | elif op == 5: 38 | if vals[0] != 0: 39 | pc = vals[1] 40 | elif op == 6: 41 | if vals[0] == 0: 42 | pc = vals[1] 43 | elif op == 7: 44 | prog[locs[2]] = int(vals[0] < vals[1]) 45 | elif op == 8: 46 | prog[locs[2]] = int(vals[0] == vals[1]) 47 | else: 48 | assert False 49 | 50 | return prog[0] 51 | 52 | 53 | with open(sys.argv[1]) as f: 54 | lines = [l.rstrip('\n') for l in f] 55 | lines = [[int(i) for i in l.split(',')] for l in lines] 56 | 57 | tryrun(lines[0]) 58 | -------------------------------------------------------------------------------- /2019/day6.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | with open(sys.argv[1]) as f: 9 | lines = [l.rstrip('\n') for l in f] 10 | 11 | starts = set() 12 | d = collections.defaultdict(set) 13 | for line in lines: 14 | a, b = line.split(')') 15 | d[a].add(b) 16 | starts.add(a) 17 | 18 | bw = {} 19 | for a, bs in d.items(): 20 | for b in bs: 21 | bw[b] = a 22 | 23 | n = 0 24 | for b in bw.keys(): 25 | while True: 26 | par = bw.get(b) 27 | if par: 28 | n += 1 29 | b = par 30 | else: 31 | break 32 | print(n) 33 | 34 | # original part 2 approach that I scrapped (not the most efficient): 35 | # 36 | # for i in range(10000): 37 | # for j in range(10000): 38 | # try: 39 | # you = 'YOU' 40 | # for _ in range(i): 41 | # you = bw[you] 42 | # san = 'SAN' 43 | # for _ in range(i): 44 | # san = bw[san] 45 | # except KeyError: 46 | # pass 47 | # if san == you: 48 | # print(i, j) 49 | # sys.exit() 50 | 51 | 52 | def parents(n): 53 | l = [] 54 | while n: 55 | l.append(n) 56 | n = bw.get(n) 57 | return l 58 | 59 | you = parents('YOU') 60 | san = parents('SAN') 61 | 62 | for i, (y, s) in enumerate(zip(reversed(you), reversed(san))): 63 | if y != s: 64 | print(len(you) - i + len(san) - i - 2) 65 | break 66 | -------------------------------------------------------------------------------- /2019/day7.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | def tryprog(prog, name): 9 | pc = 0 10 | outputs = [] 11 | 12 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3} 13 | def parse(): 14 | nonlocal pc 15 | op = prog[pc] 16 | pc += 1 17 | vals = [] 18 | locs = [] 19 | for i in range(arity[op % 100]): 20 | mode = (op // (10 ** (2 + i))) % 10 21 | vals.append(prog[pc] if mode == 1 else prog[prog[pc]]) 22 | locs.append(None if mode == 1 else prog[pc]) 23 | pc += 1 24 | return op % 100, vals, locs 25 | 26 | while prog[pc] != 99: 27 | opc = pc 28 | op, vals, locs = parse() 29 | #print(name,opc,op, prog[:opc], prog[opc], prog[opc+1:]) 30 | if op == 1: 31 | prog[locs[2]] = vals[0] + vals[1] 32 | elif op == 2: 33 | prog[locs[2]] = vals[0] * vals[1] 34 | elif op == 3: 35 | #print(pc, prog, inputs) 36 | prog[locs[0]] = yield 37 | #print(name, 'got', prog[locs[0]]) 38 | assert prog[locs[0]] is not None 39 | elif op == 4: 40 | #print('out:', vals[0]) 41 | #outputs.append(vals[0]) 42 | #print(name, 'returning', prog[locs[0]]) 43 | yield vals[0] 44 | elif op == 5: 45 | if vals[0] != 0: 46 | pc = vals[1] 47 | elif op == 6: 48 | if vals[0] == 0: 49 | pc = vals[1] 50 | elif op == 7: 51 | prog[locs[2]] = int(vals[0] < vals[1]) 52 | elif op == 8: 53 | prog[locs[2]] = int(vals[0] == vals[1]) 54 | else: 55 | assert False 56 | 57 | #print(name,'done') 58 | return prog[0], outputs[0] if outputs else None 59 | 60 | with open(sys.argv[1]) as f: 61 | lines = [l.rstrip('\n') for l in f] 62 | prog = [int(c) for c in lines[0].split(',')] 63 | 64 | m = 0 65 | for a in range(5,10): 66 | for b in range(5,10): 67 | for c in range(5,10): 68 | for d in range(5,10): 69 | for e in range(5,10): 70 | if len(set([a,b,c,d,e]))!=5: 71 | continue 72 | #if [a,b,c,d,e]!=[9,8,7,6,5]: 73 | # continue 74 | ap = tryprog(prog[:],'a') 75 | bp = tryprog(prog[:],'b') 76 | cp = tryprog(prog[:],'c') 77 | dp = tryprog(prog[:],'d') 78 | ep = tryprog(prog[:],'e') 79 | 80 | next(ap); ap.send(a) 81 | next(bp); bp.send(b) 82 | next(cp); cp.send(c) 83 | next(dp); dp.send(d) 84 | next(ep); ep.send(e) 85 | 86 | eo = -123 87 | val = 0 88 | for lo in range(10000000000000000): 89 | done = False 90 | for p in (ap, bp, cp, dp, ep): 91 | try: 92 | #print('[') 93 | if lo != 0: 94 | next(p) 95 | val = p.send(val) 96 | #print(']') 97 | if p == ep: 98 | eo = val 99 | except StopIteration: 100 | done = True 101 | break 102 | if done: 103 | break 104 | #print(eo, a, b, c, d, e) 105 | if eo > m: 106 | m = eo 107 | print('best', m, a, b, c, d, e) 108 | 109 | 110 | 111 | 112 | #_, eo = tryprog(prog, [e, do]) 113 | #maxe = eo 114 | #o = eo 115 | #while True: 116 | # stop = False 117 | # for p in (a, b, c, d, e): 118 | # _, o = tryprog(prog, [p, o]) 119 | # if o is None: 120 | # stop = True 121 | # break 122 | # if p == e and True:#o > maxe: 123 | # maxe = o 124 | # if stop: 125 | # break 126 | #if maxe > m: 127 | # m = maxe 128 | # #print(m, a, b, c, d, e) 129 | -------------------------------------------------------------------------------- /2019/day8.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | with open(sys.argv[1]) as f: 9 | lines = [l.rstrip('\n') for l in f] 10 | 11 | pic = list(lines[0]) 12 | image = pic[:25*6] 13 | 14 | fewest = 1000000000 15 | ott = 0 16 | while pic: 17 | layer = pic[:25*6] 18 | pic = pic[25*6:] 19 | 20 | # part 1 21 | zs = layer.count('0') 22 | if zs < fewest: 23 | fewest = zs 24 | ott = layer.count('1') * layer.count('2') 25 | 26 | # part 2 27 | s = '' 28 | for lc, ic in zip(layer, image): 29 | if ic == '2': 30 | s += lc 31 | else: 32 | s += ic 33 | image = s 34 | 35 | print(ott) 36 | for i in range(6): 37 | print(image[i*25:(i+1)*25].replace('0', ' ')) 38 | 39 | 40 | -------------------------------------------------------------------------------- /2019/day9.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import sortedcollections 7 | 8 | def tryprog(prog, name): 9 | pc = 0 10 | outputs = [] 11 | 12 | progd = collections.defaultdict(int) 13 | for i, v in enumerate(prog): 14 | progd[i] = v 15 | prog = progd 16 | 17 | relbase = 0 18 | 19 | arity = {99: 0, 1: 3, 2: 3, 3: 1, 4: 1, 5: 2, 6: 2, 7: 3, 8: 3, 9: 1} 20 | def parse(): 21 | nonlocal pc 22 | op = prog[pc] 23 | pc += 1 24 | vals = [] 25 | locs = [] 26 | for i in range(arity[op % 100]): 27 | mode = (op // (10 ** (2 + i))) % 10 28 | vals.append(prog[pc] if mode == 1 else prog[prog[pc] + relbase] if mode == 2 else prog[prog[pc]]) 29 | locs.append(None if mode == 1 else prog[pc] + relbase if mode == 2 else prog[pc]) 30 | pc += 1 31 | return op % 100, vals, locs 32 | 33 | while prog[pc] != 99: 34 | opc = pc 35 | op, vals, locs = parse() 36 | #print(name,opc,op) 37 | if op == 1: 38 | prog[locs[2]] = vals[0] + vals[1] 39 | elif op == 2: 40 | prog[locs[2]] = vals[0] * vals[1] 41 | elif op == 3: 42 | #print(pc, prog, inputs) 43 | prog[locs[0]] = 2 # 1 # yield 44 | #print(name, 'got', prog[locs[0]]) 45 | assert prog[locs[0]] is not None 46 | elif op == 4: 47 | #print('out:', vals[0]) 48 | #outputs.append(vals[0]) 49 | #print(name, 'returning', prog[locs[0]]) 50 | yield vals[0] 51 | elif op == 5: 52 | if vals[0] != 0: 53 | pc = vals[1] 54 | elif op == 6: 55 | if vals[0] == 0: 56 | pc = vals[1] 57 | elif op == 7: 58 | prog[locs[2]] = int(vals[0] < vals[1]) 59 | elif op == 8: 60 | prog[locs[2]] = int(vals[0] == vals[1]) 61 | elif op == 9: 62 | relbase += vals[0] 63 | else: 64 | assert False 65 | 66 | #print(name,'done') 67 | return prog[0], outputs[0] if outputs else None 68 | 69 | 70 | with open(sys.argv[1]) as f: 71 | lines = [l.rstrip('\n') for l in f] 72 | 73 | p = tryprog([int(c) for c in lines[0].split(',')], 'z') 74 | print(list(p)) 75 | 76 | 77 | -------------------------------------------------------------------------------- /2020/day01.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | with open(sys.argv[1]) as f: 7 | lines = [l.rstrip('\n') for l in f] 8 | lines = [[int(i) for i in re.findall(r'-?\d+', l)] for l in lines] 9 | 10 | nums = set(l[0] for l in lines) 11 | for num in nums: 12 | for num2 in nums: 13 | x = 2020 - num - num2 14 | if x in nums and len({x,num,num2})==3: 15 | print(num * num2 * (2020 - num - num2)) -------------------------------------------------------------------------------- /2020/day02.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | with open(sys.argv[1]) as f: 7 | lines = [l.rstrip('\n') for l in f] 8 | 9 | valid = 0 10 | for line in lines: 11 | match = re.fullmatch(r'(\d+)-(\d+) (.): (.+)', line) 12 | lo, hi, ch, word = match.groups() 13 | lo = int(lo) 14 | hi = int(hi) 15 | 16 | # if lo <= word.count(ch) <= hi: 17 | # valid += 1 18 | 19 | if (word[lo - 1] == ch) + (word[hi - 1] == ch) == 1: 20 | valid += 1 21 | print(valid) 22 | -------------------------------------------------------------------------------- /2020/day03.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | rows = len(lines) 9 | cols = len(lines[0]) 10 | 11 | def tryy(rs, cs): 12 | row = 0 13 | col = 0 14 | ct = 0 15 | while row < rows: 16 | row += rs 17 | col += cs 18 | if row >= rows: 19 | break 20 | if lines[row][col % cols] == '#': 21 | ct += 1 22 | return ct 23 | 24 | print( 25 | tryy(1, 1) 26 | * tryy(1, 3) 27 | * tryy(1, 5) 28 | * tryy(1, 7) 29 | * tryy(2, 1) 30 | ) 31 | -------------------------------------------------------------------------------- /2020/day04.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin.read().split('\n\n')] 7 | 8 | 9 | ALL = {'ecl', 'pid', 'eyr', 'hcl', 'byr', 'iyr', 'hgt'} 10 | 11 | valid = 0 12 | # part 1: 13 | # for line in lines: 14 | # chunks = re.findall(r'(\w+):', line) 15 | # if len(ALL - set(chunks)) == 0: 16 | # valid += 1 17 | for line in lines: 18 | chunks = re.findall(r'(\w+):(\S+)', line) 19 | 20 | if len(ALL - set(c[0] for c in chunks)) != 0: 21 | continue 22 | 23 | allvalid = True 24 | for typ, val in chunks: 25 | isv = False 26 | if typ == 'byr': 27 | isv = 1920 <= int(val) <= 2002 28 | elif typ == 'iyr': 29 | isv = 2010 <= int(val) <= 2020 30 | elif typ == 'eyr': 31 | isv = 2020 <= int(val) <= 2030 32 | elif typ == 'hgt': 33 | if val.endswith('cm'): 34 | isv = 150 <= int(val[:-2]) <= 193 35 | elif val.endswith('in'): 36 | isv = 59 <= int(val[:-2]) <= 76 37 | elif typ == 'hcl': 38 | isv = bool(re.fullmatch(r'#[0-9a-f]{6}', val)) 39 | elif typ == 'ecl': 40 | isv = val in 'amb blu brn gry grn hzl oth'.split() 41 | elif typ == 'pid': 42 | isv = bool(re.fullmatch(r'[0-9]{9}', val)) 43 | elif typ == 'cid': 44 | isv = True 45 | 46 | allvalid = allvalid and isv 47 | 48 | if allvalid: 49 | valid += 1 50 | 51 | print(valid) -------------------------------------------------------------------------------- /2020/day05.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | # part 1 in comments: 9 | # mm = 0 10 | allst = set() 11 | for line in lines: 12 | line = line.replace('F', '0') 13 | line = line.replace('B', '1') 14 | line = line.replace('L', '0') 15 | line = line.replace('R', '1') 16 | num = int(line, 2) 17 | # mm = max(num, mm) 18 | allst.add(num) 19 | 20 | # print(mm) 21 | for i in range(256 * 8): 22 | if i not in allst and i+1 in allst and i-1 in allst: 23 | print(i) 24 | -------------------------------------------------------------------------------- /2020/day06.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import string 3 | import math 4 | import re 5 | import sys 6 | 7 | lines = [l.rstrip('\n') for l in sys.stdin.read().split('\n\n')] 8 | 9 | ct = 0 10 | for line in lines: 11 | # part 1: 12 | # ct += len(set(c for c in line if 'a' <= c <= 'z')) 13 | alls = set(string.ascii_lowercase) 14 | for ll in line.split('\n'): 15 | alls &= set(c for c in ll if 'a' <= c <= 'z') 16 | ct += len(alls) 17 | print(ct) 18 | -------------------------------------------------------------------------------- /2020/day07.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | containedin = collections.defaultdict(set) 9 | contains = collections.defaultdict(list) 10 | for line in lines: 11 | color = re.match(r'(.+?) bags contain', line)[1] 12 | for ct, innercolor in re.findall(r'(\d+) (.+?) bags?[,.]', line): 13 | ct = int(ct) 14 | containedin[innercolor].add(color) 15 | contains[color].append((ct, innercolor)) 16 | 17 | # part 1: 18 | # holdsgold = set() 19 | # def check(color): 20 | # for c in containedin[color]: 21 | # holdsgold.add(c) 22 | # check(c) 23 | # check('shiny gold') 24 | # print(len(holdsgold)) 25 | 26 | def cost(color): 27 | total = 0 28 | for ct, inner in contains[color]: 29 | total += ct 30 | total += ct * cost(inner) 31 | return total 32 | print(cost('shiny gold')) 33 | -------------------------------------------------------------------------------- /2020/day08.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | # part 1: 9 | # acc = 0 10 | # pc = 0 11 | # seen = set() 12 | # while True: 13 | # if pc in seen: 14 | # print(acc) 15 | # break 16 | # seen.add(pc) 17 | # line = lines[pc] 18 | # inst, arg = line.split() 19 | # arg = int(arg) 20 | # 21 | # if inst == 'jmp': 22 | # pc += arg 23 | # continue 24 | # if inst == 'acc': 25 | # acc += arg 26 | # if inst == 'nop': 27 | # pass 28 | # 29 | # pc += 1 30 | 31 | def tryprog(prog): 32 | acc = 0 33 | pc = 0 34 | seen = set() 35 | while True: 36 | if pc == len(prog): 37 | return acc 38 | if pc in seen: 39 | return None 40 | seen.add(pc) 41 | line = prog[pc] 42 | inst, arg = line.split() 43 | arg = int(arg) 44 | 45 | if inst == 'jmp': 46 | pc += arg 47 | continue 48 | if inst == 'acc': 49 | acc += arg 50 | if inst == 'nop': 51 | pass 52 | 53 | pc += 1 54 | 55 | for i in range(len(lines)): 56 | prog = lines[:] 57 | if prog[i].startswith('jmp'): 58 | prog[i] = prog[i].replace('jmp', 'nop') 59 | elif prog[i].startswith('nop'): 60 | prog[i] = prog[i].replace('nop', 'jmp') 61 | x = tryprog(prog) 62 | if x: 63 | print(x) -------------------------------------------------------------------------------- /2020/day09.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | lines = [int(l) for l in lines] 8 | 9 | def issumoftwo(n, options): 10 | options = set(options) 11 | for o in options: 12 | if n-o != o and n-o in options: 13 | return True 14 | return False 15 | 16 | for i in range(25, len(lines)): 17 | if not issumoftwo(lines[i], lines[i-25:i]): 18 | goal = lines[i] 19 | print(goal) 20 | break 21 | 22 | partsum = [0] 23 | acc = 0 24 | for x in lines: 25 | acc += x 26 | partsum.append(acc) 27 | for i in range(len(partsum)): 28 | j = i + 2 29 | while 0 <= j < len(partsum) and partsum[j] - partsum[i] <= goal: 30 | if partsum[j] - partsum[i] == goal: 31 | print(max(lines[i:j]) + min(lines[i:j])) 32 | break 33 | j += 1 34 | -------------------------------------------------------------------------------- /2020/day10.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | lines = [int(l) for l in lines] 8 | 9 | # part 1: 10 | # lines.append(0) 11 | # lines.append(max(lines)+3) 12 | # lines.sort() 13 | # ones = 0 14 | # threes = 0 15 | # for a, b in zip(lines, lines[1:]): 16 | # if b-a == 1: 17 | # ones +=1 18 | # elif b-a == 3: 19 | # threes += 1 20 | # print(ones * threes) 21 | 22 | memo = {} 23 | def countways(adapters, start, goal): 24 | k = (len(adapters), start) 25 | if k in memo: 26 | return memo[k] 27 | ways = 0 28 | if goal - start <= 3: 29 | ways += 1 30 | if not adapters: 31 | return ways 32 | if adapters[0] - start <= 3: 33 | ways += countways(adapters[1:], adapters[0], goal) 34 | ways += countways(adapters[1:], start, goal) 35 | memo[k] = ways 36 | return ways 37 | 38 | print(countways(sorted(lines), 0, max(lines) + 3)) 39 | 40 | # alternate part 2 solution, written after the contest: 41 | # def countways2(nums): 42 | # top = max(nums) + 3 43 | # nums = set(nums) 44 | # nums.add(top) 45 | # a, b, c = 0, 0, 1 46 | # for i in range(1, top + 1): 47 | # if i in nums: 48 | # a, b, c = b, c, a + b + c 49 | # else: 50 | # a, b, c = b, c, 0 51 | # return c 52 | # 53 | # print(countways2(lines)) 54 | -------------------------------------------------------------------------------- /2020/day11.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | def step(grid): 9 | newgrid = [] 10 | for row in range(len(grid)): 11 | newrow = '' 12 | for col in range(len(grid[0])): 13 | adj = [] 14 | for x in (-1, 0, 1): 15 | for y in (-1, 0, 1): 16 | if x == y == 0: 17 | continue 18 | # part 1 in comments: 19 | # if 0 <= row+x < len(grid) and 0 <= col+y < len(grid[0]): 20 | # adj.append(grid[row+x][col+y]) 21 | i = 1 22 | while 0 <= row+i*x < len(grid) and 0 <= col+i*y < len(grid[0]): 23 | ch = grid[row+i*x][col+i*y] 24 | if ch != '.': 25 | adj.append(ch) 26 | break 27 | i += 1 28 | if grid[row][col] == 'L' and '#' not in adj: 29 | newrow += '#' 30 | # elif grid[row][col] == '#' and adj.count('#') >= 4: 31 | elif grid[row][col] == '#' and adj.count('#') >= 5: 32 | newrow += 'L' 33 | else: 34 | newrow += grid[row][col] 35 | newgrid.append(newrow) 36 | return newgrid 37 | 38 | grid = lines 39 | while True: 40 | after = step(grid) 41 | if after == grid: 42 | print(''.join(grid).count('#')) 43 | break 44 | grid = after 45 | -------------------------------------------------------------------------------- /2020/day12.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | x, y = 0, 0 9 | wx, wy = 10, 1 10 | 11 | # part 1: 12 | # dir = 0 13 | # for line in lines: 14 | # action = line[:1] 15 | # arg = int(line[1:]) 16 | # if action == 'F': 17 | # if dir % 360 == 0: 18 | # action = 'E' 19 | # if dir % 360 == 90: 20 | # action = 'N' 21 | # if dir % 360 == 180: 22 | # action = 'W' 23 | # if dir % 360 == 270: 24 | # action = 'S' 25 | # if action == 'N': 26 | # y += arg 27 | # if action == 'E': 28 | # x += arg 29 | # if action == 'S': 30 | # y -= arg 31 | # if action == 'W': 32 | # x -= arg 33 | # if action == 'L': 34 | # dir += arg 35 | # if action == 'R': 36 | # dir -= arg 37 | # assert action != 'F' 38 | 39 | for line in lines: 40 | action = line[:1] 41 | arg = int(line[1:]) 42 | if action == 'F': 43 | x += wx * arg 44 | y += wy * arg 45 | if action == 'N': 46 | wy += arg 47 | if action == 'E': 48 | wx += arg 49 | if action == 'S': 50 | wy -= arg 51 | if action == 'W': 52 | wx -= arg 53 | if action == 'L': 54 | while arg: 55 | wx, wy = -wy, wx 56 | arg -= 90 57 | if action == 'R': 58 | while arg: 59 | wx, wy = wy, -wx 60 | arg -= 90 61 | 62 | print(abs(x) + abs(y)) 63 | -------------------------------------------------------------------------------- /2020/day13.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | def crt(pairs): 9 | M = 1 10 | for x, mx in pairs: 11 | M *= mx 12 | total = 0 13 | for x, mx in pairs: 14 | b = M // mx 15 | total += x * b * pow(b, mx-2, mx) 16 | total %= M 17 | return total 18 | 19 | 20 | start = int(lines[0]) 21 | pairs = [] 22 | for i, n in enumerate(lines[1].split(',')): 23 | if n == 'x': 24 | continue 25 | n = int(n) 26 | pairs.append((n - i, n)) 27 | print(crt(pairs)) 28 | -------------------------------------------------------------------------------- /2020/day14.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | def domask(arg, mask): 9 | arg |= int(mask.replace('X', '0'), 2) 10 | arg &= int(mask.replace('X', '1'), 2) 11 | return arg 12 | 13 | def allmasks(pos, mask): 14 | if not mask: 15 | yield 0 16 | else: 17 | # (yes, I probably could have done the ifs *inside* the loop...) 18 | if mask[-1] == '0': 19 | for m in allmasks(pos // 2, mask[:-1]): 20 | yield 2*m + pos % 2 21 | if mask[-1] == '1': 22 | for m in allmasks(pos // 2, mask[:-1]): 23 | yield 2*m + 1 24 | if mask[-1] == 'X': 25 | for m in allmasks(pos // 2, mask[:-1]): 26 | yield 2*m + 0 27 | yield 2*m + 1 28 | 29 | # part 2 alternate solution, written later: 30 | # def allmasks(mask): 31 | # if not mask: 32 | # yield '' 33 | # return 34 | # for m in allmasks(mask[1:]): 35 | # if mask[0] == '0': 36 | # yield 'X' + m # leave unchanged 37 | # elif mask[0] == '1': 38 | # yield '1' + m # replace with 1 39 | # elif mask[0] == 'X': 40 | # yield '0' + m # replace with 0 41 | # yield '1' + m # replace with 1 42 | 43 | 44 | mask = None 45 | mem = collections.defaultdict(int) 46 | for line in lines: 47 | op, arg = line.split(' = ') 48 | if op == 'mask': 49 | mask = arg 50 | else: 51 | pos = int(op[4:-1]) 52 | # part 1: 53 | # mem[pos] = domask(int(arg), mask) 54 | for m in allmasks(pos, mask): 55 | mem[m] = int(arg) 56 | # part 2 alternate solution: 57 | # for m in allmasks(mask): 58 | # mem[domask(pos, m)] = int(arg) 59 | 60 | print(sum(mem.values())) 61 | -------------------------------------------------------------------------------- /2020/day15.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | lines = [int(l) for l in lines[0].split(',')] 8 | 9 | spoken = collections.defaultdict(list) 10 | last = None 11 | spkc = 1 12 | for line in lines: 13 | spoken[line].append(spkc) 14 | last = line 15 | spkc += 1 16 | while spkc <= 30000000: 17 | if len(spoken[last]) > 1: 18 | last = spoken[last][-1] - spoken[last][-2] 19 | else: 20 | last = 0 21 | spoken[last].append(spkc) 22 | if spkc % 1000000 == 0: 23 | print(spkc,last) 24 | spkc += 1 25 | print(last) 26 | -------------------------------------------------------------------------------- /2020/day16.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | ranges, your, neighbor = [l.rstrip('\n') for l in sys.stdin.read().split('\n\n')] 7 | 8 | rp = [] 9 | rpd = collections.defaultdict(list) 10 | for line in ranges.splitlines(): 11 | kind = line.split(': ')[0] 12 | for a, b in re.findall(r'(\d+)-(\d+)', line): 13 | rp.append((int(a),int(b),kind)) 14 | rpd[kind].append((int(a), int(b))) 15 | 16 | fail = 0 17 | valtix = [] 18 | for line in neighbor.splitlines()[1:]: 19 | nums = [int(l) for l in line.split(',')] 20 | anyfail = False 21 | for num in nums: 22 | if any(a <= num <= b for a, b, _ in rp): 23 | pass 24 | else: 25 | fail += num 26 | anyfail = True 27 | if not anyfail: 28 | valtix.append(nums) 29 | print(fail) 30 | 31 | yourp = [int(l) for l in your.splitlines()[1].split(',')] 32 | valtix.append(yourp) 33 | 34 | overunder = collections.defaultdict(list) 35 | numf = len(valtix[0]) 36 | for f in range(len(valtix[0])): 37 | overunder[f] = [t[f] for t in valtix] 38 | 39 | memo = {} 40 | def canmatch(fname, im): 41 | k = (fname, im) 42 | if k in memo: 43 | return memo[k] 44 | cm = all(any(a <= v <= b for a, b in rpd[fname]) for v in overunder[im]) 45 | memo[k] = cm 46 | return cm 47 | 48 | assign = {} 49 | assignb = {} 50 | while len(rpd) > len(assign): 51 | progress = False 52 | for im in range(numf): 53 | if im in assignb.keys(): 54 | continue 55 | t = [] 56 | for fname in rpd.keys(): 57 | if fname in assign.keys(): 58 | continue 59 | if canmatch(fname, im): 60 | t.append(fname) 61 | if len(t) == 1: 62 | assign[t[0]] = im 63 | assignb[im] = t[0] 64 | # print(im, 'is', t) 65 | 66 | 67 | prod = 1 68 | for fname, im in assign.items(): 69 | if fname.startswith('departure'): 70 | print(fname, yourp[im]) 71 | prod *= yourp[im] 72 | print(prod) 73 | 74 | 75 | -------------------------------------------------------------------------------- /2020/day17.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | def step(grid): 9 | ng = {} 10 | for x in range(min(k[0] for k in grid.keys())-1, max(k[0] for k in grid.keys())+2): 11 | for y in range(min(k[1] for k in grid.keys())-1, max(k[1] for k in grid.keys())+2): 12 | for z in range(min(k[2] for k in grid.keys())-1, max(k[2] for k in grid.keys())+2): 13 | for q in range(min(k[3] for k in grid.keys())-1, max(k[3] for k in grid.keys())+2): 14 | s = grid.get((x,y,z,q), False) 15 | an = 0 16 | for dx in (-1, 0, 1): 17 | for dy in (-1, 0, 1): 18 | for dz in (-1, 0, 1): 19 | for dq in (-1, 0, 1): 20 | if dx == dy == dz == dq == 0: 21 | continue 22 | if grid.get((x+dx,y+dy,z+dz,q+dq),False): 23 | an += 1 24 | if (s and an in (2, 3)) or (not s and an == 3): 25 | ng[(x,y,z,q)] = True 26 | return ng 27 | 28 | grid = {} 29 | for row, line in enumerate(lines): 30 | for col, ch in enumerate(line): 31 | grid[(row, col, 0, 0)] = ch == '#' 32 | 33 | 34 | for i in range(6): 35 | print(i, sum(grid.values())) 36 | grid = step(grid) 37 | print(sum(grid.values())) 38 | -------------------------------------------------------------------------------- /2020/day18.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | def solve(line): 9 | def doInner(inner): 10 | # part 1: 11 | # while '+' in inner or '*' in inner: 12 | # inner = re.sub('^(\d+)\s*\+\s*(\d+)', lambda m: str(int(m.group(1)) + int(m.group(2))), inner) 13 | # inner = re.sub('^(\d+)\s*\*\s*(\d+)', lambda m: str(int(m.group(1)) * int(m.group(2))), inner) 14 | while '+' in inner: 15 | inner = re.sub('(\d+)\s*\+\s*(\d+)', lambda m: str(int(m.group(1)) + int(m.group(2))), inner) 16 | while '*' in inner: 17 | inner = re.sub('(\d+)\s*\*\s*(\d+)', lambda m: str(int(m.group(1)) * int(m.group(2))), inner) 18 | return inner 19 | while '(' in line: 20 | def doExpr(match): 21 | inner = match.group(1) 22 | return doInner(inner) 23 | line = re.sub(r'\(([^()]+)\)', doExpr, line) 24 | return doInner(line) 25 | 26 | total = 0 27 | for line in lines: 28 | total += int(solve(line)) 29 | 30 | print(total) -------------------------------------------------------------------------------- /2020/day19.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | rules, strings = [l.rstrip('\n') for l in sys.stdin.read().split('\n\n')] 7 | 8 | rules = dict([rule.split(': ', 1) for rule in rules.split('\n')]) 9 | def getre(rulenum): 10 | # for part 1, delete these two rules: 11 | if rulenum == '8': 12 | return getre('42') + '+' 13 | elif rulenum == '11': 14 | a = getre('42') 15 | b = getre('31') 16 | return '(?:' + '|'.join(f'{a}{{{n}}}{b}{{{n}}}' for n in range(1, 100)) + ')' 17 | 18 | rule = rules[rulenum] 19 | if re.fullmatch(r'"."', rule): 20 | return rule[1] 21 | else: 22 | parts = rule.split(' | ') 23 | res = [] 24 | for part in parts: 25 | nums = part.split(' ') 26 | res.append(''.join(getre(num) for num in nums)) 27 | return '(?:' + '|'.join(res) + ')' 28 | 29 | 30 | z = getre('0') 31 | ct = 0 32 | for string in strings.split('\n'): 33 | ct += bool(re.fullmatch(z, string)) 34 | print(ct) 35 | -------------------------------------------------------------------------------- /2020/day20.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import itertools 3 | import math 4 | import re 5 | import sys 6 | 7 | tiles = [l.rstrip('\n') for l in sys.stdin.read().split('\n\n')] 8 | 9 | def edges(lines): 10 | lines = lines.split('\n') 11 | return [''.join(l[0] for l in lines)[::-1], lines[0], ''.join(l[-1] for l in lines), lines[-1][::-1]] 12 | 13 | def flip(lines): 14 | lines = lines.split('\n') 15 | return '\n'.join(l[::-1] for l in lines) 16 | 17 | def rot(lines): 18 | lines = lines.split('\n') 19 | s = [] 20 | for row in range(len(lines)): 21 | s.append(''.join(l[-1-row] for l in lines)) 22 | return '\n'.join(s) 23 | 24 | edgecount = collections.defaultdict(int) 25 | edgetotile = collections.defaultdict(list) 26 | tdict = {} 27 | for tile in tiles: 28 | head, lines = tile.split('\n', 1) 29 | tnum = int(head[5:-1]) 30 | tdict[tnum] = lines 31 | 32 | ee = edges(lines) 33 | for e in ee: 34 | e = min(e, e[::-1]) 35 | edgecount[e] += 1 36 | edgetotile[e].append(tnum) 37 | 38 | 39 | used = set() 40 | 41 | assembly = [[]] 42 | 43 | def go(): 44 | p = 1 45 | for tile in tiles: 46 | head, lines = tile.split('\n', 1) 47 | tnum = int(head[5:-1]) 48 | 49 | uq = 0 50 | for e in edges(lines): 51 | e = min(e, e[::-1]) 52 | if edgecount[e] == 1: 53 | uq += 1 54 | if uq == 2: 55 | # print(tnum) 56 | p *= tnum 57 | 58 | # for part 1, remove the following code: 59 | ll = lines 60 | for _ in range(4): 61 | e = edges(ll) 62 | if edgecount[min(e[0], e[0][::-1])] == 1 and edgecount[min(e[1], e[1][::-1])] == 1: 63 | assembly[0].append(ll) 64 | used.add(tnum) 65 | return 66 | ll = flip(ll) 67 | if edgecount[min(e[0], e[0][::-1])] == 1 and edgecount[min(e[1], e[1][::-1])] == 1: 68 | assembly[0].append(ll) 69 | used.add(tnum) 70 | return 71 | ll = flip(ll) 72 | ll = rot(ll) 73 | assert False 74 | # print(p) 75 | go() 76 | 77 | def k(edge): 78 | return min(edge, edge[::-1]) 79 | 80 | # fill first row 81 | while True: 82 | tile = assembly[0][-1] 83 | edge = edges(tile)[2][::-1] 84 | nextt = next((tnum for tnum in edgetotile[k(edge)] if tnum not in used), None) 85 | if not nextt: 86 | # end of row 87 | break 88 | ll = tdict[nextt] 89 | for _ in range(4): 90 | if edges(ll)[0] == edge: 91 | break 92 | ll = flip(ll) 93 | if edges(ll)[0] == edge: 94 | break 95 | ll = flip(ll) 96 | ll = rot(ll) 97 | else: 98 | assert False 99 | assembly[0].append(ll) 100 | used.add(nextt) 101 | 102 | def printrow(row): 103 | for chunks in itertools.zip_longest(*(r.split('\n') for r in row), fillvalue=' '*8): 104 | print(' '.join(chunks)) 105 | print() 106 | 107 | def join(assembly): 108 | rows = collections.defaultdict(str) 109 | for mr, row in enumerate(assembly): 110 | for chunk in row: 111 | chunklines = chunk.split('\n') 112 | for r, subrow in enumerate(chunklines): 113 | if r not in (0, len(chunklines) - 1): 114 | rows[100*mr + r] += subrow[1:-1] 115 | return '\n'.join(v for k, v in sorted(rows.items())) 116 | 117 | 118 | # fill rest of rows 119 | while len(used) < len(tdict): 120 | newrow = [] 121 | for tile in assembly[-1]: 122 | edge = edges(tile)[3][::-1] 123 | nextt = next((tnum for tnum in edgetotile[k(edge)] if tnum not in used), None) 124 | if not nextt: 125 | # end of row 126 | break 127 | ll = tdict[nextt] 128 | for _ in range(4): 129 | if edges(ll)[1] == edge: 130 | break 131 | ll = flip(ll) 132 | if edges(ll)[1] == edge: 133 | break 134 | ll = flip(ll) 135 | ll = rot(ll) 136 | else: 137 | assert False 138 | newrow.append(ll) 139 | used.add(nextt) 140 | assert len(newrow) == len(assembly[-1]) 141 | assembly.append(newrow) 142 | 143 | seamonster = '''\ 144 | # \n\ 145 | # ## ## ### 146 | # # # # # # '''.split('\n') 147 | 148 | joined = join(assembly) 149 | 150 | def look(joined): 151 | fc = 0 152 | for row in range(len(joined)): 153 | for col in range(len(joined[0])): 154 | found = True 155 | for r in range(len(seamonster)): 156 | # print('r is', r) 157 | for c in range(len(seamonster[0])): 158 | # print('c is', c, repr(seamonster[r][c])) 159 | if seamonster[r][c] != '#': 160 | continue 161 | try: 162 | # print('sm', r, c, joined[row+r][col+c]) 163 | if joined[row+r][col+c] != '#': 164 | found = False 165 | except IndexError: 166 | found = False 167 | pass 168 | if found: 169 | # print('found at', row, col) 170 | fc += 1 171 | return fc 172 | 173 | jj = joined 174 | for _ in range(4): 175 | k = look(jj.split('\n')) 176 | if k: 177 | break 178 | jj = flip(jj) 179 | k = look(jj.split('\n')) 180 | if k: 181 | break 182 | jj = flip(jj) 183 | jj = rot(jj) 184 | 185 | 186 | print(jj) 187 | print(jj.count('#') - ''.join(seamonster).count('#') * k) -------------------------------------------------------------------------------- /2020/day21.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | import z3 7 | 8 | lines = [l.rstrip('\n') for l in sys.stdin] 9 | 10 | allwhere = collections.defaultdict(set) 11 | ingwhere = collections.defaultdict(set) 12 | 13 | for i, line in enumerate(lines): 14 | ingredients, allergens = line.split('contains', 1) 15 | ingredients = re.findall(r'\w+', ingredients) 16 | allergens = re.findall(r'\w+', allergens) 17 | 18 | for ingredient in ingredients: 19 | ingwhere[ingredient].add(i) 20 | for allergen in allergens: 21 | allwhere[allergen].add(i) 22 | 23 | inert = set() 24 | ct = 0 25 | for ing, vals2 in ingwhere.items(): 26 | canallerg = False 27 | for allergen, vals in allwhere.items(): 28 | if vals < vals2: 29 | canallerg = True 30 | if not canallerg: 31 | inert.add(ing) 32 | ct += len(ingwhere[ing]) 33 | print(ct) 34 | 35 | possing = list(set(ingwhere.keys()) - inert) 36 | possall = list(set(allwhere.keys())) 37 | assert len(possing) == len(possall) 38 | 39 | assignments = z3.IntVector('allergen', len(possall)) 40 | solver = z3.Solver() 41 | for a in assignments: 42 | solver.add(0 <= a) 43 | solver.add(a < len(possall)) 44 | solver.add(z3.Distinct(assignments)) 45 | for ai, allergen in enumerate(possall): 46 | conds = [] 47 | for ii, ing in enumerate(possing): 48 | if ingwhere[ing] >= allwhere[allergen]: 49 | conds.append(assignments[ii] == ai) 50 | solver.add(z3.Or(conds)) 51 | assert solver.check() == z3.sat 52 | m = solver.model() 53 | matches = [] 54 | for ii, a in enumerate(assignments): 55 | matches.append((possall[m.evaluate(assignments[ii]).as_long()], possing[ii])) 56 | 57 | matches.sort() 58 | print(','.join(m[1] for m in matches)) 59 | 60 | -------------------------------------------------------------------------------- /2020/day22.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | decks = [l.rstrip('\n') for l in sys.stdin.read().split('\n\n')] 7 | decks = [[int(i) for i in re.findall(r'(?<=\n)(\d+)', l)] for l in decks] 8 | 9 | a, b = decks 10 | a = collections.deque(a) 11 | b = collections.deque(b) 12 | 13 | # part 1: 14 | # while a and b: 15 | # aa = a.popleft() 16 | # bb = b.popleft() 17 | # if aa > bb: 18 | # winner = a 19 | # elif bb > aa: 20 | # winner = b 21 | # else: 22 | # assert False, aa 23 | # winner.append(max(aa, bb)) 24 | # winner.append(min(aa, bb)) 25 | # winner = a or b 26 | 27 | def combat(a, b): 28 | seen = set() 29 | while a and b: 30 | kk = (tuple(a), tuple(b)) 31 | if kk in seen: 32 | return 1, a 33 | seen.add(kk) 34 | aa = a.popleft() 35 | bb = b.popleft() 36 | if len(a) >= aa and len(b) >= bb: 37 | winnum, _ = combat( 38 | collections.deque(list(a)[:aa]), 39 | collections.deque(list(b)[:bb]), 40 | ) 41 | winner = a if winnum == 1 else b 42 | else: 43 | if aa > bb: 44 | winner = a 45 | elif bb > aa: 46 | winner = b 47 | else: 48 | assert False, aa 49 | winner.append(aa if winner is a else bb) 50 | winner.append(aa if winner is not a else bb) 51 | return 1 if winner is a else 2, winner 52 | winnum, winner = combat(a, b) 53 | 54 | print(sum(i * v for i, v in enumerate(reversed(winner), 1))) 55 | -------------------------------------------------------------------------------- /2020/day23.py: -------------------------------------------------------------------------------- 1 | import collections 2 | from fractions import Fraction 3 | import math 4 | import re 5 | import sys 6 | 7 | line = [int(i) for i in [l.rstrip('\n') for l in sys.stdin][0]] 8 | 9 | # part 1 (rearrange the final answer by hand): 10 | # def step(line): 11 | # three = line[1:4] 12 | # dest = int(line[0]) - 1 13 | # if dest == 0: 14 | # dest = int(max(line)) 15 | # while dest in three: 16 | # dest -= 1 17 | # if dest == 0: 18 | # dest = int(max(line)) 19 | # idx = line.index(dest) 20 | # a, b = line[:idx], line[idx+1:] 21 | # return a[4:] + [dest] + three + b + [line[0]] 22 | # for _ in range(100): 23 | # line = step(line) 24 | # print(line) 25 | 26 | 27 | class Node: 28 | __slots__ = ('value', 'next') 29 | def __init__(self, value): 30 | self.value = value 31 | self.next = None 32 | 33 | def run(line, steps=100): 34 | nodes = {num: Node(num) for num in line} 35 | for num, num2 in zip(line, line[1:] + line[:1]): 36 | nodes[num].next = nodes[num2] 37 | front = nodes[line[0]] 38 | for stepno in range(steps): 39 | a = front.next 40 | b = a.next 41 | c = b.next 42 | 43 | look = front.value 44 | while True: 45 | look -= 1 46 | if look == 0: 47 | look = len(line) 48 | if look not in (a.value, b.value, c.value): 49 | break 50 | 51 | after = nodes[look] 52 | front.next, front, after.next, c.next = c.next, c.next, a, after.next 53 | 54 | if stepno % 500000 == 0: 55 | print(stepno) 56 | 57 | print(nodes[1].next.value * nodes[1].next.next.value) 58 | 59 | line.extend(range(10, 10 ** 6 + 1)) 60 | run(line, 10 ** 7) 61 | -------------------------------------------------------------------------------- /2020/day24.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | 8 | # nw ne 9 | # w . e 10 | # sw se 11 | 12 | flips = collections.defaultdict(int) 13 | for line in lines: 14 | dirs = re.findall('e|se|sw|w|nw|ne', line) 15 | x, y = 0, 0 16 | for d in dirs: 17 | ch = { 18 | 'e': (1, 0), 19 | 'se': (0, -1), 20 | 'sw': (-1, -1), 21 | 'w': (-1, 0), 22 | 'nw': (0, 1), 23 | 'ne': (1, 1), 24 | }[d] 25 | x += ch[0] 26 | y += ch[1] 27 | flips[(x,y)] += 1 28 | 29 | print(sum(1 for k, v in flips.items() if v % 2 == 1)) 30 | 31 | def step(flips): 32 | adjblack = collections.defaultdict(int) 33 | for k, v in flips.items(): 34 | if v % 2 == 0: 35 | continue 36 | for adj in [ 37 | (1, 0), 38 | (0, -1), 39 | (-1, -1), 40 | (-1, 0), 41 | (0, 1), 42 | (1, 1), 43 | ]: 44 | pt = (k[0] + adj[0], k[1] + adj[1]) 45 | adjblack[pt] += 1 46 | 47 | newflips = {} 48 | for k, v in flips.items(): 49 | if v % 2 == 1: 50 | if adjblack.get(k, 0) not in (1, 2): 51 | # flip to white 52 | pass 53 | else: 54 | newflips[k] = 1 55 | for k, v in adjblack.items(): 56 | if v == 2 and flips.get(k, 0) % 2 == 0: 57 | newflips[k] = 1 58 | return newflips 59 | 60 | for i in range(100): 61 | flips = step(flips) 62 | print(i+1, sum(1 for k, v in flips.items() if v % 2 == 1)) -------------------------------------------------------------------------------- /2020/day25.py: -------------------------------------------------------------------------------- 1 | import collections 2 | import math 3 | import re 4 | import sys 5 | 6 | lines = [l.rstrip('\n') for l in sys.stdin] 7 | a, b = [int(i) for i in lines] 8 | 9 | def root(a): 10 | for i in range(100000000): 11 | if pow(7, i, 20201227) == a: 12 | return i 13 | 14 | print(pow(a, root(b), 20201227)) 15 | print(pow(b, root(a), 20201227)) 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advent of Code Solutions 2 | 3 | My solutions to [Advent of Code](https://adventofcode.com/). 4 | 5 | You can find some discussion about these solutions within [my comments](https://www.reddit.com/user/sophiebits/comments/) on the /r/adventofcode subreddit. 6 | --------------------------------------------------------------------------------