├── 2017 ├── day01 │ └── day01.py ├── day04 │ └── day04.py ├── day02 │ └── day02.py ├── day05 │ └── day05.py ├── day06 │ └── day06.py ├── day08 │ └── day08.py ├── day09 │ └── day09.py ├── day12 │ └── day12.py ├── day17 │ └── day17.py ├── day07 │ └── day07.py ├── day13 │ └── day13.py ├── day19 │ └── day19.py ├── day11 │ └── day11.py ├── day03 │ └── day03.py ├── day10 │ └── day10.py ├── day25 │ └── day25.py ├── day15 │ └── day15.py ├── day22 │ └── day22.py ├── day14 │ └── day14.py ├── day20 │ └── day20.py ├── day16 │ └── day16.py ├── day24 │ └── day24.py ├── day23 │ └── day23.py └── day21 │ └── day21.py ├── 2018 ├── day-22 │ ├── input.txt │ └── day_22.py ├── day-09 │ ├── input.txt │ └── Day9.java ├── day-18 │ ├── samp.txt │ ├── day_18.py │ └── input.txt ├── day-01 │ └── day_1.py ├── day-02 │ └── day_2.py ├── day-05 │ └── day_5.py ├── day-21 │ ├── input.txt │ └── day_21.py ├── day-19 │ ├── input.txt │ └── day_19.py ├── day-12 │ ├── input.txt │ └── day_12.py ├── day-03 │ └── day_3.py ├── day-06 │ ├── input.txt │ └── day_6.py ├── day-08 │ └── day_8.py ├── day-23 │ └── day_23.py ├── day-11 │ └── day_11.py ├── day-15 │ └── input.txt ├── day-10 │ └── day_10.py ├── day-04 │ └── day_4.py ├── day-07 │ └── day_7.py ├── day-17 │ └── day_17.py ├── day-14 │ └── Day14.java ├── day-24 │ └── input.txt ├── day-13 │ └── day_13.py ├── day-16 │ └── day_16.py └── day-20 │ └── day_20.py ├── 2020 ├── day24 │ ├── aaa.png │ ├── vis.mp4 │ ├── viz.mp4 │ ├── part1gif.gif │ ├── day24.py │ ├── nirmit_day24.py │ └── day24_clean.py ├── day16 │ ├── ex.txt │ └── day16.py ├── day25 │ └── day25.py ├── day3 │ └── day3.py ├── day5 │ └── day5.py ├── template.py ├── day6 │ └── day6.py ├── day2 │ └── day2.py ├── aoc_lib.py ├── day10 │ ├── day10.py │ └── day10_alt.py ├── day1 │ └── day1.py ├── day15 │ └── day15.py ├── day9 │ └── day9.py ├── day7 │ └── day7.py ├── day18 │ ├── day18_alt.py │ └── day18.py ├── day13 │ └── day13.py ├── day8 │ ├── day8.py │ └── alt_day8.py ├── day12 │ └── day12.py ├── day4 │ └── day4.py ├── day20 │ └── ex.txt ├── day21 │ └── day21.py ├── day23 │ ├── day23_opt.py │ └── day23.py ├── day22 │ └── day22.py ├── day14 │ └── day14.py ├── day19 │ └── day19.py └── day11 │ └── day11.py ├── 2021 ├── day01 │ └── day01.py ├── day07 │ └── day07.py ├── day02 │ └── day02.py ├── day17 │ └── day17.py ├── day06 │ └── day06.py ├── day08 │ └── day08.py ├── day05 │ └── day05.py ├── day13 │ └── day13.py ├── day12 │ └── day12.py ├── day14 │ └── day14.py ├── day04 │ └── day04.py ├── day03 │ └── day03.py ├── day09 │ └── day09.py ├── day15 │ └── day15.py ├── day11 │ └── day11.py └── day16 │ └── day16.py ├── 2022 ├── day19 │ └── Solve.java ├── day01 │ ├── day01.py │ └── aoc_tools.py ├── day03 │ ├── day03.py │ └── aoc_tools.py ├── day15 │ ├── day15_p1.py │ ├── day15_p2.py │ ├── day15.py │ └── aoc_tools.py ├── day04 │ ├── day04.py │ └── aoc_tools.py ├── day02 │ ├── day02.py │ └── aoc_tools.py ├── day25 │ └── day25.py ├── day09 │ ├── day09_p2.py │ ├── day09.py │ └── aoc_tools.py ├── day07 │ ├── day07.py │ └── aoc_tools.py ├── day05 │ ├── day05.py │ ├── day05_clean.py │ └── aoc_tools.py ├── day06 │ ├── day06.py │ └── aoc_tools.py ├── day08 │ ├── day08.py │ └── aoc_tools.py ├── day12 │ ├── day12.py │ ├── day12_orig.py │ └── aoc_tools.py ├── day14 │ └── day14.py ├── day11 │ ├── day11.py │ └── aoc_tools.py ├── day13 │ ├── day13.py │ └── aoc_tools.py ├── day20 │ └── day20.py ├── day10 │ ├── day10.py │ └── aoc_tools.py ├── day18 │ └── day18.py └── day24 │ └── day24.py └── .gitignore /2018/day-22/input.txt: -------------------------------------------------------------------------------- 1 | depth: 11991 2 | target: 6,797 3 | -------------------------------------------------------------------------------- /2018/day-09/input.txt: -------------------------------------------------------------------------------- 1 | 425 players; last marble is worth 70848 points 2 | -------------------------------------------------------------------------------- /2020/day24/aaa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nthistle/advent-of-code/HEAD/2020/day24/aaa.png -------------------------------------------------------------------------------- /2020/day24/vis.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nthistle/advent-of-code/HEAD/2020/day24/vis.mp4 -------------------------------------------------------------------------------- /2020/day24/viz.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nthistle/advent-of-code/HEAD/2020/day24/viz.mp4 -------------------------------------------------------------------------------- /2020/day24/part1gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nthistle/advent-of-code/HEAD/2020/day24/part1gif.gif -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # Advent of Code input files 7 | input.txt -------------------------------------------------------------------------------- /2018/day-18/samp.txt: -------------------------------------------------------------------------------- 1 | .#.#...|#. 2 | .....#|##| 3 | .|..|...#. 4 | ..|#.....# 5 | #.#|||#|#| 6 | ...#.||... 7 | .|....|... 8 | ||...#|.#| 9 | |.||||..|. 10 | ...#.|..|. -------------------------------------------------------------------------------- /2020/day16/ex.txt: -------------------------------------------------------------------------------- 1 | class: 0-1 or 4-19 2 | row: 0-5 or 8-19 3 | seat: 0-13 or 16-19 4 | 5 | your ticket: 6 | 11,12,13 7 | 8 | nearby tickets: 9 | 3,9,18 10 | 15,1,5 11 | 5,14,9 -------------------------------------------------------------------------------- /2022/day19/Solve.java: -------------------------------------------------------------------------------- 1 | import java.util.*; 2 | 3 | public static class Solve { 4 | public static Hash 5 | public static void main(String[] args) { 6 | 7 | } 8 | 9 | public stat 10 | } -------------------------------------------------------------------------------- /2017/day01/day01.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip() 3 | 4 | print(sum(int(s[i]) for i in range(len(s)) if s[i] == s[i-1])) 5 | 6 | n = len(s) 7 | 8 | print(sum(int(s[i]) for i in range(len(s)) if s[i] == s[(i+n//2)%n])) 9 | -------------------------------------------------------------------------------- /2017/day04/day04.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | print(sum(1 for p in s if p.count(" ")+1 == len(set(p.split(" "))))) 5 | 6 | print(sum(1 for p in s if p.count(" ")+1 == \ 7 | len(set("".join(sorted(pp)) for pp in p.split(" "))))) 8 | -------------------------------------------------------------------------------- /2020/day25/day25.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip("\n") 3 | 4 | s = s.split("\n") 5 | 6 | pkc,pkd = map(int,s) 7 | 8 | mod = 20201227 9 | 10 | loopc = 0 11 | val = 1 12 | while val != pkc: 13 | val = (7 * val) % mod 14 | loopc += 1 15 | 16 | print(pow(pkd,loopc,mod)) 17 | 18 | -------------------------------------------------------------------------------- /2021/day01/day01.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | with open("input.txt") as f: 4 | s = f.read() 5 | 6 | a = [int(x) for x in s.strip().split("\n")] 7 | 8 | print(sum(1 for x,y in zip(a,a[1:]) if y > x)) 9 | 10 | sliding = [x+y+z for x,y,z in zip(a,a[1:],a[2:])] 11 | 12 | print(sum(1 for x,y in zip(sliding,sliding[1:]) if y > x)) 13 | -------------------------------------------------------------------------------- /2022/day01/day01.py: -------------------------------------------------------------------------------- 1 | from aoc_tools import * 2 | 3 | with open("input.txt") as f: 4 | s = f.read() 5 | 6 | s = s.strip().split("\n\n") 7 | 8 | s = [[int(x) for x in y.split("\n")] for y in s] 9 | 10 | print(max(sum(z) for z in s)) 11 | 12 | s.sort(key = lambda x : sum(x), reverse=True) 13 | 14 | print(sum(sum(z) for z in s[:3])) 15 | -------------------------------------------------------------------------------- /2018/day-01/day_1.py: -------------------------------------------------------------------------------- 1 | 2 | with open("input.txt") as file: 3 | inp = file.read().strip() 4 | 5 | inp = [int(x) for x in inp.split("\n")] 6 | print("Part 1:",sum(inp)) 7 | 8 | reached = set() 9 | rsum = 0 10 | 11 | while rsum not in reached: 12 | for val in inp: 13 | reached.add(rsum) 14 | rsum += val 15 | if rsum in reached: 16 | print("Part 2:",rsum) 17 | break 18 | -------------------------------------------------------------------------------- /2020/day3/day3.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | s = [list(x) for x in s] 5 | 6 | def ans(xd,yd): 7 | y,x = 0,0 8 | c = 0 9 | while y < len(s): 10 | if s[y][x%len(s[y])] == "#": 11 | c += 1 12 | x += xd 13 | y += yd 14 | return c 15 | 16 | print(ans(3,1)) 17 | 18 | print(ans(1,1)*ans(3,1)*ans(5,1)*ans(7,1)*ans(1,2)) 19 | -------------------------------------------------------------------------------- /2020/day5/day5.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | sids = [] 5 | for ln in s: 6 | a,b = ln[:7],ln[7:] 7 | a = int(a.replace("F","0").replace("B","1"),2) 8 | b = int(b.replace("L","0").replace("R","1"),2) 9 | sid = 8*a + b 10 | sids.append(sid) 11 | 12 | print(max(sids)) 13 | 14 | sids.sort() 15 | m = min(sids) 16 | while m in sids: 17 | m += 1 18 | print(m) 19 | -------------------------------------------------------------------------------- /2020/template.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip().split("\n") 18 | -------------------------------------------------------------------------------- /2020/day6/day6.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n\n") 3 | 4 | print(sum(len(set(r.replace("\n",""))) for r in s)) 5 | 6 | import string 7 | 8 | cc = 0 9 | for r in s: 10 | p = r.split("\n") 11 | for c in string.ascii_lowercase: 12 | if all(c in pi for pi in p): 13 | cc += 1 14 | print(cc) 15 | 16 | 17 | print(sum(1 for c in string.ascii_lowercase for r in s if all(c in pi for pi in r.split("\n")))) 18 | -------------------------------------------------------------------------------- /2020/day2/day2.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | c = 0 5 | for ln in s: 6 | r,lt,pw = ln.split(" ") 7 | lt = lt[0] 8 | rl,rh = map(int,r.split("-")) 9 | if rl <= pw.count(lt) <= rh: 10 | c += 1 11 | 12 | print(c) 13 | 14 | 15 | c = 0 16 | for ln in s: 17 | r,lt,pw = ln.split(" ") 18 | lt = lt[0] 19 | rl,rh = map(int,r.split("-")) 20 | if (pw[rl-1] == lt) ^ (pw[rh-1] == lt): 21 | c += 1 22 | 23 | print(c) 24 | -------------------------------------------------------------------------------- /2018/day-02/day_2.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | inp = inp.split("\n") 5 | 6 | count3 = sum(any(s.count(c) == 3 for c in s) for s in inp) 7 | count2 = sum(any(s.count(c) == 2 for c in s) for s in inp) 8 | print("Part 1:",count2*count3) 9 | for s1 in inp: 10 | for s2 in inp: 11 | if sum(s1[i] == s2[i] for i in range(len(s1))) == len(s1)-1: 12 | print("Part 2:","".join(s1[i] for i in range(len(s1)) if s1[i] == s2[i])) 13 | break 14 | -------------------------------------------------------------------------------- /2018/day-05/day_5.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | def react(poly): 5 | i = 0 6 | while i < len(poly)-1: 7 | if abs(ord(poly[i]) - ord(poly[i+1])) == 32: 8 | poly = poly[:i] + poly[i+2:] 9 | i = max(0, i-1) 10 | else: 11 | i += 1 12 | return poly 13 | 14 | poly = react(inp) 15 | print("Part 1:",len(poly)) 16 | print("Part 2:",min(len(react(poly.replace(c,"").replace(c.upper(),""))) for c in set(poly.lower()))) 17 | -------------------------------------------------------------------------------- /2017/day02/day02.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | import re 5 | 6 | s = [re.sub("\\s+"," ",r).split(" ") for r in s] 7 | s = [[int(x) for x in r] for r in s] 8 | 9 | print(sum(max(r)-min(r) for r in s)) 10 | 11 | ans = 0 12 | for r in s: 13 | for i in range(len(r)): 14 | for j in range(i+1,len(r)): 15 | if r[i] % r[j] == 0: 16 | ans += r[i] // r[j] 17 | elif r[j] % r[i] == 0: 18 | ans += r[j] // r[i] 19 | 20 | print(ans) 21 | -------------------------------------------------------------------------------- /2017/day05/day05.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | v = [int(x) for x in s] 5 | idx = 0 6 | 7 | steps = 0 8 | while idx in range(len(v)): 9 | v[idx] += 1 10 | idx += v[idx] - 1 11 | steps += 1 12 | 13 | print(steps) 14 | 15 | 16 | v = [int(x) for x in s] 17 | idx = 0 18 | 19 | steps = 0 20 | while idx in range(len(v)): 21 | j = v[idx] 22 | if j >= 3: 23 | v[idx] -= 1 24 | else: 25 | v[idx] += 1 26 | idx += j 27 | steps += 1 28 | 29 | print(steps) 30 | -------------------------------------------------------------------------------- /2018/day-21/input.txt: -------------------------------------------------------------------------------- 1 | #ip 4 2 | seti 123 0 2 3 | bani 2 456 2 4 | eqri 2 72 2 5 | addr 2 4 4 6 | seti 0 0 4 7 | seti 0 8 2 8 | bori 2 65536 5 9 | seti 2238642 0 2 10 | bani 5 255 3 11 | addr 2 3 2 12 | bani 2 16777215 2 13 | muli 2 65899 2 14 | bani 2 16777215 2 15 | gtir 256 5 3 16 | addr 3 4 4 17 | addi 4 1 4 18 | seti 27 3 4 19 | seti 0 8 3 20 | addi 3 1 1 21 | muli 1 256 1 22 | gtrr 1 5 1 23 | addr 1 4 4 24 | addi 4 1 4 25 | seti 25 4 4 26 | addi 3 1 3 27 | seti 17 2 4 28 | setr 3 9 5 29 | seti 7 9 4 30 | eqrr 2 0 3 31 | addr 3 4 4 32 | seti 5 0 4 33 | -------------------------------------------------------------------------------- /2020/aoc_lib.py: -------------------------------------------------------------------------------- 1 | 2 | def get_input(): 3 | with open("input.txt") as f: 4 | s = f.read().strip() 5 | lines = s.split("\n") 6 | print(f"num lines = {len(lines)}") 7 | total_length = sum(len(line) for line in lines) 8 | print(f"avg line length = {round(total_length/len(lines),2)}") 9 | return s 10 | 11 | def grid(s, split = None): 12 | if split is None: 13 | split = lambda line : list(line) 14 | s = s.split("\n") 15 | n = len(s) 16 | g = [split(line) for line in s] 17 | m = len(g[0]) 18 | return n, m, g 19 | 20 | -------------------------------------------------------------------------------- /2020/day10/day10.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip() 3 | 4 | nums = [int(x) for x in s.split("\n")] 5 | nums.append(0) 6 | nums.append(max(nums)+3) 7 | 8 | nums.sort() 9 | 10 | c3 = sum(1 for i in range(1,len(nums)) if nums[i] - nums[i-1] == 3) 11 | c1 = sum(1 for i in range(1,len(nums)) if nums[i] - nums[i-1] == 1) 12 | 13 | print(c1*c3) 14 | 15 | cnts = [0]*(len(nums)) 16 | 17 | cnts[0] = 1 18 | for i in range(1,len(cnts)): 19 | for j in range(0,i): 20 | if nums[i] - nums[j] <= 3: 21 | cnts[i] += cnts[j] 22 | 23 | print(cnts[-1]) 24 | -------------------------------------------------------------------------------- /2018/day-19/input.txt: -------------------------------------------------------------------------------- 1 | #ip 2 2 | addi 2 16 2 3 | seti 1 4 3 4 | seti 1 5 1 5 | mulr 3 1 5 6 | eqrr 5 4 5 7 | addr 5 2 2 8 | addi 2 1 2 9 | addr 3 0 0 10 | addi 1 1 1 11 | gtrr 1 4 5 12 | addr 2 5 2 13 | seti 2 9 2 14 | addi 3 1 3 15 | gtrr 3 4 5 16 | addr 5 2 2 17 | seti 1 6 2 18 | mulr 2 2 2 19 | addi 4 2 4 20 | mulr 4 4 4 21 | mulr 2 4 4 22 | muli 4 11 4 23 | addi 5 7 5 24 | mulr 5 2 5 25 | addi 5 4 5 26 | addr 4 5 4 27 | addr 2 0 2 28 | seti 0 1 2 29 | setr 2 1 5 30 | mulr 5 2 5 31 | addr 2 5 5 32 | mulr 2 5 5 33 | muli 5 14 5 34 | mulr 5 2 5 35 | addr 4 5 4 36 | seti 0 6 0 37 | seti 0 6 2 38 | -------------------------------------------------------------------------------- /2021/day07/day07.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | 4 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 5 | 6 | def nums(s): 7 | m = nums_regex.match(s) 8 | vals = m.capturesdict()["nums"] 9 | return [int(x) for x in vals] 10 | 11 | with open("input.txt") as f: 12 | s = f.read().strip() 13 | 14 | x = nums(s) 15 | 16 | print(min(sum(abs(y-t) for y in x) for t in range(0,2000))) 17 | 18 | cost = [0] 19 | for i in range(1,2000): 20 | cost.append(cost[-1] + i) 21 | 22 | print(min(sum(cost[abs(y-t)] for y in x) for t in range(0,2000))) 23 | -------------------------------------------------------------------------------- /2017/day06/day06.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | with open("input.txt") as f: 4 | s = re.sub("\\s+"," ",f.read().strip()).split(" ") 5 | 6 | s = [int(x) for x in s] 7 | 8 | seen = {} 9 | 10 | count = 0 11 | while tuple(s) not in seen: 12 | seen[tuple(s)] = count 13 | idx = max(range(len(s)), key = lambda i : (s[i], -i)) 14 | value = s[idx] 15 | s[idx] = 0 16 | per, rem = value // len(s), value % len(s) 17 | for i in range(len(s)): 18 | s[i] += per 19 | if (i - idx - 1) % len(s) < rem: 20 | s[i] += 1 21 | count += 1 22 | 23 | print(count) 24 | print(count - seen[tuple(s)]) 25 | -------------------------------------------------------------------------------- /2017/day08/day08.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | from collections import defaultdict 5 | 6 | reg = defaultdict(lambda : 0) 7 | 8 | m = 0 9 | for ins in s: 10 | cmd, cnd = ins.split(" if ") 11 | cnd_r, cnd_op, cnd_cmp = cnd.split(" ") 12 | if eval(str(reg[cnd_r]) + cnd_op + cnd_cmp): 13 | cmd_r, cmd_op, cmd_arg = cmd.split(" ") 14 | if cmd_op == "inc": 15 | reg[cmd_r] += int(cmd_arg) 16 | elif cmd_op == "dec": 17 | reg[cmd_r] -= int(cmd_arg) 18 | m = max(m, reg[cmd_r]) 19 | 20 | print(max(reg.values())) 21 | print(m) 22 | -------------------------------------------------------------------------------- /2018/day-12/input.txt: -------------------------------------------------------------------------------- 1 | initial state: #.##.#.##..#.#...##...#......##..#..###..##..#.#.....##..###...#.#..#...######...#####..##....#..### 2 | 3 | ##.## => . 4 | ##... => # 5 | ..#.# => # 6 | #.... => . 7 | #..#. => # 8 | .#### => . 9 | .#..# => . 10 | .##.# => . 11 | #.##. => # 12 | ####. => . 13 | ..##. => . 14 | ##..# => . 15 | .#.## => # 16 | .#... => . 17 | .##.. => # 18 | ..#.. => # 19 | #..## => # 20 | #.#.. => # 21 | ..### => # 22 | ...#. => # 23 | ###.. => . 24 | ##.#. => # 25 | #.#.# => # 26 | ##### => # 27 | ....# => . 28 | #.### => . 29 | .#.#. => # 30 | .###. => # 31 | ...## => . 32 | ..... => . 33 | ###.# => # 34 | #...# => . 35 | -------------------------------------------------------------------------------- /2022/day03/day03.py: -------------------------------------------------------------------------------- 1 | from aoc_tools import * 2 | 3 | with open("input.txt") as f: 4 | s = f.read().strip() 5 | 6 | s = s.split("\n") 7 | 8 | import string 9 | p = lambda c : ord(c) - ord('a') + 1 if c in string.ascii_lowercase else ( 10 | ord(c) - ord("A") + 27) 11 | 12 | 13 | r = 0 14 | for c in s: 15 | c1 = c[:len(c)//2] 16 | c2 = c[len(c)//2:] 17 | z = set(c1) & set(c2) 18 | assert len(z) == 1 19 | r += p(list(z)[0]) 20 | 21 | print(r) 22 | 23 | 24 | 25 | r = 0 26 | for i in range(0,len(s),3): 27 | z = set(s[i]) & set(s[i+1]) & set(s[i+2]) 28 | assert len(z) == 1 29 | r += p(list(z)[0]) 30 | 31 | print(r) 32 | -------------------------------------------------------------------------------- /2018/day-03/day_3.py: -------------------------------------------------------------------------------- 1 | import numpy as np # lmao 2 | 3 | with open("input.txt") as file: 4 | inp = file.read().strip() 5 | 6 | inp = inp.replace("#","").replace("@ ","").replace(":","").replace(","," ").replace("x"," ").split("\n") 7 | inp = [[int(x) for x in line.split(" ")] for line in inp] 8 | 9 | mat = np.empty((1000,1000), dtype=np.int) 10 | 11 | for entry in inp: 12 | mat[entry[1]:entry[1]+entry[3],entry[2]:entry[2]+entry[4]] += 1 13 | 14 | print("Part 1:",(mat > 1).sum()) 15 | 16 | mat -= 1 17 | 18 | for entry in inp: 19 | if mat[entry[1]:entry[1]+entry[3],entry[2]:entry[2]+entry[4]].sum() == 0: 20 | print("Part 2:",entry[0]) 21 | -------------------------------------------------------------------------------- /2018/day-06/input.txt: -------------------------------------------------------------------------------- 1 | 84, 212 2 | 168, 116 3 | 195, 339 4 | 110, 86 5 | 303, 244 6 | 228, 338 7 | 151, 295 8 | 115, 49 9 | 161, 98 10 | 60, 197 11 | 40, 55 12 | 55, 322 13 | 148, 82 14 | 86, 349 15 | 145, 295 16 | 243, 281 17 | 91, 343 18 | 280, 50 19 | 149, 129 20 | 174, 119 21 | 170, 44 22 | 296, 148 23 | 152, 160 24 | 115, 251 25 | 266, 281 26 | 269, 285 27 | 109, 242 28 | 136, 241 29 | 236, 249 30 | 338, 245 31 | 71, 101 32 | 254, 327 33 | 208, 231 34 | 289, 184 35 | 282, 158 36 | 352, 51 37 | 326, 230 38 | 88, 240 39 | 292, 342 40 | 352, 189 41 | 231, 141 42 | 280, 350 43 | 296, 185 44 | 226, 252 45 | 172, 235 46 | 137, 161 47 | 207, 90 48 | 101, 133 49 | 156, 234 50 | 241, 185 51 | -------------------------------------------------------------------------------- /2017/day09/day09.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip() 3 | 4 | import re 5 | 6 | s = re.sub("!.","",s) 7 | 8 | in_g = False 9 | g_count = 0 10 | idx = 0 11 | while idx < len(s): 12 | if in_g: 13 | if s[idx] == ">": 14 | in_g = False 15 | else: 16 | g_count += 1 17 | else: 18 | if s[idx] == "<": 19 | in_g = True 20 | idx += 1 21 | 22 | s = re.sub("\\<.*?\\>","",s) 23 | 24 | scores = 0 25 | stack = [] 26 | for c in s: 27 | if c == "{": 28 | stack.append(0) 29 | elif c == "}": 30 | stack.pop(-1) 31 | scores += len(stack) + 1 32 | 33 | print(scores) 34 | print(g_count) 35 | -------------------------------------------------------------------------------- /2021/day02/day02.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | # part 1 5 | horiz = 0 6 | depth = 0 7 | for line in s: 8 | op,x = line.split(" ") 9 | x = int(x) 10 | if op == "forward": 11 | horiz += x 12 | elif op == "down": 13 | depth += x 14 | elif op == "up": 15 | depth -= x 16 | 17 | print(horiz*depth) 18 | 19 | horiz = 0 20 | depth = 0 21 | aim = 0 22 | for line in s: 23 | op,x = line.split(" ") 24 | x = int(x) 25 | if op == "forward": 26 | horiz += x 27 | depth += aim * x 28 | elif op == "down": 29 | aim += x 30 | elif op == "up": 31 | aim -= x 32 | 33 | print(horiz*depth) 34 | -------------------------------------------------------------------------------- /2017/day12/day12.py: -------------------------------------------------------------------------------- 1 | import regex 2 | 3 | nums_regex = regex.compile("((?P\\d+)([^\\d]*))*") 4 | 5 | def nums(s): 6 | m = nums_regex.match(s) 7 | vals = m.capturesdict()["nums"] 8 | return [int(x) for x in vals] 9 | 10 | with open("input.txt") as f: 11 | s = f.read().strip().split("\n") 12 | 13 | g = {} 14 | for line in s: 15 | line = nums(line) 16 | g[line[0]] = line[1:] 17 | 18 | seen = set() 19 | def dfs(cur): 20 | if cur in seen: 21 | return 22 | seen.add(cur) 23 | for nxt in g[cur]: 24 | dfs(nxt) 25 | 26 | dfs(0) 27 | print(len(seen)) 28 | 29 | groups = 1 30 | for i in g: 31 | if i not in seen: 32 | dfs(i) 33 | groups += 1 34 | print(groups) 35 | -------------------------------------------------------------------------------- /2017/day17/day17.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | inp = 329 13 | 14 | buf = [0] 15 | 16 | cur = 0 17 | 18 | for v in range(1,2017+1): 19 | cur = (cur + inp) % len(buf) 20 | buf.insert(cur + 1, v) 21 | cur += 1 22 | 23 | print(buf[buf.index(2017)+1]) 24 | 25 | 26 | after_0 = 0 27 | cur = 0 28 | buf_len = 1 29 | for v in range(1,50000000+1): 30 | cur = (cur + inp) % buf_len 31 | buf_len += 1 32 | if cur == 0: 33 | after_0 = v 34 | cur += 1 35 | 36 | print(after_0) 37 | -------------------------------------------------------------------------------- /2021/day17/day17.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | import itertools 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | with open("input.txt") as f: 13 | s = f.read().strip() 14 | 15 | tx1, tx2 = 244, 303 16 | ty1, ty2 = -91, -54 17 | 18 | def sim(vx,vy): 19 | cx,cy = 0,0 20 | hy = 0 21 | while cy > -100: 22 | #print(cx,cy) 23 | cx += vx 24 | cy += vy 25 | vx = max(vx - 1, 0) 26 | hy = max(hy, cy) 27 | vy -= 1 28 | if 244 <= cx <= 303 and -91 <= cy <= -54: 29 | return hy 30 | return -1 31 | 32 | -------------------------------------------------------------------------------- /2020/day10/day10_alt.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import time 3 | 4 | with open("input.txt") as f: 5 | s = f.read().strip() 6 | 7 | nums = [int(x) for x in s.split("\n")] 8 | nums.append(0) 9 | nums.append(max(nums)+3) 10 | #nums.sort() 11 | m#nums[4] = 5 12 | 13 | st = time.time() 14 | 15 | f = lambda i, j : j > i and nums[j] - nums[i] <= 3 16 | m = np.fromfunction(np.vectorize(f), (len(nums), len(nums)), dtype=np.int64).astype(np.int64) 17 | m[len(nums)-1, len(nums)-1] = 1 18 | 19 | aux = np.eye(len(nums)) 20 | 21 | aux = np.linalg.matrix_power(m, len(nums)) 22 | #ans = 0 23 | #for _ in range(len(nums)): 24 | # aux = aux @ m 25 | # ans += aux[0, len(nums) - 1] 26 | 27 | et = time.time() 28 | 29 | ans = aux[0, len(nums) - 1] 30 | 31 | print(ans) 32 | print(et - st) 33 | -------------------------------------------------------------------------------- /2022/day15/day15_p1.py: -------------------------------------------------------------------------------- 1 | import string 2 | from collections import defaultdict 3 | from aoc_tools import * 4 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 5 | 6 | with open(r"C:\Users\Neil\Documents\AOC2022\day15\input.txt") as f: 7 | s = f.read().strip() 8 | 9 | d = [nums(r) for r in s.split("\n")] 10 | 11 | dist = lambda x1,y1,x2,y2 : abs(y2-y1) + abs(x2-x1) 12 | 13 | c = 0 14 | 15 | for x in range(-9019876, 9019876): 16 | if x % 100_000 == 0: 17 | print(x) 18 | y = 2000000 19 | poss = True 20 | for sx,sy,bx,by in d: 21 | if (x,y) == (bx,by): 22 | poss = True 23 | break 24 | if dist(sx,sy,x,y) <= dist(sx,sy,bx,by): 25 | poss = False 26 | break 27 | if not poss: 28 | c += 1 29 | print(c) 30 | -------------------------------------------------------------------------------- /2021/day06/day06.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | 4 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 5 | 6 | def nums(s): 7 | m = nums_regex.match(s) 8 | vals = m.capturesdict()["nums"] 9 | return [int(x) for x in vals] 10 | 11 | with open("input.txt") as f: 12 | s = f.read().strip().split("\n") 13 | 14 | f = nums(s[0]) 15 | f = Counter(f) 16 | 17 | def proc(x): 18 | n = defaultdict(int) 19 | for k,v in f.items(): 20 | if k == 0: 21 | n[6] += v 22 | n[8] += v 23 | else: 24 | n[k-1] += v 25 | return n 26 | 27 | for _ in range(80): 28 | f = proc(f) 29 | 30 | print(sum(f.values())) 31 | 32 | for _ in range(256-80): 33 | f = proc(f) 34 | 35 | print(sum(f.values())) 36 | -------------------------------------------------------------------------------- /2020/day1/day1.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip() 3 | 4 | s = [int(x) for x in s.split("\n")] 5 | 6 | # part 1 7 | for i in range(len(s)): 8 | for j in range(i+1,len(s)): 9 | if s[i]+s[j] == 2020: 10 | print(s[i]*s[j]) 11 | 12 | # part 2 13 | for i in range(len(s)): 14 | for j in range(i+1,len(s)): 15 | for k in range(j+1,len(s)): 16 | if s[i]+s[j]+s[k] == 2020: 17 | print(s[i]*s[j]*s[k]) 18 | 19 | 20 | # faster version for neel 21 | s_set = set() 22 | for i in range(len(s)): 23 | if 2020 - s[i] in s_set: 24 | print(s[i]*(2020-s[i])) 25 | s_set.add(s[i]) 26 | 27 | s_set = set() 28 | for i in range(len(s)): 29 | for j in range(i+1,len(s)): 30 | if 2020 - s[i] - s[j] in s_set: 31 | print(s[i]*s[j]*(2020-s[i]-s[j])) 32 | s_set.add(s[i]) 33 | -------------------------------------------------------------------------------- /2018/day-08/day_8.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | inp = list(map(int,inp.split(" "))) 5 | data = inp.copy() 6 | 7 | global total_meta 8 | total_meta = 0 9 | 10 | def gen_node(dat): 11 | global total_meta 12 | num_child = dat.pop(0) 13 | num_meta = dat.pop(0) 14 | children = [] 15 | for _ in range(num_child): 16 | children.append(gen_node(dat)) 17 | meta = [] 18 | for _ in range(num_meta): 19 | meta.append(dat.pop(0)) 20 | total_meta += meta[-1] 21 | return (children, meta) 22 | 23 | tree = gen_node(data) 24 | print("Part 1:",total_meta) 25 | 26 | def get_value(node): 27 | if len(node[0]) == 0: 28 | return sum(node[1]) 29 | else: 30 | return sum(get_value(node[0][n-1]) for n in node[1] if n <= len(node[0])) 31 | 32 | print("Part 2:",get_value(tree)) 33 | -------------------------------------------------------------------------------- /2022/day04/day04.py: -------------------------------------------------------------------------------- 1 | from aoc_tools import * 2 | 3 | with open("input.txt") as f: 4 | s = f.read().strip() 5 | 6 | s = s.split("\n") 7 | 8 | c = 0 9 | for l in s: 10 | a,b = l.split(",") 11 | a1,a2 = map(int,a.split("-")) 12 | b1,b2 = map(int,b.split("-")) 13 | a = set(range(a1,a2+1)) 14 | b = set(range(b1,b2+1)) 15 | if a.issubset(b) or b.issubset(a): 16 | ## if a1 <= b1 <= b2 <= a2 or ( 17 | ## b1 <= a1 <= a2 <= b2): 18 | c += 1 19 | print(c) 20 | 21 | c = 0 22 | for l in s: 23 | a,b = l.split(",") 24 | a1,a2 = map(int,a.split("-")) 25 | b1,b2 = map(int,b.split("-")) 26 | if a1 <= b1 <= a2 or b1 <= a1 <= b2: 27 | ## a = set(range(a1,a2+1)) 28 | ## b = set(range(b1,b2+1)) 29 | ## if len(a & b) > 0: 30 | ## if a2 >= b1 and a1 <= b2 or ( 31 | ## b2 >= a1 and b1 <= a2): 32 | c += 1 33 | print(c) 34 | -------------------------------------------------------------------------------- /2020/day15/day15.py: -------------------------------------------------------------------------------- 1 | s = list(map(int,"5,2,8,16,18,0,1".split(","))) 2 | 3 | speak = [] 4 | for i in s: 5 | speak.append(i) 6 | 7 | while len(speak) < 2020: 8 | last = speak[-1] 9 | if last not in speak[:-1]: 10 | speak.append(0) 11 | else: 12 | speak.append(speak[:-1][::-1].index(last) + 1) 13 | 14 | print(speak[-1]) 15 | 16 | def say(last, i, n): 17 | if n not in last: 18 | last[n] = (-1, i) 19 | else: 20 | pr = last[n][1] 21 | last[n] = (pr, i) 22 | return n 23 | 24 | last = {} 25 | prev = -1 26 | for i in range(30000000): 27 | if i < len(s): 28 | prev = say(last, i, s[i]) 29 | else: 30 | if last[prev][0] == -1: 31 | prev = say(last, i, 0) 32 | else: 33 | newn = last[prev][1] - last[prev][0] 34 | prev = say(last, i, newn) 35 | 36 | print(prev) 37 | 38 | -------------------------------------------------------------------------------- /2018/day-23/day_23.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | bots = inp.split("\n") 5 | bots = [(eval("["+b[1+b.find("<"):b.rfind(">")]+"]"),int(b[b.rfind("=")+1:])) for b in bots] 6 | s = max(bots, key=lambda v : v[1]) 7 | cnt = 0 8 | for b in bots: 9 | dist = abs(b[0][0]-s[0][0])+abs(b[0][1]-s[0][1])+abs(b[0][2]-s[0][2]) 10 | if dist <= s[1]: 11 | cnt += 1 12 | 13 | print("Part 1:",cnt) 14 | 15 | from z3 import * 16 | 17 | x,y,z = Int('x'),Int('y'),Int('z') 18 | in_ranges = [] 19 | Abs = lambda c : If(c > 0, c, -c) 20 | for bot in bots: 21 | in_ranges.append(If( (Abs(x - bot[0][0]) + Abs(y-bot[0][1]) 22 | + Abs(z-bot[0][2])) <= bot[1], 1, 0 )) 23 | 24 | opt = Optimize() 25 | v1 = opt.maximize(Sum(*in_ranges)) 26 | v2 = opt.minimize(Abs(x)+Abs(y)+Abs(z)) 27 | opt.check() 28 | 29 | print("Part 2:",v2.value()) 30 | -------------------------------------------------------------------------------- /2020/day9/day9.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | nums = [int(x) for x in s] 5 | 6 | def valid(nums,n): 7 | for i in range(len(nums)): 8 | for j in range(i+1,len(nums)): 9 | if nums[i] + nums[j] == n: 10 | return True 11 | return False 12 | 13 | target = -1 14 | for i in range(25,len(nums)): 15 | if not valid(nums[i-25:i], nums[i]): 16 | target = nums[i] 17 | print(target) 18 | break 19 | 20 | sset = {} 21 | rsum = 0 22 | 23 | for i in range(len(nums)): 24 | for j in range(i+1,len(nums)): 25 | if sum(nums[i:j+1]) == target: 26 | print(min(nums[i:j+1])+max(nums[i:j+1])) 27 | 28 | for i in range(len(nums)): 29 | sset[rsum] = i 30 | rsum += nums[i] 31 | if rsum - target in sset: 32 | lo,hi = sset[rsum-target],i 33 | vals = nums[lo:hi+1] 34 | print(min(vals)+max(vals)) 35 | break 36 | -------------------------------------------------------------------------------- /2020/day7/day7.py: -------------------------------------------------------------------------------- 1 | import regex 2 | 3 | nums_regex = regex.compile("((?P\\d+)([^\\d]*))*") 4 | 5 | def nums(s): 6 | m = nums_regex.match(s) 7 | vals = m.capturesdict()["nums"] 8 | return [int(x) for x in vals] 9 | 10 | # yeah, i know this should be dfs but 11 | valid = set() 12 | last_len = -1 13 | while len(valid) != last_len: 14 | last_len = len(valid) 15 | for color in rules: 16 | if rules[color] is None: 17 | continue 18 | if any(rc == "shiny gold" for rn, rc in rules[color]): 19 | valid.add(color) 20 | if any(rc in valid for rn, rc in rules[color]): 21 | valid.add(color) 22 | 23 | print(len(valid)) 24 | 25 | import sys 26 | 27 | sys.setrecursionlimit(100000) 28 | 29 | def ans(c): 30 | cnt = 1 31 | if rules[c] is None: 32 | return cnt 33 | for rn, rc in rules[c]: 34 | cnt += rn * ans(rc) 35 | return cnt 36 | 37 | print(ans("shiny gold") - 1) 38 | -------------------------------------------------------------------------------- /2018/day-11/day_11.py: -------------------------------------------------------------------------------- 1 | grid_serial = 6392 2 | 3 | fuel = [[0 for _ in range(300)] for _ in range(300)] 4 | rsum = [[0 for _ in range(301)] for _ in range(301)] 5 | 6 | for i in range(300): 7 | for j in range(300): 8 | fuel[i][j] = (((i+11)*(j+1)+grid_serial)*(i+11))//100%10-5 9 | 10 | for i in range(300): 11 | for j in range(300): 12 | rsum[i+1][j+1] += fuel[i][j] 13 | rsum[i+1][j+1] += rsum[i][j+1] 14 | rsum[i+1][j+1] += rsum[i+1][j] 15 | rsum[i+1][j+1] -= rsum[i][j] 16 | 17 | calc_sum = lambda x,y,s,r : r[x+s][y+s]-r[x][y+s]-r[x+s][y]+r[x][y] 18 | 19 | print("Part 1:",",".join(map(str,max(((x+1,y+1) for x in range(300-2) for y in range(300-2)), \ 20 | key=lambda p:calc_sum(p[0]-1,p[1]-1,3,rsum))))) 21 | print("Part 2:",",".join(map(str,max(((x+1,y+1,s) for s in range(1,301) for x in range(300-s+1) \ 22 | for y in range(300-s+1)), key=lambda p:calc_sum(p[0]-1,p[1]-1,p[2],rsum))))) 23 | -------------------------------------------------------------------------------- /2017/day07/day07.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | weights = {} 5 | children = {} 6 | 7 | seen = set() 8 | 9 | for p in s: 10 | pn = p[:p.find(" ")] 11 | pw = int(p[p.find("(")+1:p.find(")")]) 12 | weights[pn] = pw 13 | if "->" in p: 14 | children[pn] = p[p.find("->")+3:].split(", ") 15 | seen.update(children[pn]) 16 | 17 | root = None 18 | for p in weights: 19 | if p not in seen: 20 | root = p 21 | print(root) 22 | 23 | def find(c): 24 | w = weights[c] 25 | if c in children: 26 | cw = [find(n) for n in children[c]] 27 | if -1 in cw: 28 | return -1 29 | w += sum(cw) 30 | if len(set(cw)) != 1: 31 | for i in range(len(cw)): 32 | if cw[i] != cw[i-1] and cw[i] != cw[i-2]: 33 | print(weights[children[c][i]] - cw[i] + cw[i-1]) 34 | return -1 35 | return w 36 | 37 | find(root) 38 | -------------------------------------------------------------------------------- /2020/day18/day18_alt.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | 5 | # + => + 6 | # * => - 7 | class P1: 8 | def __init__(self, v): 9 | self.v = v 10 | 11 | def __add__(self, o): 12 | return P1(self.v + o.v) 13 | 14 | def __sub__(self, o): 15 | return P1(self.v * o.v) 16 | 17 | ans = 0 18 | for l in s: 19 | l = "".join("P1("+x+")" if x.isdigit() else x for x in l) 20 | l = l.replace("*","-") 21 | ans += eval(l).v 22 | print(ans) 23 | 24 | # + => * 25 | # * => + 26 | class P2: 27 | def __init__(self, v): 28 | self.v = v 29 | 30 | def __add__(self, o): 31 | return P2(self.v * o.v) 32 | 33 | def __mul__(self, o): 34 | return P2(self.v + o.v) 35 | 36 | ans = 0 37 | for l in s: 38 | l = "".join("P2("+x+")" if x.isdigit() else x for x in l) 39 | l = l.replace("*","?").replace("+","*").replace("?","+") 40 | ans += eval(l).v 41 | print(ans) 42 | 43 | 44 | -------------------------------------------------------------------------------- /2017/day13/day13.py: -------------------------------------------------------------------------------- 1 | import regex 2 | 3 | nums_regex = regex.compile("((?P\\d+)([^\\d]*))*") 4 | 5 | def nums(s): 6 | m = nums_regex.match(s) 7 | vals = m.capturesdict()["nums"] 8 | return [int(x) for x in vals] 9 | 10 | with open("input.txt") as f: 11 | s = f.read().strip().split("\n") 12 | 13 | layers = {} 14 | for line in s: 15 | line = nums(line) 16 | layers[line[0]] = line[1] 17 | 18 | # if depth is d 19 | # d - 1 in each direction 20 | # returns to top every 2d - 2 21 | 22 | def at_top(r,t): 23 | return t % (2*r - 2) == 0 24 | 25 | def v(d,t): 26 | t = t % (2*d - 2) 27 | return min(2*d - 2 - t, t) 28 | 29 | print(sum(depth*layers[depth] for depth in layers \ 30 | if depth % (2*layers[depth] - 2) == 0)) 31 | 32 | 33 | restrictions = [] 34 | for depth in layers: 35 | restrictions.append((depth, 2 * layers[depth] - 2)) 36 | 37 | i = 0 38 | while any((i+a)%b == 0 for a,b in restrictions): 39 | i += 1 40 | 41 | print(i) 42 | -------------------------------------------------------------------------------- /2017/day19/day19.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | # N,E,S,W 7 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 8 | 9 | with open("input.txt") as f: 10 | s = f.read().strip("\n").split("\n") 11 | 12 | g = [list(r) for r in s] 13 | 14 | x = 0 15 | y = g[0].index("|") 16 | cdir = 1 17 | 18 | st = 0 19 | path = "" 20 | while "Z" not in path: 21 | while g[x][y] in "|-": 22 | dx, dy = dirs[cdir] 23 | x += dx 24 | y += dy 25 | st += 1 26 | if g[x][y].isalpha(): 27 | path += g[x][y] 28 | x += dx 29 | y += dy 30 | st += 1 31 | elif g[x][y] == "+": 32 | for ndir in (1,-1): 33 | ndir = (cdir + ndir) % 4 34 | dx, dy = dirs[ndir] 35 | if g[x+dx][y+dy] != " ": 36 | cdir = ndir 37 | x += dx 38 | y += dy 39 | st += 1 40 | break 41 | print(path) 42 | print(st) 43 | -------------------------------------------------------------------------------- /2017/day11/day11.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split(",") 3 | 4 | coords = [0,0,0] 5 | 6 | lookup = { 7 | "n" : (0,1), 8 | "s" : (0,-1), 9 | "ne" : (1,1), 10 | "sw" : (1,-1), 11 | "se" : (2,1), 12 | "nw" : (2,-1) 13 | } 14 | 15 | dist = lambda c : sum(abs(ci) for ci in c) 16 | 17 | def rule1(coords, freq): 18 | coords[0] -= freq 19 | coords[2] -= freq 20 | coords[1] += freq 21 | 22 | def rule2(coords, freq): 23 | coords[0] -= freq 24 | coords[1] += freq 25 | coords[2] -= freq 26 | 27 | def true_dist(coords): 28 | rule1(coords, coords[0]) 29 | min_dist = dist(coords) 30 | rule1(coords, coords[2]) 31 | min_dist = min(min_dist, dist(coords)) 32 | rule2(coords, -coords[1]) 33 | return min(min_dist, dist(coords)) 34 | 35 | furthest = 0 36 | for d in s: 37 | i,v = lookup[d] 38 | coords[i] += v 39 | furthest = max(furthest, true_dist(coords)) 40 | 41 | print(true_dist(coords)) 42 | print(furthest) 43 | -------------------------------------------------------------------------------- /2020/day13/day13.py: -------------------------------------------------------------------------------- 1 | import functools 2 | 3 | with open("input.txt") as f: 4 | s = f.read().strip().split("\n") 5 | 6 | ts = int(s[0]) 7 | v = s[1].split(",") 8 | nums = set(int(x) for x in v if x != "x") 9 | 10 | c = ts 11 | while all(c % n != 0 for n in nums): 12 | c += 1 13 | 14 | for n in nums: 15 | if c % n == 0: 16 | print(n * (c - ts)) 17 | 18 | def egcd(a, b): 19 | if a == 0: 20 | return (b, 0, 1) 21 | else: 22 | g, y, x = egcd(b % a, a) 23 | return (g, x - (b // a) * y, y) 24 | 25 | def _crt(n1, n2, r1, r2): 26 | d, m1, m2 = egcd(n1, n2) 27 | return (r1 * m2 * n2 + r2 * m1 * n1) % (n1 * n2) 28 | 29 | def crt(mods, ress): 30 | return functools.reduce( 31 | lambda p1, p2 : (p1[0] * p2[0], _crt(p1[0], p2[0], p1[1], p2[1])) 32 | , zip(mods, ress))[1] 33 | 34 | mods = [] 35 | ress = [] 36 | 37 | for i,x in enumerate(v): 38 | if x == "x": 39 | continue 40 | mods.append(int(x)) 41 | ress.append(-i) 42 | 43 | print(crt(mods, ress)) 44 | -------------------------------------------------------------------------------- /2017/day03/day03.py: -------------------------------------------------------------------------------- 1 | inp = 325489 2 | 3 | s = 1 4 | while s*s < inp: 5 | s += 2 6 | s -= 2 7 | 8 | v = s*s + 1 9 | loc = [(s//2)+1, -(s//2)] 10 | if v < inp: 11 | loc[1] += min(inp - v, s) 12 | v += min(inp - v, s) 13 | 14 | if v < inp: 15 | loc[0] -= min(inp - v, s+1) 16 | v += min(inp - v, s+1) 17 | 18 | if v < inp: 19 | loc[1] -= min(inp - v, s+1) 20 | v += min(inp - v, s+1) 21 | 22 | if v < inp: 23 | loc[0] += min(inp - v, s+1) 24 | v += min(inp - v, s+1) 25 | 26 | print(abs(loc[0])+abs(loc[1])) 27 | 28 | from collections import defaultdict 29 | 30 | grid = defaultdict(lambda : 0) 31 | grid[0,0] = 1 32 | loc = [0,0] 33 | 34 | dirs = [(1,0),(0,1),(-1,0),(0,-1)] 35 | v = 0 36 | while v < inp: 37 | loc[0] += dirs[0][0] 38 | loc[1] += dirs[0][1] 39 | grid[tuple(loc)] = v = sum(grid[loc[0]+i,loc[1]+j] for i in range(-1,2) \ 40 | for j in range(-1,2) if (i,j) != (0,0)) 41 | if grid[loc[0]+dirs[1][0],loc[1]+dirs[1][1]] == 0: 42 | dirs.append(dirs.pop(0)) 43 | 44 | print(v) 45 | -------------------------------------------------------------------------------- /2021/day08/day08.py: -------------------------------------------------------------------------------- 1 | import itertools 2 | 3 | with open("input.txt") as f: 4 | s = f.read().strip().split("\n") 5 | 6 | c = 0 7 | for line in s: 8 | a,b = line.split(" | ") 9 | b = b.split(" ") 10 | for x in b: 11 | if len(x) in (2,3,4,7): 12 | c += 1 13 | print(c) 14 | 15 | m = {"acedgfb":8, "cdfbe":5, "gcdfa":2, "fbcad":3, "dab":7, 16 | "cefabd":9, "cdfgeb":6, "eafb":4, "cagedb":0, "ab":1} 17 | 18 | m = {"".join(sorted(k)):v for k,v in m.items()} 19 | 20 | ans = 0 21 | for line in s: 22 | a,b = line.split(" | ") 23 | a = a.split(" ") 24 | b = b.split(" ") 25 | for perm in itertools.permutations("abcdefg"): 26 | pmap = {a:b for a,b in zip(perm,"abcdefg")} 27 | anew = ["".join(pmap[c] for c in x) for x in a] 28 | bnew = ["".join(pmap[c] for c in x) for x in b] 29 | if all("".join(sorted(an)) in m for an in anew): 30 | bnew = ["".join(sorted(x)) for x in bnew] 31 | ans += int("".join(str(m[x]) for x in bnew)) 32 | break 33 | print(ans) 34 | 35 | -------------------------------------------------------------------------------- /2022/day02/day02.py: -------------------------------------------------------------------------------- 1 | from aoc_tools import * 2 | 3 | with open("input.txt") as f: 4 | s = f.read() 5 | 6 | s = [x.split(" ") for x in s.strip().split("\n")] 7 | 8 | # A = X = Rock 9 | # B = Y = Paper 10 | # C = Z = Scissors 11 | 12 | def f(s): 13 | r = 0 14 | for a, b in s: 15 | if b == "X": 16 | r += 1 17 | if a == "C": 18 | r += 6 19 | elif a == "A": 20 | r += 3 21 | elif b == "Y": 22 | r += 2 23 | if a == "A": 24 | r += 6 25 | elif a == "B": 26 | r += 3 27 | elif b == "Z": 28 | r += 3 29 | if a == "B": 30 | r += 6 31 | elif a == "C": 32 | r += 3 33 | return r 34 | 35 | print(f(s)) 36 | 37 | m = { 38 | "AX": "Z", 39 | "AY": "X", 40 | "AZ": "Y", 41 | "BX": "X", 42 | "BY": "Y", 43 | "BZ": "Z", 44 | "CX": "Y", 45 | "CY": "Z", 46 | "CZ": "X", 47 | } 48 | 49 | s = [(a, m[a+b]) for a, b in s] 50 | 51 | print(f(s)) 52 | -------------------------------------------------------------------------------- /2017/day10/day10.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip() 3 | 4 | v = [int(x) for x in s.split(",")] 5 | 6 | cur = list(range(256)) 7 | 8 | offset = 0 9 | skip = 0 10 | for length in v: 11 | cur = cur[length:] + cur[:length][::-1] 12 | cur = cur[skip:] + cur[:skip] 13 | offset += length + skip 14 | skip += 1 15 | 16 | offset = (-offset) % len(cur) 17 | cur = cur[offset:] + cur[:offset] 18 | 19 | print(cur[0]*cur[1]) 20 | 21 | lengths = [ord(c) for c in s] + [17,31,73,47,23] 22 | 23 | cur = list(range(256)) 24 | 25 | offset = 0 26 | skip = 0 27 | for _ in range(64): 28 | for length in lengths: 29 | cur = cur[length:] + cur[:length][::-1] 30 | cur = cur[skip:] + cur[:skip] 31 | offset = (offset + length + skip) % len(cur) 32 | skip = (skip + 1) % len(cur) 33 | offset = (-offset) % len(cur) 34 | cur = cur[offset:] + cur[:offset] 35 | 36 | def xor(vals): 37 | out = 0 38 | for val in vals: 39 | out ^= val 40 | return out 41 | 42 | print("".join(hex(xor(cur[i:i+16])+256)[3:] for i in range(0,256,16))) 43 | -------------------------------------------------------------------------------- /2022/day25/day25.py: -------------------------------------------------------------------------------- 1 | import string 2 | from collections import defaultdict 3 | from aoc_tools import * 4 | import functools 5 | import sys 6 | sys.setrecursionlimit(100000) 7 | dirs = ((0,1),(1,0),(0,-1),(-1,0)) 8 | dirs3 = ((1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)) 9 | 10 | with open(r"input.txt") as f: 11 | s = f.read().strip() 12 | #print("\n".join(x[:60] for x in s.split("\n")[:6])) 13 | 14 | m = {"2":2,"1":1,"0":0,"-":-1,"=":-2} 15 | def conv(x): 16 | a = [1] 17 | while len(a)= 3 for n in b5): 36 | for i in range(len(b5)): 37 | if b5[i] >= 3: 38 | b5[i-1] += 1 39 | b5[i] -= 5 40 | print("".join(["0","1","2","=","-"][n] for n in b5).lstrip("0")) 41 | print("click the button") 42 | -------------------------------------------------------------------------------- /2021/day05/day05.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | with open("input.txt") as f: 4 | s = f.read().strip().split("\n") 5 | 6 | d = defaultdict(int) 7 | 8 | for line in s: 9 | a,b = line.split(" -> ") 10 | x1,y1 = a.split(",") 11 | x2,y2 = b.split(",") 12 | x1=int(x1) 13 | y1=int(y1) 14 | x2=int(x2) 15 | y2=int(y2) 16 | if x1 == x2: 17 | x1,x2 = min(x1,x2),max(x1,x2) 18 | y1,y2 = min(y1,y2),max(y1,y2) 19 | for y in range(y1,y2+1): 20 | d[x1,y] += 1 21 | elif y1 == y2: 22 | x1,x2 = min(x1,x2),max(x1,x2) 23 | y1,y2 = min(y1,y2),max(y1,y2) 24 | for x in range(x1,x2+1): 25 | d[x,y1] += 1 26 | else: 27 | cx,cy = x1,y1 28 | if x2 > x1: 29 | dx = 1 30 | else: 31 | dx = -1 32 | if y2 > y1: 33 | dy = 1 34 | else: 35 | dy = -1 36 | while (cx,cy) != (x2,y2): 37 | d[cx,cy] += 1 38 | cx += dx 39 | cy += dy 40 | d[cx,cy] += 1 41 | 42 | print(sum(1 for v in d.values() if v > 1)) 43 | 44 | -------------------------------------------------------------------------------- /2021/day13/day13.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | import itertools 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | with open("input.txt") as f: 13 | s, z = f.read().strip().split("\n\n") 14 | 15 | pts = set() 16 | for x in s.split("\n"): 17 | a,b = x.split(",") 18 | pts.add((int(a),int(b))) 19 | 20 | folds = [("x=" in y, nums(y)) for y in z.split("\n")] 21 | # x = is true 22 | 23 | cur = pts 24 | for xf,(v,) in folds: 25 | ncur = set() 26 | for a,b in cur: 27 | if xf: 28 | if a < v: 29 | ncur.add((a,b)) 30 | else: 31 | ncur.add((v-(a-v),b)) 32 | else: 33 | if b < v: 34 | ncur.add((a,b)) 35 | else: 36 | ncur.add((a,v-(b-v))) 37 | cur = ncur 38 | print(len(cur)) 39 | 40 | print("\n".join("".join(".#"[(i,j) in cur] \ 41 | for i in range(-1,40)) for j in range(-1,7))) 42 | -------------------------------------------------------------------------------- /2018/day-15/input.txt: -------------------------------------------------------------------------------- 1 | ################################ 2 | ##########################.##### 3 | ##########################.##### 4 | ##########################.#.### 5 | #######################......### 6 | #################....#........## 7 | ##############.##....G......G.## 8 | #############..#G...##.........# 9 | #############.GG..G..##.......## 10 | #############.................## 11 | #############G.........G....#.## 12 | ###########G..........E........# 13 | ###########...#####............# 14 | ###########..#######...........# 15 | #######.....#########........### 16 | #######....G#########.......#### 17 | ##...G.G....#########...#....### 18 | #...G..G...G#########.###E...### 19 | ##.......#..#########.#####..E## 20 | #............#######..########## 21 | #.GG........G.#####...########## 22 | #................E.....######### 23 | ########..........##.########### 24 | #########.....###.....########## 25 | ##########.E..##......########## 26 | #######..#....###.E...########## 27 | ######.........###.E############ 28 | ######.#..G....##..############# 29 | ######.....##..##.E############# 30 | #######....##.E...E############# 31 | ######....G#......############## 32 | ################################ 33 | -------------------------------------------------------------------------------- /2021/day12/day12.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | import itertools 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | with open("input.txt") as f: 13 | s = f.read().strip().split("\n") 14 | 15 | g = defaultdict(set) 16 | p = {} 17 | 18 | for l in s: 19 | a,b = l.split("-") 20 | g[a].add(b) 21 | g[b].add(a) 22 | 23 | 24 | def dfs(c,d,seen,path): 25 | if c == "end": 26 | return 1 27 | r = 0 28 | for a in g[c]: 29 | big = a[0].isupper() 30 | if seen[a] == 0: 31 | if not big: 32 | seen[a] += 1 33 | r += dfs(a,d,seen,path) 34 | if not big: 35 | seen[a] -= 1 36 | elif seen[a] == 1 and not d: 37 | if not big: 38 | seen[a] += 1 39 | r += dfs(a,True,seen,path) 40 | if not big: 41 | seen[a] -= 1 42 | return r 43 | 44 | dd = defaultdict(int) 45 | dd["start"] = 2 46 | 47 | print(dfs("start",False,dd,["start"])) 48 | -------------------------------------------------------------------------------- /2021/day14/day14.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | import itertools 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | with open("input.txt") as f: 13 | a, b = f.read().strip().split("\n\n") 14 | b = b.split("\n") 15 | rules = {} 16 | for x in b: 17 | z,y = x.split(" -> ") 18 | rules[z] = y 19 | 20 | def apply(s): 21 | out = "" 22 | for a,b in zip(s,s[1:]): 23 | out += a 24 | out += rules[a+b] 25 | out += s[-1] 26 | return out 27 | 28 | def apply(d): 29 | nd = defaultdict(int) 30 | for px,py in d.keys(): 31 | r = rules[px+py] 32 | nd[px,r] += d[px,py] 33 | nd[r,py] += d[px,py] 34 | return nd 35 | 36 | d = defaultdict(int) 37 | for x,y in zip(a,a[1:]): 38 | d[x,y] += 1 39 | 40 | for _ in range(40): 41 | d = apply(d) 42 | 43 | c = defaultdict(int) 44 | for x,y in d.keys(): 45 | c[x] += d[x,y] 46 | c[y] += d[x,y] 47 | 48 | print(max(c.values()) - min(c.values())) 49 | 50 | ## A B C 51 | 52 | # AB : 1 53 | # BC : 1 54 | -------------------------------------------------------------------------------- /2017/day25/day25.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | s = s.split("\n\n") 20 | 21 | start, diag = s[0].split("\n") 22 | start = start[-2] 23 | diag, = nums(diag) 24 | 25 | t = {} 26 | for ti in s[1:]: 27 | st, _, wr0, mv0, ns0, _, wr1, mv1, ns1 = ti.split("\n") 28 | st = st[-2] 29 | wr0 = int(wr0[-2]) 30 | mv0 = (+1 if "right" in mv0 else -1) 31 | ns0 = ns0[-2] 32 | wr1 = int(wr1[-2]) 33 | mv1 = (+1 if "right" in mv1 else -1) 34 | ns1 = ns1[-2] 35 | t[st] = ((wr0,mv0,ns0),(wr1,mv1,ns1)) 36 | 37 | tape = defaultdict(lambda : 0) 38 | cur_loc = 0 39 | cur_st = start 40 | 41 | for _ in range(diag): 42 | read = tape[cur_loc] 43 | wr,mv,ns = t[cur_st][read] 44 | tape[cur_loc] = wr 45 | cur_loc += mv 46 | cur_st = ns 47 | 48 | print(sum(tape.values())) 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /2022/day09/day09_p2.py: -------------------------------------------------------------------------------- 1 | import string 2 | from aoc_tools import * 3 | 4 | with open("input.txt") as f: 5 | s = f.read().strip() 6 | 7 | global rope 8 | rope = [[0,0] for _ in range(10)] 9 | 10 | sign = lambda x : 1 if x > 0 else ( 11 | -1 if x < 0 else 0) 12 | 13 | # i >= 1 14 | def update(i): 15 | global rope 16 | hx,hy = rope[i-1] 17 | tx,ty = rope[i] 18 | dx = tx-hx 19 | dy = ty-hy 20 | if dx == 0 or dy == 0: 21 | if abs(dx) >= 2: 22 | rope[i][0] -= sign(dx) 23 | if abs(dy) >= 2: 24 | rope[i][1] -= sign(dy) 25 | elif (abs(dx),abs(dy)) != (1,1): 26 | rope[i][0] -= sign(dx) 27 | rope[i][1] -= sign(dy) 28 | 29 | 30 | m = { 31 | "U":(0,1), 32 | "D":(0,-1), 33 | "R":(1,0), 34 | "L":(-1,0) 35 | } 36 | 37 | p = set() 38 | 39 | for x in s.split("\n"): 40 | dr,n = x.split(" ") 41 | n = int(n) 42 | p.add(tuple(rope[-1])) 43 | for _ in range(n): 44 | dx,dy = m[dr] 45 | rope[0][0] += dx 46 | rope[0][1] += dy 47 | for i in range(1,10): 48 | update(i) 49 | p.add(tuple(rope[-1])) 50 | 51 | print(len(p)) 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /2021/day04/day04.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n\n") 3 | 4 | nums = s[0] 5 | boards = s[1:] 6 | 7 | nums = [int(x) for x in nums.split(",")] 8 | boards = [[[int(x) for x in r.strip().split()] for r in b.split("\n")] for b in boards] 9 | 10 | def wins(b): 11 | for i in range(5): 12 | if all(b[i][j] for j in range(5)): 13 | return True 14 | if all(b[j][i] for j in range(5)): 15 | return True 16 | return False 17 | 18 | best = (0, None) 19 | for b in boards: 20 | l = {} 21 | for i in range(5): 22 | for j in range(5): 23 | l[b[i][j]] = (i,j) 24 | 25 | bm = [[False for _ in range(5)] for _ in range(5)] 26 | 27 | for z,n in enumerate(nums): 28 | if n in l: 29 | x,y = l[n] 30 | bm[x][y] = True 31 | if wins(bm): 32 | best = max(best, (z, b)) 33 | if z == 81: 34 | # sum unmarked 35 | su = 0 36 | for i in range(5): 37 | for j in range(5): 38 | if not bm[i][j]: 39 | su += b[i][j] 40 | print(su * n) 41 | break 42 | 43 | print(best) 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /2022/day15/day15_p2.py: -------------------------------------------------------------------------------- 1 | import string 2 | from collections import defaultdict 3 | from aoc_tools import * 4 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 5 | 6 | with open(r"C:\Users\Neil\Documents\AOC2022\day15\input.txt") as f: 7 | s = f.read().strip() 8 | 9 | d = [nums(r) for r in s.split("\n")] 10 | 11 | dist = lambda x1,y1,x2,y2 : abs(y2-y1) + abs(x2-x1) 12 | 13 | constraints = [] 14 | for sx,sy,bx,by in d: 15 | z = dist(sx,sy,bx,by) 16 | # must be the case that |ty-sy|+|tx-sx|>z 17 | # +,+ ty-sy+tx-sx>z 18 | # +,+ ty+tx>z+sx+sy 19 | # -,+ -ty+sy+tx-sx>z 20 | # -,+ tx-ty>z+sx-sy 21 | # +,- ty-sy-tx+sx>z 22 | # +,- -tx+ty>z-sx+sy 23 | # +,- tx-ty<-z+sx-sy 24 | # -,- -ty+sy-tx+sx>z 25 | # -,- -(tx+ty)>z-sx-sy 26 | # -,- tx+ty<-z+sx+sy 27 | constraints.append( 28 | (z + sx + sy, -z + sx + sy, z + sx - sy, -z + sx - sy) 29 | ) 30 | 31 | from z3 import * 32 | x = Int("x") 33 | y = Int("y") 34 | sumxy = x + y 35 | difxy = x - y 36 | s = Solver() 37 | for a,b,c,d in constraints: 38 | s.add(Or((sumxy > a), (sumxy < b), (difxy > c), (difxy < d))) 39 | s.add(x >= 0) 40 | s.add(x <= 4000000) 41 | s.add(y >= 0) 42 | s.add(y <= 4000000) 43 | print(s.check()) 44 | print(s.model()[x].as_long() * 4000000 + s.model()[y].as_long()) 45 | -------------------------------------------------------------------------------- /2020/day8/day8.py: -------------------------------------------------------------------------------- 1 | import regex 2 | 3 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 4 | 5 | def nums(s): 6 | m = nums_regex.match(s) 7 | vals = m.capturesdict()["nums"] 8 | return [int(x) for x in vals] 9 | 10 | with open("alt_input.txt") as f: 11 | s = f.read().strip().split("\n") 12 | 13 | ins = [] 14 | for line in s: 15 | i, v = line[:3], line[4:] 16 | if v[0] == "+": 17 | v = v[1:] 18 | v = int(v) 19 | ins.append((i,v)) 20 | 21 | 22 | def run(cins): 23 | acc = 0 24 | ex = set() 25 | 26 | cur = 0 27 | while cur not in ex and cur < len(cins): 28 | ex.add(cur) 29 | i, v = cins[cur] 30 | if i == "acc": 31 | acc += v 32 | cur += 1 33 | elif i == "jmp": 34 | cur += v 35 | elif i == "nop": 36 | cur += 1 37 | 38 | return (cur not in ex, acc) 39 | 40 | print(run(ins)[1]) 41 | 42 | for i in range(len(ins)): 43 | if ins[i][0] == "jmp": 44 | nins = ins[:i] + [("nop",ins[i][1])] + ins[i+1:] 45 | if run(nins)[0]: 46 | print(run(nins)[1]) 47 | if ins[i][0] == "nop": 48 | nins = ins[:i] + [("jmp",ins[i][1])] + ins[i+1:] 49 | if run(nins)[0]: 50 | print(run(nins)[1]) 51 | -------------------------------------------------------------------------------- /2021/day03/day03.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n") 3 | 4 | n = len(s[0]) 5 | r = "" 6 | for i in range(n): 7 | zc = 0 8 | oc = 0 9 | for line in s: 10 | if line[i] == "1": 11 | oc += 1 12 | else: 13 | zc += 1 14 | if zc > oc: 15 | r += "0" 16 | else: 17 | r += "1" 18 | 19 | r2 = "".join("1" if x == "0" else "0" for x in r) 20 | 21 | print(int(r,2)*int(r2,2)) 22 | 23 | def get_a(keep_common): 24 | cur = s[:] 25 | for i in range(n): 26 | new = [] 27 | zc = 0 28 | oc = 0 29 | for x in cur: 30 | if x[i] == "1": 31 | oc += 1 32 | else: 33 | zc += 1 34 | keep_zeros = (zc > oc) if keep_common else (oc >= zc) 35 | for x in cur: 36 | if keep_zeros: 37 | if x[i] == "0": 38 | new.append(x) 39 | else: 40 | if x[i] == "1": 41 | new.append(x) 42 | cur = new 43 | if len(cur) == 1: 44 | break 45 | return cur[0] 46 | 47 | print(int(get_a(True),2) * int(get_a(False),2)) 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /2022/day07/day07.py: -------------------------------------------------------------------------------- 1 | from aoc_tools import * 2 | 3 | with open("input.txt") as f: 4 | s = f.read().strip() 5 | 6 | files = {} 7 | 8 | folders = set() 9 | 10 | cur = [] 11 | 12 | for cmd in s.split("\n"): 13 | if cmd.startswith("$"): 14 | if cmd.startswith("$ cd"): 15 | r = cmd[5:] 16 | if r == "..": 17 | if len(cur) > 0: 18 | cur.pop(-1) 19 | elif r == "/": 20 | cur = [] 21 | else: 22 | cur.extend(r.split("/")) 23 | else: 24 | size, name = cmd.split(" ") 25 | if size == "dir": 26 | continue 27 | size = int(size) 28 | files["/".join(cur + [name])] = size 29 | folders.add("/".join(cur)) 30 | 31 | 32 | r = 0 33 | fsizes = {} 34 | for folder in folders: 35 | fsize = 0 36 | for file in files: 37 | if file.startswith(folder): 38 | fsize += files[file] 39 | if fsize <= 100000: 40 | r += fsize 41 | fsizes[folder] = fsize 42 | 43 | print(r) 44 | 45 | print(min(v for v in fsizes.values() if 70000000 - fsizes[""] + v >= 30000000)) 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /2022/day09/day09.py: -------------------------------------------------------------------------------- 1 | import string 2 | from aoc_tools import * 3 | 4 | with open("input.txt") as f: 5 | s = f.read().strip() 6 | 7 | global hx,hy,tx,ty 8 | hx,hy = 0,0 9 | tx,ty = 0,0 10 | 11 | sign = lambda x : 1 if x > 0 else ( 12 | -1 if x < 0 else 0) 13 | 14 | def updt(): 15 | global hx,hy,tx,ty 16 | dx = tx-hx 17 | dy = ty-hy 18 | if abs(dx) >= 2: 19 | tx -= sign(dx) 20 | if abs(dy) >= 2: 21 | ty -= sign(dy) 22 | 23 | def updt(): 24 | global hx,hy,tx,ty 25 | dx = tx-hx 26 | dy = ty-hy 27 | if dx == 0 or dy == 0: 28 | if abs(dx) >= 2: 29 | tx -= sign(dx) 30 | if abs(dy) >= 2: 31 | ty -= sign(dy) 32 | elif (abs(dx),abs(dy)) != (1,1): 33 | tx -= sign(dx) 34 | ty -= sign(dy) 35 | 36 | 37 | m = { 38 | "U":(0,1), 39 | "D":(0,-1), 40 | "R":(1,0), 41 | "L":(-1,0) 42 | } 43 | 44 | p = set() 45 | 46 | for x in s.split("\n"): 47 | dr,n = x.split(" ") 48 | n = int(n) 49 | p.add((tx,ty)) 50 | for _ in range(n): 51 | dx,dy = m[dr] 52 | hx += dx 53 | hy += dy 54 | updt() 55 | p.add((tx,ty)) 56 | 57 | print(len(p)) 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | -------------------------------------------------------------------------------- /2022/day05/day05.py: -------------------------------------------------------------------------------- 1 | from aoc_tools import * 2 | 3 | with open("input.txt") as f: 4 | s = f.read().strip() 5 | 6 | s = s.split("\n") 7 | 8 | stacks = [ 9 | [], 10 | list("QSWCZVFT"), 11 | list("QRB"), 12 | list("BZTQPMS"), 13 | list("DVFRQH"), 14 | list("JGLDBSTP"), 15 | list("WRTZ"), 16 | list("HQMNSFRJ"), 17 | list("RNFHW"), 18 | list("JZTQPRB") 19 | ] 20 | 21 | 22 | for l in s[10:]: 23 | l = l[5:] 24 | a,r = l.split(" from ") 25 | b,c = r.split(" to ") 26 | a=int(a) 27 | b=int(b) 28 | c=int(c) 29 | for _ in range(a): 30 | stacks[c].append(stacks[b].pop(-1)) 31 | 32 | print("".join(z[-1] for z in stacks[1:])) 33 | 34 | 35 | 36 | stacks = [ 37 | [], 38 | list("QSWCZVFT"), 39 | list("QRB"), 40 | list("BZTQPMS"), 41 | list("DVFRQH"), 42 | list("JGLDBSTP"), 43 | list("WRTZ"), 44 | list("HQMNSFRJ"), 45 | list("RNFHW"), 46 | list("JZTQPRB") 47 | ] 48 | 49 | 50 | for l in s[10:]: 51 | l = l[5:] 52 | a,r = l.split(" from ") 53 | b,c = r.split(" to ") 54 | a=int(a) 55 | b=int(b) 56 | c=int(c) 57 | stacks[c].extend(stacks[b][-a:]) 58 | del stacks[b][-a:] 59 | # for _ in range(a): 60 | # stacks[b].pop(-1) 61 | 62 | 63 | print("".join(z[-1] for z in stacks[1:])) 64 | -------------------------------------------------------------------------------- /2017/day15/day15.py: -------------------------------------------------------------------------------- 1 | import regex 2 | 3 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 4 | 5 | def nums(s): 6 | m = nums_regex.match(s) 7 | vals = m.capturesdict()["nums"] 8 | return [int(x) for x in vals] 9 | 10 | with open("input.txt") as f: 11 | s = f.read().strip().split("\n") 12 | 13 | 14 | mod = 2147483647 15 | 16 | a, = nums(s[0]) 17 | b, = nums(s[1]) 18 | 19 | a_mult = 16807 20 | b_mult = 48271 21 | 22 | a_seen = {} 23 | b_seen = {} 24 | 25 | count = 0 26 | mask = (1 << 16) - 1 27 | for i in range(40_000_000): 28 | a = (a_mult * a) % mod 29 | b = (b_mult * b) % mod 30 | if (a & mask) == (b & mask): 31 | count += 1 32 | 33 | print(count) 34 | 35 | a, = nums(s[0]) 36 | b, = nums(s[1]) 37 | 38 | count = 0 39 | num_generated = 0 40 | a_next = None 41 | b_next = None 42 | 43 | while num_generated < 5_000_000: 44 | if a_next is None: 45 | a = (a_mult * a) % mod 46 | if a & (4 - 1) == 0: 47 | a_next = a 48 | if b_next is None: 49 | b = (b_mult * b) % mod 50 | if b & (8 - 1) == 0: 51 | b_next = b 52 | if a_next is not None and b_next is not None: 53 | num_generated += 1 54 | if (a_next & mask) == (b_next & mask): 55 | count += 1 56 | a_next = b_next = None 57 | 58 | print(count) 59 | -------------------------------------------------------------------------------- /2022/day05/day05_clean.py: -------------------------------------------------------------------------------- 1 | import copy 2 | import re 3 | 4 | with open("input.txt") as f: 5 | raw_input_string = f.read().strip() 6 | 7 | initial_crate_config_string, operations_string = raw_input_string.split("\n\n") 8 | 9 | stacks = [] 10 | 11 | initial_crate_config_lines = initial_crate_config_string.split("\n") 12 | # 3 characters in between stacks 13 | for _ in range((len(initial_crate_config_lines[-1]) + 1) // 4): 14 | stacks.append([]) 15 | 16 | for line in initial_crate_config_lines[-2::-1]: 17 | for stack, crate in zip(stacks, line[1::4]): 18 | if crate != " ": 19 | stack.append(crate) 20 | 21 | operations = [] 22 | for line in operations_string.split("\n"): 23 | operations.append( 24 | tuple( 25 | map(int, re.match("^move (\\d+) from (\\d+) to (\\d+)$", line).groups()) 26 | ) 27 | ) 28 | 29 | # save a copy for part 2 30 | _stacks = copy.deepcopy(stacks) 31 | 32 | # part 1: 33 | for cnt, src, dst in operations: 34 | for _ in range(cnt): 35 | stacks[dst - 1].append(stacks[src - 1].pop(-1)) 36 | 37 | print("".join(stack[-1] for stack in stacks)) 38 | 39 | 40 | # part 2: 41 | stacks = _stacks 42 | 43 | for cnt, src, dst in operations: 44 | stacks[dst - 1].extend(stacks[src - 1][-cnt:]) 45 | del stacks[src - 1][-cnt:] 46 | 47 | print("".join(stack[-1] for stack in stacks)) 48 | -------------------------------------------------------------------------------- /2022/day06/day06.py: -------------------------------------------------------------------------------- 1 | from aoc_tools import * 2 | 3 | with open("input.txt") as f: 4 | s = f.read().strip() 5 | 6 | s1 = s 7 | i = 0 8 | while len(set(s1[:4])) != 4: 9 | s1 = s1[1:] 10 | i += 1 11 | print(i+4) 12 | 13 | s2 = s 14 | i = 0 15 | while len(set(s2[:14])) != 14: 16 | s2 = s2[1:] 17 | i += 1 18 | print(i+14) 19 | 20 | 21 | # alternate approach for part 2 22 | 23 | cur_window = {} 24 | 25 | # mjqjpqmgbljsphdztnvjfqwrcgsmlb 26 | # XXXXXXXXXXXXXX 27 | # to roll the window over one 28 | # XXXXXXXXXXXXXX (remove m, add d) 29 | 30 | # first we need to populate cur_window with the first 14 31 | for i in range(14): 32 | if s[i] not in cur_window: 33 | cur_window[s[i]] = 0 34 | cur_window[s[i]] += 1 35 | 36 | # roll until cur_window has 14 unique elements 37 | next_idx = 14 38 | while len(cur_window.keys()) < 14: 39 | # roll the window by 1 40 | 41 | # adding next_idx 42 | if s[next_idx] not in cur_window: 43 | cur_window[s[next_idx]] = 0 44 | cur_window[s[next_idx]] += 1 45 | 46 | # delete next_idx - 14 47 | cur_window[s[next_idx - 14]] -= 1 48 | 49 | if cur_window[s[next_idx - 14]] == 0: 50 | del cur_window[s[next_idx - 14]] 51 | 52 | next_idx += 1 53 | 54 | print(next_idx) 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /2020/day12/day12.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | sys.path.append(os.path.join(os.path.dirname(__file__), "..")) 3 | from aoc_lib import * 4 | 5 | s = get_input().split("\n") 6 | 7 | s = """F10 8 | N3 9 | F7 10 | R90 11 | F11""".split("\n") 12 | 13 | dirs = {"N":(0,1),"S":(0,-1),"E":(1,0),"W":(-1,0)} 14 | 15 | lookup = {0:"E",90:"N",180:"W",270:"S"} 16 | 17 | angle = 0 18 | loc = [0,0] 19 | 20 | for ln in s: 21 | v,nv = ln[0],int(ln[1:]) 22 | if v in dirs: 23 | dx,dy = dirs[v] 24 | loc[0] += nv*dx 25 | loc[1] += nv*dy 26 | else: 27 | if v == "L": 28 | angle = (angle + nv) % 360 29 | elif v == "R": 30 | angle = (angle - nv) % 360 31 | else: 32 | dx,dy = dirs[lookup[angle]] 33 | loc[0] += nv*dx 34 | loc[1] += nv*dy 35 | 36 | print(abs(loc[0])+abs(loc[1])) 37 | 38 | 39 | wp = [10,1] 40 | 41 | angle = 0 42 | loc = [0,0] 43 | 44 | for ln in s: 45 | v,nv = ln[0],int(ln[1:]) 46 | if v in dirs: 47 | dx,dy = dirs[v] 48 | wp[0] += nv*dx 49 | wp[1] += nv*dy 50 | else: 51 | if v == "L": 52 | for _ in range(nv//90): 53 | wp = [-wp[1], wp[0]] 54 | elif v == "R": 55 | for _ in range(nv//90): 56 | wp = [wp[1], -wp[0]] 57 | else: 58 | for _ in range(nv): 59 | loc[0] += wp[0] 60 | loc[1] += wp[1] 61 | 62 | print(abs(loc[0])+abs(loc[1])) 63 | -------------------------------------------------------------------------------- /2021/day09/day09.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | 4 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 5 | 6 | def nums(s): 7 | m = nums_regex.match(s) 8 | vals = m.capturesdict()["nums"] 9 | return [int(x) for x in vals] 10 | 11 | with open("input.txt") as f: 12 | s = f.read().strip().split("\n") 13 | 14 | g = [[int(x) for x in list(y)] for y in s] 15 | 16 | c = 0 17 | start = set() 18 | for i in range(len(g)): 19 | for j in range(len(g[i])): 20 | valid = True 21 | for dx,dy in ((-1,0),(1,0),(0,1),(0,-1)): 22 | nx = i + dx 23 | ny = j + dy 24 | if nx in range(len(g)) and ny in range(len(g[i])): 25 | if g[nx][ny] <= g[i][j]: 26 | valid = False 27 | break 28 | if valid: 29 | c += 1 + g[i][j] 30 | start.add((i,j)) 31 | print(c) 32 | 33 | seen={} 34 | def dfs(i,j,c): 35 | if i not in range(len(g)) or j not in range(len(g[i])): 36 | return 37 | if g[i][j] == 9: 38 | return 39 | if (i,j) in seen: 40 | return 41 | seen[i,j] = c 42 | for dx,dy in ((-1,0),(1,0),(0,1),(0,-1)): 43 | x,y = i + dx, j + dy 44 | dfs(x,y,c) 45 | 46 | cur = 0 47 | for i,j in start: 48 | dfs(i,j,cur) 49 | cur += 1 50 | 51 | sizes = [] 52 | for i in range(cur): 53 | sizes.append(sum(1 for x in seen if seen[x] == i)) 54 | 55 | sizes.sort() 56 | -------------------------------------------------------------------------------- /2022/day08/day08.py: -------------------------------------------------------------------------------- 1 | import string 2 | from aoc_tools import * 3 | 4 | with open("input.txt") as f: 5 | s = f.read().strip() 6 | 7 | g = [[int(y) for y in x] for x in s.split("\n")] 8 | n = len(g) 9 | m = len(g[0]) 10 | 11 | vis = set() 12 | for i in range(n): 13 | for j in range(m): 14 | isviz = False 15 | for dx,dy in [(0,1),(0,-1),(1,0),(-1,0)]: 16 | ni = i + dx 17 | nj = j + dy 18 | v = True 19 | while ni in range(n) and nj in range(m): 20 | if g[ni][nj] >= g[i][j]: 21 | v = False 22 | break 23 | ni += dx 24 | nj += dy 25 | if v: 26 | isviz = True 27 | break 28 | if isviz: 29 | vis.add((i, j)) 30 | 31 | print(len(vis)) 32 | 33 | r = 0 34 | 35 | for i in range(n): 36 | for j in range(m): 37 | vd = [] 38 | for dx,dy in [(0,1),(0,-1),(1,0),(-1,0)]: 39 | ni = i + dx 40 | nj = j + dy 41 | c = 0 42 | v = True 43 | while ni in range(n) and nj in range(m): 44 | if g[ni][nj] >= g[i][j]: 45 | v = False 46 | break 47 | ni += dx 48 | nj += dy 49 | c += 1 50 | vd.append(c + (1 if ni in range(n) and nj in range(m) else 0)) 51 | r = max(r, vd[0]*vd[1]*vd[2]*vd[3]) 52 | 53 | print(r) 54 | -------------------------------------------------------------------------------- /2021/day15/day15.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | import itertools 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | with open("input.txt") as f: 13 | s = f.read().strip().split("\n") 14 | 15 | g = [[int(x) for x in y] for y in s] 16 | 17 | 18 | n = len(g) 19 | m = len(g[0]) 20 | 21 | ng = [] 22 | for i in range(n): 23 | cm = g[i] 24 | nr = [] 25 | for _ in range(5): 26 | nr.extend(cm) 27 | cm = [cmx + 1 if cmx < 9 else 1 for cmx in cm] 28 | ng.append(nr) 29 | 30 | first = [ngr[:] for ngr in ng] 31 | for _ in range(4): 32 | # add 1 33 | first = [[x + 1 if x < 9 else 1 for x in r] for r in first] 34 | for row in first: 35 | ng.append(row) 36 | 37 | part2 = True 38 | if part2: 39 | g = ng 40 | n = len(ng) 41 | m = len(ng[0]) 42 | 43 | import heapq 44 | 45 | pq = [] 46 | 47 | heapq.heappush(pq, (0, (0,0))) 48 | dist = {} 49 | dist[0,0] = 0 50 | while True: 51 | d, (x,y) = heapq.heappop(pq) 52 | if (x,y) == (n-1,m-1): 53 | print(d) 54 | break 55 | for dx,dy in ((-1,0),(0,1),(0,-1),(1,0)): 56 | nx,ny = x+dx,y+dy 57 | if nx not in range(n) or ny not in range(m): 58 | continue 59 | nd = d + g[nx][ny] 60 | if (nx,ny) not in dist or nd < dist[nx,ny]: 61 | dist[nx,ny] = nd 62 | heapq.heappush(pq, (nd, (nx, ny))) 63 | -------------------------------------------------------------------------------- /2017/day22/day22.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | s = [list(x) for x in s.split("\n")] 20 | 21 | g = defaultdict(lambda : False) 22 | 23 | n,m = len(s),len(s[0]) 24 | 25 | for i in range(n): 26 | for j in range(m): 27 | if s[i][j] == "#": 28 | g[i,j] = True 29 | 30 | c = [n//2,m//2] 31 | d = 3 32 | 33 | cnt = 0 34 | for _ in range(10000): 35 | if g[tuple(c)]: 36 | d = (d+1)%4 37 | else: 38 | d = (d-1)%4 39 | cnt += 1 40 | 41 | g[tuple(c)] = not g[tuple(c)] 42 | 43 | c[0] += dirs[d][0] 44 | c[1] += dirs[d][1] 45 | 46 | print(cnt) 47 | 48 | g = defaultdict(lambda : 0) 49 | 50 | for i in range(n): 51 | for j in range(m): 52 | if s[i][j] == "#": 53 | g[i,j] = 2 54 | 55 | c = [n//2,m//2] 56 | d = 3 57 | 58 | cnt = 0 59 | 60 | for _ in range(10000000): 61 | if g[tuple(c)] == 0: 62 | d = (d-1)%4 63 | elif g[tuple(c)] == 1: 64 | cnt += 1 65 | elif g[tuple(c)] == 2: 66 | d = (d+1)%4 67 | elif g[tuple(c)] == 3: 68 | d = (d+2)%4 69 | 70 | g[tuple(c)] = (g[tuple(c)]+1)%4 71 | 72 | c[0] += dirs[d][0] 73 | c[1] += dirs[d][1] 74 | 75 | print(cnt) 76 | 77 | -------------------------------------------------------------------------------- /2017/day14/day14.py: -------------------------------------------------------------------------------- 1 | def xor(vals): 2 | out = 0 3 | for val in vals: 4 | out ^= val 5 | return out 6 | 7 | def knot_hash(s): 8 | lengths = [ord(c) for c in s] + [17,31,73,47,23] 9 | cur = list(range(256)) 10 | offset = 0 11 | skip = 0 12 | for _ in range(64): 13 | for length in lengths: 14 | cur = cur[length:] + cur[:length][::-1] 15 | cur = cur[skip:] + cur[:skip] 16 | offset = (offset + length + skip) % len(cur) 17 | skip = (skip + 1) % len(cur) 18 | offset = (-offset) % len(cur) 19 | cur = cur[offset:] + cur[:offset] 20 | return "".join(hex(xor(cur[i:i+16])+256)[3:] for i in range(0,256,16)) 21 | 22 | key = "ugkiagan" 23 | 24 | def count_bits(v): 25 | c = 0 26 | while v: 27 | c += 1 28 | v = v & (v - 1) 29 | return c 30 | 31 | hashes = [int(knot_hash(f"{key}-{i}"),16) for i in range(128)] 32 | 33 | print(sum(count_bits(khash) for khash in hashes)) 34 | 35 | grid = [list(bin(khash)[2:].rjust(128, "0")) for khash in hashes] 36 | 37 | def dfs(cur, seen): 38 | if cur[0] not in range(128) or cur[1] not in range(128): 39 | return 40 | if grid[cur[0]][cur[1]] != '1': 41 | return 42 | if cur in seen: 43 | return 44 | seen.add(cur) 45 | x,y = cur 46 | for dx,dy in [(-1,0),(1,0),(0,1),(0,-1)]: 47 | dfs((x+dx,y+dy), seen) 48 | 49 | count = 0 50 | seen = set() 51 | for i in range(128): 52 | for j in range(128): 53 | if (i,j) not in seen and grid[i][j] == '1': 54 | dfs((i,j), seen) 55 | count += 1 56 | print(count) 57 | -------------------------------------------------------------------------------- /2017/day20/day20.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | s = s.split("\n") 20 | 21 | inits = [] 22 | particles = [] 23 | 24 | for l in s: 25 | p = tuple(nums(l)) 26 | inits.append(p) 27 | particles.append(p) 28 | 29 | for _ in range(1000): 30 | np = [] 31 | for p in particles: 32 | x,y,z,vx,vy,vz,ax,ay,az = p 33 | vx += ax 34 | vy += ay 35 | vz += az 36 | x += vx 37 | y += vy 38 | z += vz 39 | np.append((x,y,z,vx,vy,vz,ax,ay,az)) 40 | particles = np 41 | 42 | print(min(range(len(particles)), key = lambda i : sum(abs(particles[i][j]) for j in range(3)))) 43 | 44 | particles = inits 45 | 46 | for _ in range(1000): 47 | np = [] 48 | col = defaultdict(lambda : 0) 49 | for p in particles: 50 | x,y,z,vx,vy,vz,ax,ay,az = p 51 | vx += ax 52 | vy += ay 53 | vz += az 54 | x += vx 55 | y += vy 56 | z += vz 57 | np.append((x,y,z,vx,vy,vz,ax,ay,az)) 58 | col[x,y,z] += 1 59 | nnp = [] 60 | for p in np: 61 | x,y,z,_,_,_,_,_,_ = p 62 | if col[x,y,z] == 1: 63 | nnp.append(p) 64 | particles = nnp 65 | 66 | print(len(particles)) 67 | -------------------------------------------------------------------------------- /2018/day-10/day_10.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | vals = [(int(line[10:16]),int(line[18:24]),int(line[36:38]),int(line[40:42])) for line in inp.split("\n")] 5 | 6 | rough_tx = sum(-x[0]/x[2] for x in vals)/len(vals) 7 | rough_ty = sum(-x[1]/x[3] for x in vals)/len(vals) 8 | 9 | def calc_coords(v,t): 10 | return [(x[0]+t*x[2],x[1]+t*x[3]) for x in v] 11 | 12 | rough_t = (rough_tx + rough_ty)/2 13 | 14 | dirs = [(0,1),(1,0),(0,-1),(-1,0),(1,1),(-1,1),(1,-1),(-1,-1)] 15 | 16 | def make_map(c): 17 | x_min = min(map(lambda x : x[0], c)) 18 | x_max = max(map(lambda x : x[0], c)) 19 | y_min = min(map(lambda x : x[1], c)) 20 | y_max = max(map(lambda x : x[1], c)) 21 | tmap = [[0 for i in range(y_max-y_min+1)] for j in range(x_max-x_min+1)] 22 | for co in c: 23 | tmap[co[0]-x_min][co[1]-y_min] += 1 24 | adj_count = 0 25 | for co in c: 26 | aco = (co[0]-x_min,co[1]-y_min) 27 | for d in dirs: 28 | aco_d = (aco[0]+d[0],aco[1]+d[1]) 29 | if aco_d[0] >= 0 and aco_d[1] >= 0 and aco_d[0] < len(tmap) and aco_d[1] < len(tmap[aco_d[0]]): 30 | if tmap[aco_d[0]][aco_d[1]] > 0: 31 | adj_count += 1 32 | break 33 | return tmap, adj_count/len(c) 34 | 35 | print("Part 1:") 36 | for t in range(int(rough_t-10),int(rough_t+10)): 37 | mp, frac = make_map(calc_coords(vals, t)) 38 | if frac == 1.0: 39 | print("\n".join("".join(" " if mp[j][i] == 0 else str(mp[j][i]) for j in range(len(mp))) for i in range(len(mp[0])))) 40 | print("Part 2:",t) 41 | 42 | -------------------------------------------------------------------------------- /2020/day4/day4.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | s = f.read().strip().split("\n\n") 3 | 4 | fields = ["byr","iyr","eyr","hgt","hcl","ecl","pid"]#,"cid"] 5 | 6 | def fdr(s,lo,hi): 7 | if len(s) != 4: 8 | return False 9 | if any(not c.isdigit() for c in s): 10 | return False 11 | return lo <= int(s) <= hi 12 | 13 | c = 0 14 | for p in s: 15 | try: 16 | p = p.replace("\n"," ").split(" ") 17 | pd = {} 18 | for f in p: 19 | pd[f[:f.find(":")]] = f[f.find(":")+1:] 20 | if not fdr(pd["byr"],1920,2002): 21 | continue 22 | if not fdr(pd["iyr"],2010,2020): 23 | continue 24 | if not fdr(pd["eyr"],2020,2030): 25 | continue 26 | hgt = pd["hgt"] 27 | if hgt[-2:] == "cm": 28 | if not (150 <= int(hgt[:-2]) <= 193): 29 | continue 30 | else: 31 | if hgt[-2:] != "in": 32 | continue 33 | if not (59 <= int(hgt[:-2]) <= 76): 34 | continue 35 | hcl = pd["hcl"] 36 | if len(hcl) != 7: 37 | continue 38 | if hcl[0] != "#": 39 | continue 40 | if any(c not in "0123456789abcdef" for c in hcl[1:]): 41 | continue 42 | if pd["ecl"] not in ["amb","blu","brn","gry","grn","hzl","oth"]: 43 | continue 44 | if len(pd["pid"]) != 9: 45 | continue 46 | if any(not c.isdigit() for c in pd["pid"]): 47 | continue 48 | c += 1 49 | except: 50 | pass 51 | #if all(f+":" in p for f in fields): 52 | # c += 1 53 | print(c) 54 | -------------------------------------------------------------------------------- /2018/day-04/day_4.py: -------------------------------------------------------------------------------- 1 | from datetime import datetime 2 | 3 | with open("input.txt") as file: 4 | inp = file.read() 5 | 6 | inp = [(datetime.strptime(line[1:17],"%Y-%m-%d %H:%M"),line[19:]) for line in inp.strip().split("\n")] 7 | inp.sort() 8 | 9 | sleep = {} 10 | cguard = None 11 | i = 0 12 | while i < len(inp): 13 | if inp[i][1].startswith("Guard #"): 14 | cguard = int(inp[i][1][7:inp[i][1].find(" ",7)]) 15 | else: 16 | if cguard not in sleep: 17 | sleep[cguard] = 0 18 | sleep[cguard] += (inp[i+1][0] - inp[i][0]).seconds//60 19 | i += 1 20 | i += 1 21 | 22 | sleepiest = max(sleep, key=sleep.get) 23 | 24 | minutes = [0 for i in range(60)] 25 | cguard = None 26 | i = 0 27 | while i < len(inp): 28 | if inp[i][1].startswith("Guard #"): 29 | cguard = int(inp[i][1][7:inp[i][1].find(" ",7)]) 30 | elif cguard == sleepiest: 31 | for j in range(inp[i][0].minute, inp[i+1][0].minute): 32 | minutes[j] += 1 33 | i += 1 34 | i += 1 35 | 36 | print("Part 1:",sleepiest * max(enumerate(minutes), key=lambda v:v[1])[0]) 37 | 38 | sleep = {} 39 | cguard = None 40 | i = 0 41 | while i < len(inp): 42 | if inp[i][1].startswith("Guard #"): 43 | cguard = int(inp[i][1][7:inp[i][1].find(" ",7)]) 44 | else: 45 | if cguard not in sleep: 46 | sleep[cguard] = [0 for i in range(60)] 47 | for j in range(inp[i][0].minute, inp[i+1][0].minute): 48 | sleep[cguard][j] += 1 49 | i += 1 50 | i += 1 51 | 52 | guard = max(sleep, key=lambda k:max(sleep[k])) 53 | print("Part 2:",guard * max(enumerate(sleep[guard]), key=lambda v:v[1])[0]) 54 | -------------------------------------------------------------------------------- /2017/day16/day16.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | with open("input.txt") as f: 13 | s = f.read().strip().split(",") 14 | 15 | import time 16 | 17 | 18 | progs = list("abcdefghijklmnop") 19 | 20 | st = time.time() 21 | 22 | for m in s: 23 | if m[0] == "s": 24 | x = int(m[1:]) 25 | progs = progs[-x:] + progs[:-x] 26 | elif m[0] == "x": 27 | a,b = map(int,m[1:].split("/")) 28 | progs[a], progs[b] = progs[b], progs[a] 29 | elif m[0] == "p": 30 | a,b = map(progs.index,m[1:].split("/")) 31 | progs[a], progs[b] = progs[b], progs[a] 32 | 33 | et = time.time() 34 | print("".join(progs)) 35 | 36 | seen = {} 37 | 38 | progs = list("abcdefghijklmnop") 39 | 40 | iters = 1_000_000_000 41 | 42 | while iters > 0: 43 | str_rep = "".join(progs) 44 | if str_rep in seen: 45 | cycle_length = seen[str_rep] - iters 46 | iters %= cycle_length 47 | seen = {} 48 | seen[str_rep] = iters 49 | for m in s: 50 | if m[0] == "s": 51 | x = int(m[1:]) 52 | progs = progs[-x:] + progs[:-x] 53 | elif m[0] == "x": 54 | a,b = map(int,m[1:].split("/")) 55 | progs[a], progs[b] = progs[b], progs[a] 56 | elif m[0] == "p": 57 | a,b = map(progs.index,m[1:].split("/")) 58 | progs[a], progs[b] = progs[b], progs[a] 59 | iters -= 1 60 | 61 | print("".join(progs)) 62 | 63 | 64 | -------------------------------------------------------------------------------- /2020/day20/ex.txt: -------------------------------------------------------------------------------- 1 | Tile 2311: 2 | ..##.#..#. 3 | ##..#..... 4 | #...##..#. 5 | ####.#...# 6 | ##.##.###. 7 | ##...#.### 8 | .#.#.#..## 9 | ..#....#.. 10 | ###...#.#. 11 | ..###..### 12 | 13 | Tile 1951: 14 | #.##...##. 15 | #.####...# 16 | .....#..## 17 | #...###### 18 | .##.#....# 19 | .###.##### 20 | ###.##.##. 21 | .###....#. 22 | ..#.#..#.# 23 | #...##.#.. 24 | 25 | Tile 1171: 26 | ####...##. 27 | #..##.#..# 28 | ##.#..#.#. 29 | .###.####. 30 | ..###.#### 31 | .##....##. 32 | .#...####. 33 | #.##.####. 34 | ####..#... 35 | .....##... 36 | 37 | Tile 1427: 38 | ###.##.#.. 39 | .#..#.##.. 40 | .#.##.#..# 41 | #.#.#.##.# 42 | ....#...## 43 | ...##..##. 44 | ...#.##### 45 | .#.####.#. 46 | ..#..###.# 47 | ..##.#..#. 48 | 49 | Tile 1489: 50 | ##.#.#.... 51 | ..##...#.. 52 | .##..##... 53 | ..#...#... 54 | #####...#. 55 | #..#.#.#.# 56 | ...#.#.#.. 57 | ##.#...##. 58 | ..##.##.## 59 | ###.##.#.. 60 | 61 | Tile 2473: 62 | #....####. 63 | #..#.##... 64 | #.##..#... 65 | ######.#.# 66 | .#...#.#.# 67 | .######### 68 | .###.#..#. 69 | ########.# 70 | ##...##.#. 71 | ..###.#.#. 72 | 73 | Tile 2971: 74 | ..#.#....# 75 | #...###... 76 | #.#.###... 77 | ##.##..#.. 78 | .#####..## 79 | .#..####.# 80 | #..#.#..#. 81 | ..####.### 82 | ..#.#.###. 83 | ...#.#.#.# 84 | 85 | Tile 2729: 86 | ...#.#.#.# 87 | ####.#.... 88 | ..#.#..... 89 | ....#..#.# 90 | .##..##.#. 91 | .#.####... 92 | ####.#.#.. 93 | ##.####... 94 | ##..#.##.. 95 | #.##...##. 96 | 97 | Tile 3079: 98 | #.#.#####. 99 | .#..###### 100 | ..#....... 101 | ######.... 102 | ####.#..#. 103 | .#...#.##. 104 | #.#####.## 105 | ..#.###... 106 | ..#....... 107 | ..#.###... -------------------------------------------------------------------------------- /2020/day21/day21.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n").split("\n") 18 | 19 | d = [] 20 | for l in s: 21 | ing, a = l.split(" (") 22 | a = a.strip("()")[len("contains "):] 23 | d.append((ing.split(" "), a.split(", "))) 24 | 25 | n = defaultdict(set) 26 | an = defaultdict(list) 27 | for i,a in d: 28 | for al in a: 29 | an[al].append(i) 30 | for ii in i: 31 | n[ii].add(al) 32 | 33 | poss = defaultdict(list) 34 | np = set() 35 | for i,a in n.items(): 36 | good = True 37 | for al in a: 38 | # does i appear in all of an[a] 39 | if all(i in anl for anl in an[al]): 40 | good = False 41 | poss[i].append(al) 42 | if good: 43 | np.add(i) 44 | 45 | c = 0 46 | for i,a in d: 47 | for ii in i: 48 | if ii in np: 49 | c += 1 50 | print(c) 51 | 52 | while any(len(v) != 1 for v in poss.values()): 53 | for k in poss: 54 | if len(poss[k]) == 1: # remove it from all others 55 | v = next(iter(poss[k])) 56 | for k2,v2 in poss.items(): 57 | if k == k2: 58 | continue 59 | if v in v2: 60 | poss[k2].remove(v) 61 | 62 | ing = list(poss.items()) 63 | ing.sort(key = lambda x : x[1]) 64 | print(",".join(x[0] for x in ing)) 65 | -------------------------------------------------------------------------------- /2022/day12/day12.py: -------------------------------------------------------------------------------- 1 | import string 2 | from aoc_tools import * 3 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 4 | 5 | with open("input.txt") as f: 6 | s = f.read().strip() 7 | 8 | 9 | g = [list(x) for x in s.split("\n")] 10 | n = len(g) 11 | m = len(g[0]) 12 | 13 | sx,sy = [(i,j) for i in range(n) for j in range(m) if g[i][j] == "S"][0] 14 | tx,ty = [(i,j) for i in range(n) for j in range(m) if g[i][j] == "E"][0] 15 | 16 | g[sx][sy] = "a" 17 | g[tx][ty] = "z" 18 | 19 | g = [[ord(c) - ord("a") for c in r] for r in g] 20 | 21 | from collections import deque 22 | 23 | dst = defaultdict(lambda : 1000000) 24 | 25 | part = 2 26 | # part 1: 27 | if part == 1: 28 | q = deque([(sx,sy)]) 29 | # part 2: 30 | else: 31 | q = deque([(i,j) for i in range(n) for j in range(m) if g[i][j] == 0]) 32 | 33 | for x,y in q: 34 | dst[x,y] = 0 35 | 36 | ans = 100000 37 | while len(q) > 0: 38 | cx,cy = q.popleft() 39 | if (cx,cy) == (tx,ty): 40 | ans = dst[tx,ty] 41 | print(ans) 42 | break 43 | for dx,dy in dirs: 44 | nx,ny = cx+dx,cy+dy 45 | if nx in range(n) and ny in range(m): 46 | if g[cx][cy] >= g[nx][ny] - 1: 47 | ndst = dst[cx,cy] + 1 48 | if ndst < dst[nx,ny]: 49 | q.append((nx,ny)) 50 | dst[nx,ny] = ndst 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /2020/day23/day23_opt.py: -------------------------------------------------------------------------------- 1 | s = "219347865" 2 | 3 | cups = list(map(int,s)) 4 | 5 | for _ in range(100): 6 | tc = cups[1:4] 7 | dest = cups[0] - 1 8 | if dest == 0: 9 | dest = 9 10 | while dest in tc: 11 | dest -= 1 12 | if dest == 0: 13 | dest = 9 14 | new_cups = cups[:1] + cups[4:] 15 | n_idx = new_cups.index(dest) 16 | new_cups = new_cups[:n_idx+1] + tc + new_cups[n_idx+1:] 17 | cups = new_cups[1:] + new_cups[:1] 18 | 19 | print("".join(map(str,cups[cups.index(1)+1:] + cups[:cups.index(1)]))) 20 | 21 | import time 22 | st = time.time() 23 | 24 | class Node: 25 | def __init__(self, val, prev=None, next=None): 26 | self.val = val 27 | self.next = next 28 | 29 | def __repr__(self): 30 | return f"({self.val})" 31 | 32 | lookup = {} 33 | 34 | nodes = [Node(int(c)) for c in s] 35 | 36 | cur = 10 37 | while len(nodes) < 1000000: 38 | nodes.append(Node(cur)) 39 | cur += 1 40 | 41 | for a,b in zip(nodes,nodes[1:]): 42 | a.next = b 43 | 44 | nodes[-1].next = nodes[0] 45 | 46 | lookup = {} 47 | for node in nodes: 48 | lookup[node.val] = node 49 | 50 | cur = nodes[0] 51 | 52 | for _ in range(10000000): 53 | a = cur.next 54 | b = a.next 55 | c = b.next 56 | cur.next = c.next 57 | used = {cur.val,a.val,b.val,c.val} 58 | cval = cur.val 59 | while cval in used: 60 | cval -= 1 61 | if cval == 0: 62 | cval = 1000000 63 | new_ins = lookup[cval] 64 | ins_nxt = new_ins.next 65 | 66 | new_ins.next = a 67 | c.next = ins_nxt 68 | 69 | cur = cur.next 70 | 71 | cup1 = lookup[1] 72 | a = cup1.next 73 | b = a.next 74 | 75 | et = time.time() 76 | 77 | print(a.val * b.val) 78 | 79 | print(round(et-st,5)) 80 | -------------------------------------------------------------------------------- /2020/day8/alt_day8.py: -------------------------------------------------------------------------------- 1 | import re 2 | 3 | filename = "alt_input.txt" 4 | 5 | with open(filename, "r") as file_reader: 6 | aoc_input = file_reader.read() 7 | codes = aoc_input.split("\n") 8 | 9 | regex_code = re.compile("^([a-z]{3}) ((\\+|-)\\d+)") 10 | 11 | for i in range(len(codes)): 12 | match_code = regex_code.match(codes[i]) 13 | codes[i] = (match_code.group(1), int(match_code.group(2))) 14 | 15 | 16 | # Part 1 17 | def handle_codes(codes): 18 | acc = 0 19 | i = 0 20 | already_visited = set() 21 | finished = False 22 | while True: 23 | if i in already_visited: 24 | break 25 | if i >= len(codes): 26 | finished = True 27 | break 28 | already_visited.add(i) 29 | 30 | code = codes[i] 31 | if code[0] == "acc": 32 | acc += code[1] 33 | i += 1 34 | if code[0] == "jmp": 35 | i += code[1] 36 | if code[0] == "nop": 37 | i += 1 38 | return finished, acc 39 | 40 | 41 | print("Part 1: Loop at acc value {}".format(handle_codes(codes)[1])) 42 | 43 | # Part 2 44 | for i in range(len(codes)): 45 | if codes[i][0] == "nop" or codes[i][0] == "jmp": 46 | copy_codes = list() 47 | 48 | for copy_code in codes: 49 | copy_codes.append(copy_code) 50 | 51 | if codes[i][0] == "nop": 52 | copy_codes.remove(copy_codes[i]) 53 | copy_codes.insert(i, ("jmp", codes[i][1])) 54 | 55 | if codes[i][0] == "jmp": 56 | copy_codes.remove(copy_codes[i]) 57 | copy_codes.insert(i, ("nop", codes[i][1])) 58 | 59 | tup = handle_codes(copy_codes) 60 | if tup[0]: 61 | print("Part 2: working Program acc value {}".format(tup[1])) 62 | -------------------------------------------------------------------------------- /2020/day22/day22.py: -------------------------------------------------------------------------------- 1 | ##from collections import defaultdict 2 | ##import functools 3 | ##import regex 4 | ##import heapq 5 | ## 6 | ##nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | ## 8 | ### N,E,S,W 9 | ##dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | ## 11 | ##def nums(s): 12 | ## m = nums_regex.match(s) 13 | ## vals = m.capturesdict()["nums"] 14 | ## return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | s = s.split("\n\n") 20 | p1 = s[0].split("\n")[1:] 21 | p2 = s[1].split("\n")[1:] 22 | p1 = list(map(int,p1)) 23 | p2 = list(map(int,p2)) 24 | 25 | while len(p1)>0 and len(p2)>0: 26 | p1a = p1.pop(0) 27 | p2a = p2.pop(0) 28 | if p1a > p2a: 29 | p1.extend([p1a,p2a]) 30 | else: 31 | p2.extend([p2a,p1a]) 32 | 33 | print(sum((i+1)*v for i,v in enumerate(p1[::-1]))) 34 | 35 | p1 = s[0].split("\n")[1:] 36 | p2 = s[1].split("\n")[1:] 37 | p1 = list(map(int,p1)) 38 | p2 = list(map(int,p2)) 39 | 40 | # p1 win = True 41 | def combat(p1,p2): 42 | seen = set() 43 | while len(p1)>0 and len(p2)>0: 44 | if (tuple(p1),tuple(p2)) in seen: 45 | return True 46 | seen.add((tuple(p1),tuple(p2))) 47 | p1a = p1.pop(0) 48 | p2a = p2.pop(0) 49 | if len(p1) >= p1a and len(p2) >= p2a: 50 | winner = combat(p1[:p1a], p2[:p2a]) 51 | if winner: 52 | p1.extend([p1a,p2a]) 53 | else: 54 | p2.extend([p2a,p1a]) 55 | else: 56 | if p1a > p2a: 57 | p1.extend([p1a,p2a]) 58 | else: 59 | p2.extend([p2a,p1a]) 60 | return len(p1)>0 61 | 62 | combat(p1,p2) 63 | print(sum((i+1)*v for i,v in enumerate(p1[::-1]))) 64 | 65 | -------------------------------------------------------------------------------- /2017/day24/day24.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | p = [] 20 | for l in s.split("\n"): 21 | p.append(tuple(map(int,l.split("/")))) 22 | 23 | lookup = defaultdict(set) 24 | for i,(a,b) in enumerate(p): 25 | lookup[a].add(i) 26 | lookup[b].add(i) 27 | 28 | def recur(cur, used): 29 | poss = [i for i in lookup[cur] if not used[i]] 30 | m = 0 31 | for pi in poss: 32 | used[pi] = True 33 | new = (p[pi][0] if p[pi][1] == cur else p[pi][1]) 34 | m = max(m, sum(p[pi]) + recur(new, used)) 35 | used[pi] = False 36 | return m 37 | 38 | def longest(cur, used): 39 | poss = [i for i in lookup[cur] if not used[i]] 40 | m = 0 41 | for pi in poss: 42 | used[pi] = True 43 | new = (p[pi][0] if p[pi][1] == cur else p[pi][1]) 44 | m = max(m, 1 + longest(new, used)) 45 | used[pi] = False 46 | return m 47 | 48 | print(recur(0, [False for _ in range(len(p))])) 49 | length = longest(0, [False for _ in range(len(p))]) 50 | 51 | def recur(cur, used): 52 | poss = [i for i in lookup[cur] if not used[i]] 53 | m = 0 54 | for pi in poss: 55 | used[pi] = True 56 | new = (p[pi][0] if p[pi][1] == cur else p[pi][1]) 57 | m = max(m, sum(p[pi]) + recur(new, used) + 10000000 * (sum(used) == length)) 58 | used[pi] = False 59 | return m 60 | 61 | print(recur(0, [False for _ in range(len(p))]) - 10000000) 62 | -------------------------------------------------------------------------------- /2021/day11/day11.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | import itertools 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | 13 | with open("input.txt") as f: 14 | s = f.read().strip().split("\n") 15 | 16 | 17 | 18 | grid = [[int(x) for x in y] for y in s] 19 | N = len(grid) 20 | M = len(grid[0]) 21 | 22 | dd = defaultdict(int) 23 | for i in range(len(grid)): 24 | for j in range(len(grid[0])): 25 | dd[i,j] = grid[i][j] 26 | 27 | def iterate(dd): 28 | nd = defaultdict(int) 29 | flash = 0 30 | flashed = set() 31 | toflash = [] 32 | for x,y in dd.keys(): 33 | nd[x,y] = dd[x,y] + 1 34 | if nd[x,y] > 9: 35 | toflash.append((x,y)) 36 | flashed.add((x,y)) 37 | while len(toflash) > 0: 38 | x,y = toflash.pop(-1) 39 | for dx in range(-1,2): 40 | for dy in range(-1,2): 41 | if (dx,dy)==(0,0): 42 | continue 43 | if x+dx not in range(N): 44 | continue 45 | if y+dy not in range(M): 46 | continue 47 | nd[x+dx,y+dy] += 1 48 | if nd[x+dx,y+dy] > 9: 49 | if (x+dx,y+dy) not in flashed: 50 | flashed.add((x+dx,y+dy)) 51 | toflash.append((x+dx,y+dy)) 52 | for x,y in flashed: 53 | nd[x,y] = 0 54 | return nd, len(flashed) 55 | 56 | c = 0 57 | while len(set(dd.values())) > 1: 58 | dd, _ = iterate(dd) 59 | c += 1 60 | print(c) 61 | #f = 0 62 | #for _ in range(100): 63 | # dd, fc = iterate(dd) 64 | # f += fc 65 | #print(f) 66 | -------------------------------------------------------------------------------- /2022/day14/day14.py: -------------------------------------------------------------------------------- 1 | import string 2 | from collections import defaultdict 3 | #from aoc_tools import * 4 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 5 | 6 | with open(r"C:\Users\Neil\Documents\AOC2022\day14\input.txt") as f: 7 | s = f.read().strip() 8 | print("\n".join(x[:60] for x in s.split("\n")[:6])) 9 | 10 | s = s.split("\n") 11 | s = [[tuple(nums(x)) for x in line.split(" -> ")] for line in s] 12 | # added for pypy 13 | #s = [[tuple(map(int,x.split(","))) for x in line.split(" -> ")] for line in s] 14 | 15 | sand = (500,0) 16 | 17 | # 0 -> air 18 | # 1 -> solid 19 | # 2 -> solid sand 20 | grid = defaultdict(lambda : 0) 21 | for line in s: 22 | for (ax,ay),(bx,by) in zip(line,line[1:]): 23 | dx = bx-ax 24 | if dx != 0: 25 | dx = dx // abs(dx) 26 | dy = by-ay 27 | if dy != 0: 28 | dy = dy // abs(dy) 29 | while (ax,ay) != (bx,by): 30 | grid[ax,ay] = 1 31 | ax += dx 32 | ay += dy 33 | grid[ax,ay] = 1 34 | 35 | maxy = max(y for x,y in grid) 36 | 37 | for x in range(-1000,1000): 38 | grid[x,maxy+2] = 1 39 | 40 | part = 2 41 | 42 | sx,sy = sand 43 | while True: 44 | blocked = True 45 | for dx,dy in ((0,1),(-1,1),(1,1)): 46 | if grid[(sx+dx,sy+dy)] == 0: 47 | sx += dx 48 | sy += dy 49 | blocked = False 50 | break 51 | if part == 1 and sy > maxy: 52 | break 53 | if blocked: 54 | grid[sx,sy] = 2 55 | if (sx,sy) == sand: 56 | break 57 | sx,sy = sand 58 | 59 | print(sum(1 for v in grid.values() if v == 2)) 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /2020/day14/day14.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | with open("input.txt") as f: 13 | s = f.read().strip().split("\n") 14 | 15 | mem = defaultdict(lambda : 0) 16 | 17 | and_mask = (1<<40)-1 18 | or_mask = 0 19 | 20 | for line in s: 21 | if line.startswith("mask ="): 22 | bits = list(line[7:]) 23 | and_mask = (1<<40)-1 24 | or_mask = 0 25 | for i,b in enumerate(bits[::-1]): 26 | if b == '0': 27 | and_mask &= ((1<<40)-1)^(1< max ? val : max; 41 | return max; 42 | } 43 | } 44 | 45 | class Node { 46 | public Node left; 47 | public Node right; 48 | public int val; 49 | 50 | public Node(Node left, Node right, int val) { 51 | this.left = left; 52 | this.right = right; 53 | this.val = val; 54 | } 55 | } -------------------------------------------------------------------------------- /2022/day11/day11.py: -------------------------------------------------------------------------------- 1 | import string 2 | from aoc_tools import * 3 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 4 | 5 | with open("input.txt") as f: 6 | s = f.read().strip() 7 | print("\n".join(x[:60] for x in s.split("\n")[:10])) 8 | 9 | 10 | s = s.split("\n\n") 11 | 12 | m = [] 13 | N = 1 14 | for x in s: 15 | lns = x.split("\n") 16 | mn = nums(lns[0])[0] 17 | st = nums(lns[1]) 18 | op = lns[2][len(" Operation: "):] 19 | tst = nums(lns[3])[0] 20 | iftrue = nums(lns[4])[0] 21 | iffalse = nums(lns[5])[0] 22 | N *= tst 23 | m.append((mn, st, op, tst, iftrue, iffalse)) 24 | 25 | mitems = [[x for x in st] for _,st,_,_,_,_ in m] 26 | counts = [0 for _ in m] 27 | 28 | # reproduced part 1 code 29 | for _ in range(20): 30 | for i in range(len(m)): 31 | mnk = m[i] 32 | for item in mitems[i]: 33 | counts[i] += 1 34 | old = item 35 | new = eval(mnk[2][5:]) 36 | if new % mnk[3] == 0: 37 | mitems[mnk[4]].append(new) 38 | else: 39 | mitems[mnk[5]].append(new) 40 | mitems[i].clear() 41 | 42 | counts.sort() 43 | print(counts[-1] * counts[-2]) 44 | 45 | mitems = [[x for x in st] for _,st,_,_,_,_ in m] 46 | counts = [0 for _ in m] 47 | 48 | for _ in range(10000): 49 | for i in range(len(m)): 50 | mnk = m[i] 51 | for item in mitems[i]: 52 | counts[i] += 1 53 | old = item 54 | new = eval(mnk[2][5:]) 55 | new %= N 56 | if new % mnk[3] == 0: 57 | mitems[mnk[4]].append(new) 58 | else: 59 | mitems[mnk[5]].append(new) 60 | mitems[i].clear() 61 | 62 | 63 | counts.sort() 64 | print(counts[-1] * counts[-2]) 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | -------------------------------------------------------------------------------- /2017/day23/day23.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | ins = s.split("\n") 20 | 21 | regs = defaultdict(lambda : 0) 22 | reg_names = "abcdefgh" 23 | 24 | #regs['a'] = 1 25 | 26 | tc = 0 27 | ic = 0 28 | mulc = 0 29 | while ic in range(len(ins)): 30 | #print(("%05d @ %02d: " % (tc,ic)) + "\t".join(str(regs[r]) for r in reg_names)) 31 | opc,x,y = ins[ic].split(" ") 32 | #if ic == 24: 33 | # print(("%08d @ %02d: " % (tc,ic)) + "\t".join(str(regs[r]) for r in reg_names)) 34 | if opc == "set": 35 | regs[x] = (regs[y] if y in reg_names else int(y)) 36 | elif opc == "sub": 37 | regs[x] -= (regs[y] if y in reg_names else int(y)) 38 | elif opc == "mul": 39 | regs[x] *= (regs[y] if y in reg_names else int(y)) 40 | mulc += 1 41 | elif opc == "jnz": 42 | if (regs[x] if x in reg_names else int(x)) != 0: 43 | ic += (regs[y] if y in reg_names else int(y)) - 1 44 | ic += 1 45 | tc += 1 46 | #if tc == 10: 47 | # regs['b'] = 12 48 | # regs['c'] = 216 49 | #print(regs['h']) 50 | print(mulc) 51 | 52 | # part 2 is counting # of composite numbers in range(109300, 126300+17, 17) 53 | primes = [] 54 | cur = 2 55 | while cur <= int(0.5 + (126300 ** 0.5)): 56 | if all(cur % p != 0 for p in primes): 57 | primes.append(cur) 58 | cur += 1 59 | 60 | out = 0 61 | for n in range(109300, 126300+17, 17): 62 | if any(n % p == 0 for p in primes): 63 | out += 1 64 | print(out) 65 | -------------------------------------------------------------------------------- /2022/day13/day13.py: -------------------------------------------------------------------------------- 1 | import string 2 | from aoc_tools import * 3 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 4 | 5 | with open("input.txt") as f: 6 | s = f.read().strip() 7 | print("\n".join(x[:60] for x in s.split("\n")[:6])) 8 | 9 | s = s.split("\n\n") 10 | s2 = [] 11 | for x in s: 12 | a,b = x.split("\n") 13 | s2.append((eval(a),eval(b))) 14 | s = s2 15 | 16 | def cmp(a,b): # -1 if a < b, 0 if a = b 17 | if type(a) is int and type(b) is int: 18 | if a < b: 19 | return -1 20 | elif a == b: 21 | return 0 22 | else: 23 | return 1 24 | elif type(a) is list and type(b) is int: 25 | b = [b] 26 | elif type(a) is int and type(b) is list: 27 | a = [a] 28 | 29 | n = len(a) 30 | m = len(b) 31 | for aa, bb in zip(a, b): 32 | r = cmp(aa, bb) 33 | if r != 0: 34 | return r 35 | if n < m: 36 | return -1 37 | elif n == m: 38 | return 0 39 | else: 40 | return 1 41 | 42 | r = 0 43 | for i,(a,b) in enumerate(s): 44 | if cmp(a,b) == -1: 45 | r += i + 1 46 | print(r) 47 | 48 | pkts = [] 49 | for a,b in s: 50 | pkts.append(a) 51 | pkts.append(b) 52 | 53 | pkts.append([[2]]) 54 | pkts.append([[6]]) 55 | 56 | for i in range(len(pkts)): 57 | for j in range(len(pkts)-1): 58 | if cmp(pkts[j], pkts[j+1]) > 0: 59 | pkts[j], pkts[j+1] = pkts[j+1], pkts[j] 60 | 61 | x, y = [i for i in range(len(pkts)) if pkts[i] == [[2]] or pkts[i] == [[6]]] 62 | print((x + 1) * (y + 1)) 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /2018/day-12/day_12.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | inp = inp.split("\n") 5 | states = inp[2:] 6 | initial_state = inp[0][15:] 7 | states = {state[:5]:state[-1:] for state in states} 8 | 9 | cur_state = initial_state 10 | left_idx = 0 11 | for _ in range(20): 12 | cur_state = "...." + cur_state + "...." 13 | new_state = "" 14 | for i in range(len(cur_state)-4): 15 | new_state += states[cur_state[i:i+5]] 16 | new_state = new_state.rstrip(".") 17 | left_idx += len(new_state) - len(new_state.lstrip(".")) - 2 18 | cur_state = new_state.lstrip(".") 19 | 20 | print("Part 1:",sum(left_idx + i for i, v in enumerate(cur_state) if v == "#")) 21 | 22 | cur_gen = 20 23 | seen_states = {} 24 | 25 | while cur_state not in seen_states: 26 | seen_states[cur_state] = (cur_gen, left_idx) 27 | cur_state = "...." + cur_state + "...." 28 | new_state = "" 29 | for i in range(len(cur_state)-4): 30 | new_state += states[cur_state[i:i+5]] 31 | new_state = new_state.rstrip(".") 32 | left_idx += len(new_state) - len(new_state.lstrip(".")) - 2 33 | cur_state = new_state.lstrip(".") 34 | cur_gen += 1 35 | 36 | cycle_length = cur_gen - seen_states[cur_state][0] 37 | left_dif = left_idx - seen_states[cur_state][1] 38 | 39 | left_idx = left_idx + left_dif * ((50000000000 - cur_gen) // cycle_length) 40 | cur_gen = cur_gen + cycle_length * ((50000000000 - cur_gen) // cycle_length) 41 | 42 | while cur_gen < 50000000000: 43 | cur_state = "...." + cur_state + "...." 44 | new_state = "" 45 | for i in range(len(cur_state)-4): 46 | new_state += states[cur_state[i:i+5]] 47 | new_state = new_state.rstrip(".") 48 | left_idx += len(new_state) - len(new_state.lstrip(".")) - 2 49 | cur_state = new_state.lstrip(".") 50 | cur_gen += 1 51 | 52 | print("Part 2:",sum(left_idx + i for i, v in enumerate(cur_state) if v == "#")) 53 | -------------------------------------------------------------------------------- /2018/day-06/day_6.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | coords = [list(map(int, line.split(","))) for line in inp.split("\n")] 5 | coords = list(map(lambda v : (v[0]+25,v[1]+25), coords)) 6 | 7 | region = [[(800,None) for i in range(400)] for j in range(400)] 8 | 9 | valid = set(range(len(coords))) 10 | 11 | in_range = lambda c : c[0] in range(400) and c[1] in range(400) 12 | 13 | def gen_dist(loc, radius): 14 | dist = [] 15 | for i in range(0,radius+1): 16 | dist.append((loc[0]+i,loc[1]+radius-i)) 17 | dist.append((loc[0]-i,loc[1]+radius-i)) 18 | dist.append((loc[0]+i,loc[1]-radius+i)) 19 | dist.append((loc[0]-i,loc[1]-radius+i)) 20 | return [d for d in set(dist) if in_range(d)] 21 | 22 | for i, co in enumerate(coords): 23 | for r in range(800): 24 | for c in gen_dist(co, r): 25 | if r < region[c[0]][c[1]][0]: 26 | region[c[0]][c[1]] = (r, i) 27 | elif r == region[c[0]][c[1]][0]: 28 | region[c[0]][c[1]] = (r, None) 29 | 30 | for i in range(400): 31 | if region[i][0][1] in valid: 32 | valid.remove(region[i][0][1]) 33 | if region[i][399][1] in valid: 34 | valid.remove(region[i][399][1]) 35 | if region[0][i][1] in valid: 36 | valid.remove(region[0][i][1]) 37 | if region[399][i][1] in valid: 38 | valid.remove(region[399][i][1]) 39 | 40 | counters = {v:0 for v in valid} 41 | 42 | for i in range(400): 43 | for j in range(400): 44 | if region[i][j][1] in counters: 45 | counters[region[i][j][1]] += 1 46 | 47 | print("Part 1:",max(counters.values())) 48 | 49 | region = [[0 for i in range(400)] for j in range(400)] 50 | 51 | for i, co in enumerate(coords): 52 | for r in range(800): 53 | for c in gen_dist(co, r): 54 | region[c[0]][c[1]] += r 55 | 56 | print("Part 2:",sum(sum(v < 10000 for v in line) for line in region)) 57 | -------------------------------------------------------------------------------- /2022/day20/day20.py: -------------------------------------------------------------------------------- 1 | import string 2 | from collections import defaultdict 3 | from aoc_tools import * 4 | import functools 5 | import sys 6 | #sys.setrecursionlimit(10000000) 7 | dirs = ((0,1),(1,0),(0,-1),(-1,0)) 8 | dirs3 = ((1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)) 9 | 10 | with open(r"input.txt") as f: 11 | s = f.read().strip() 12 | #print("\n".join(x[:60] for x in s.split("\n")[:6])) 13 | 14 | class Node: 15 | def __init__(self, value, prv=None, nxt=None): 16 | self.value = value 17 | self.prv = prv 18 | self.nxt = nxt 19 | 20 | part = 1 21 | dkey = 1 if part == 1 else 811589153 22 | 23 | r = [] 24 | for l in s.split("\n"): 25 | n = dkey * int(l) 26 | r.append(Node(n)) 27 | 28 | 29 | for a,b in zip(r,r[1:]): 30 | a.nxt = b 31 | b.prv = a 32 | 33 | r[-1].nxt = r[0] 34 | r[0].prv = r[-1] 35 | 36 | for itr in range(1 if part == 1 else 10): 37 | for x in r: 38 | x.prv.nxt = x.nxt 39 | x.nxt.prv = x.prv 40 | a,b = x.prv, x.nxt 41 | move = x.value % (len(r) - 1) 42 | for _ in range(move): 43 | a=a.nxt 44 | b=b.nxt 45 | # original code for part 1 had a separate move < 0 case 46 | # became unnecessary with mod (len(r) - 1) 47 | a.nxt = x 48 | x.prv = a 49 | b.prv = x 50 | x.nxt = b 51 | 52 | for x in r: 53 | if x.value == 0: 54 | r = 0 55 | y = x 56 | for _ in range(3): 57 | for _ in range(1000): 58 | y = y.nxt 59 | r += y.value 60 | print(r) 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /2020/day18/day18.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip().split("\n") 18 | 19 | def ev(ln): 20 | v = 0 21 | op = "+" 22 | i = 0 23 | while i < len(ln): 24 | t = ln[i] 25 | if t in "()": 26 | pc = 1 27 | j = i + 1 28 | while pc > 0: 29 | if ln[j] == "(": 30 | pc += 1 31 | elif ln[j] == ")": 32 | pc -= 1 33 | j += 1 34 | vv = ev(ln[i+1:j-1]) 35 | if op == "+": 36 | v += vv 37 | elif op == "*": 38 | v *= vv 39 | i = j - 1 40 | elif t in "+*": 41 | op = t 42 | else: 43 | if op == "+": 44 | v += int(t) 45 | elif op == "*": 46 | v *= int(t) 47 | i += 1 48 | return v 49 | 50 | ss = 0 51 | for line in s: 52 | line = line.replace("(","( ").replace(")"," )").split(" ") 53 | ss += ev(line) 54 | print(ss) 55 | 56 | 57 | class Pear: 58 | def __init__(self, s): 59 | self.v = int(s) 60 | 61 | def __add__(self, o): 62 | return Pear(self.v * o.v) 63 | 64 | def __mul__(self, o): 65 | return Pear(self.v + o.v) 66 | 67 | ss = 0 68 | for line in s: 69 | line = line.replace("+","?").replace("*","+").replace("?","*") 70 | line = line.replace("(","( ").replace(")"," )").split(" ") 71 | line = ["Pear("+x+")" if x.isdigit() else x for x in line] 72 | line = "".join(line) 73 | #print(line) 74 | ss += eval(line).v 75 | print(ss) 76 | -------------------------------------------------------------------------------- /2018/day-07/day_7.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | # both of these solutions are poor, but were fast to write 5 | # and are good enough for the given test case 6 | steps = [(line[5],line[36]) for line in inp.split("\n")] 7 | step_dict = {} 8 | for step in steps: 9 | if step[1] in step_dict: 10 | step_dict[step[1]].append(step[0]) 11 | else: 12 | step_dict[step[1]] = [step[0]] 13 | if step[0] not in step_dict: 14 | step_dict[step[0]] = [] 15 | step_dict_orig = {k:step_dict[k].copy() for k in step_dict} 16 | 17 | order = "" 18 | while len(order) < 26: 19 | next_poss = "^" 20 | for poss in step_dict: 21 | if len(step_dict[poss]) == 0: 22 | if poss < next_poss: 23 | next_poss = poss 24 | order += next_poss 25 | step_dict.pop(next_poss) 26 | for poss in step_dict: 27 | if next_poss in step_dict[poss]: 28 | step_dict[poss].remove(next_poss) 29 | 30 | print("Part 1:",order) 31 | 32 | 33 | import random 34 | 35 | step_dict = step_dict_orig 36 | 37 | # this method isn't guaranteed to work, but it's easier to write 38 | current_time = 0 39 | cur_working = [] # (letter, time_fin) 40 | while len(step_dict) > 0: 41 | i = 0 42 | while i < len(cur_working): 43 | work = cur_working[i] 44 | if work[1] == current_time: 45 | for poss in step_dict: 46 | if work[0] in step_dict[poss]: 47 | step_dict[poss].remove(work[0]) 48 | cur_working.pop(i) 49 | i -= 1 50 | i += 1 51 | to_remove = [] 52 | for poss in step_dict: 53 | if len(step_dict[poss]) == 0: 54 | if len(cur_working) < 5: 55 | to_remove.append(poss) 56 | cur_working.append((poss, current_time + ord(poss) - 4)) 57 | for d in to_remove: 58 | step_dict.pop(d) 59 | current_time = min(cur_working, key=lambda v:v[1])[1] 60 | 61 | print("Part 2:",max(cur_working, key=lambda v:v[1])[1]) 62 | 63 | -------------------------------------------------------------------------------- /2018/day-17/day_17.py: -------------------------------------------------------------------------------- 1 | import sys 2 | 3 | sys.setrecursionlimit(5000) 4 | 5 | with open("input.txt") as file: 6 | inp = file.read().strip() 7 | 8 | dat = [list(sorted(ln.replace(",","").split(" "))) for ln in inp.split("\n")] 9 | dat = [[x[2:].split("..") if ".." in x else [x[2:]] for x in pt] for pt in dat] 10 | dat = [[[int(c) for c in x] for x in pt] for pt in dat] 11 | min_x = min(d[0][0] for d in dat) 12 | max_x = max(d[0][-1] for d in dat) + 2 13 | min_y = min(d[1][0] for d in dat) 14 | max_y = max(d[1][-1] for d in dat) 15 | 16 | area = [["." for i in range(max_y+1)] for j in range(max_x-min_x+1)] 17 | for d in dat: 18 | for x_i in range(d[0][0],d[0][-1]+1): 19 | for y_i in range(d[1][0],d[1][-1]+1): 20 | area[x_i - min_x][y_i] = "#" 21 | 22 | def dfs(loc): 23 | if loc[1]+1 > max_y: 24 | area[loc[0]][loc[1]] = "|" 25 | return True 26 | 27 | if area[loc[0]][loc[1]+1] == ".": 28 | area[loc[0]][loc[1]] = "|" 29 | path = dfs((loc[0],loc[1]+1)) 30 | if path: 31 | return True 32 | elif area[loc[0]][loc[1]+1] == "|": 33 | area[loc[0]][loc[1]] = "|" 34 | return True 35 | 36 | area[loc[0]][loc[1]] = "|" 37 | left = area[loc[0]+1][loc[1]] == "." and dfs((loc[0]+1,loc[1])) 38 | right = area[loc[0]-1][loc[1]] == "." and dfs((loc[0]-1,loc[1])) 39 | if not (left or right): 40 | area[loc[0]][loc[1]] = "~" 41 | elif not left: 42 | k_idx = loc[0]+1 43 | while area[k_idx][loc[1]] == "~": 44 | area[k_idx][loc[1]] = "|" 45 | k_idx += 1 46 | return True 47 | else: 48 | k_idx = loc[0]-1 49 | while area[k_idx][loc[1]] == "~": 50 | area[k_idx][loc[1]] = "|" 51 | k_idx -= 1 52 | return True 53 | 54 | dfs((500-min_x, 0)) 55 | 56 | counts = {"~":0, "|":0} 57 | for line in area: 58 | for m in line[min_y:]: 59 | if m in "~|": 60 | counts[m] += 1 61 | 62 | print("Part 1:", counts["~"] + counts["|"]) 63 | print("Part 2:", counts["~"]) 64 | -------------------------------------------------------------------------------- /2022/day10/day10.py: -------------------------------------------------------------------------------- 1 | import string 2 | from aoc_tools import * 3 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 4 | 5 | with open("input.txt") as f: 6 | s = f.read().strip() 7 | # print("\n".join(x[:60] for x in s.split("\n")[:10])) 8 | 9 | mark_cycles = [20, 60, 100, 140, 180, 220] 10 | marked_signal_strengths = [] 11 | cycles_elapsed = 0 12 | reg = 1 13 | 14 | for line in s.split("\n"): 15 | if line.startswith("noop"): 16 | cycles_elapsed += 1 17 | if cycles_elapsed in mark_cycles: 18 | marked_signal_strengths.append(cycles_elapsed * reg) 19 | else: 20 | # add operation takes 2 cycles, only updates signal strength AFTER 21 | cycles_elapsed += 1 22 | if cycles_elapsed in mark_cycles: 23 | marked_signal_strengths.append(cycles_elapsed * reg) 24 | cycles_elapsed += 1 25 | if cycles_elapsed in mark_cycles: 26 | marked_signal_strengths.append(cycles_elapsed * reg) 27 | value = int(line.split(" ")[1]) 28 | reg += value 29 | 30 | print(sum(marked_signal_strengths)) 31 | 32 | 33 | cycles_elapsed = 0 34 | reg = 1 35 | 36 | crt = ["." for _ in range(240)] 37 | 38 | for line in s.split("\n"): 39 | if line.startswith("noop"): 40 | pixel_x = (cycles_elapsed) % 40 41 | if abs(reg - pixel_x) <= 1: 42 | crt[cycles_elapsed % 240] = "#" 43 | else: 44 | crt[cycles_elapsed % 240] = "." 45 | cycles_elapsed += 1 46 | else: 47 | pixel_x = cycles_elapsed % 40 48 | if abs(reg - pixel_x) <= 1: 49 | crt[cycles_elapsed % 240] = "#" 50 | else: 51 | crt[cycles_elapsed % 240] = "." 52 | cycles_elapsed += 1 53 | pixel_x = cycles_elapsed % 40 54 | if abs(reg - pixel_x) <= 1: 55 | crt[cycles_elapsed % 240] = "#" 56 | else: 57 | crt[cycles_elapsed % 240] = "." 58 | cycles_elapsed += 1 59 | 60 | value = int(line.split(" ")[1]) 61 | reg += value 62 | 63 | print("\n".join("".join(crt[i:i+40]) for i in range(0,240,40))) 64 | -------------------------------------------------------------------------------- /2022/day12/day12_orig.py: -------------------------------------------------------------------------------- 1 | import string 2 | from aoc_tools import * 3 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 4 | 5 | with open("input.txt") as f: 6 | s = f.read().strip() 7 | 8 | best = 10000 9 | for sx2 in range(41): 10 | for sy2 in range(81): 11 | #print("\n".join(x[:60] for x in s.split("\n")[:10])) 12 | 13 | g = [list(x) for x in s.split("\n")] 14 | n = len(g) 15 | m = len(g[0]) 16 | 17 | sx,sy = [(i,j) for i in range(n) for j in range(m) if g[i][j] == "S"][0] 18 | tx,ty = [(i,j) for i in range(n) for j in range(m) if g[i][j] == "E"][0] 19 | 20 | g[sx][sy] = "a" 21 | g[tx][ty] = "z" 22 | 23 | if g[sx2][sy2] != "a": 24 | continue 25 | 26 | g = [[ord(c) - ord("a") for c in r] for r in g] 27 | 28 | from collections import deque 29 | 30 | dst = defaultdict(lambda : 1000000) 31 | dst[sx2,sy2] = 0 32 | 33 | q = deque([(sx2,sy2)]) 34 | ans = 100000 35 | while len(q) > 0: 36 | cx,cy = q.popleft() 37 | if (cx,cy) == (tx,ty): 38 | ans = dst[tx,ty] 39 | if (sx2,sy2) == (sx,sy): 40 | print(ans) 41 | break 42 | for dx,dy in dirs: 43 | nx,ny = cx+dx,cy+dy 44 | if nx in range(n) and ny in range(m): 45 | if g[cx][cy] >= g[nx][ny] - 1: 46 | ndst = dst[cx,cy] + 1 47 | if ndst < dst[nx,ny]: 48 | q.append((nx,ny)) 49 | dst[nx,ny] = ndst 50 | best = min(best,ans) 51 | print(best) 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /2022/day18/day18.py: -------------------------------------------------------------------------------- 1 | import string 2 | from collections import defaultdict 3 | from aoc_tools import * 4 | import functools 5 | import sys 6 | sys.setrecursionlimit(10000000) 7 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 8 | 9 | with open(r"input.txt") as f: 10 | s = f.read().strip() 11 | #print("\n".join(x[:60] for x in s.split("\n")[:6])) 12 | 13 | ok = set() 14 | g = defaultdict(int) 15 | for line in s.split("\n"): 16 | t = tuple(nums(line)) 17 | g[t] = 1 18 | ok.add(t) 19 | 20 | adj = ((1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)) 21 | 22 | r = 0 23 | for nx,ny,nz in ok: 24 | bad = False 25 | for dx,dy,dz in adj: 26 | tx = nx + dx 27 | ty = ny + dy 28 | tz = nz + dz 29 | if g[tx,ty,tz] == 0: 30 | r += 1 31 | print(r) 32 | 33 | 34 | # -5..25 ish for all coords 35 | 36 | def dfs(x,y,z,c): 37 | stack = [(x,y,z)] 38 | while len(stack) > 0: 39 | x,y,z = stack.pop(-1) 40 | if x not in range(-5,25): 41 | continue 42 | if y not in range(-5,25): 43 | continue 44 | if z not in range(-5,25): 45 | continue 46 | if g[x,y,z] != 0: 47 | continue 48 | g[x,y,z] = c 49 | for dx,dy,dz in adj: 50 | stack.append((x+dx,y+dy,z+dz)) 51 | 52 | 53 | NC = 2 54 | for i in range(-1,23): 55 | for j in range(-1,23): 56 | for k in range(-1,23): 57 | dfs(i,j,k,NC) 58 | if g[i,j,k] == NC: 59 | NC += 1 60 | 61 | r = 0 62 | for nx,ny,nz in ok: 63 | bad = False 64 | for dx,dy,dz in adj: 65 | tx = nx + dx 66 | ty = ny + dy 67 | tz = nz + dz 68 | if g[tx,ty,tz] == g[-4,-4,-4]: 69 | r += 1 70 | print(r) 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /2020/day24/day24.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | s = s.split("\n") 20 | 21 | # False = White 22 | tiles = defaultdict(lambda : False) 23 | 24 | for ln in s: 25 | cur = [0,0,0] 26 | while len(ln)>0: 27 | if ln[0] == 'e' or ln[0] == 'w': 28 | if ln[0] == 'e': 29 | cur[1] += 1 30 | else: 31 | cur[1] -= 1 32 | ln = ln[1:] 33 | else: 34 | tk = ln[:2] 35 | ln = ln[2:] 36 | if tk == 'se': 37 | cur[2] += 1 38 | elif tk == 'ne': 39 | cur[0] += 1 40 | elif tk == 'nw': 41 | cur[2] -= 1 42 | elif tk == 'sw': 43 | cur[0] -= 1 44 | cur[0] += cur[1] 45 | cur[2] += cur[1] 46 | cur[1] = 0 47 | tiles[tuple(cur)] = not tiles[tuple(cur)] 48 | 49 | nbs = [(1,0,0),(-1,0,0),(0,0,1),(0,0,-1),(1,0,1),(-1,0,-1)] 50 | 51 | print(sum(tiles.values())) 52 | 53 | def upd(tiles): 54 | ntiles = defaultdict(lambda : False) 55 | 56 | cnt = defaultdict(lambda : 0) 57 | for x,y,z in tiles: 58 | if tiles[x,y,z]: 59 | for dx,dy,dz in nbs: 60 | nx,ny,nz = x+dx,y+dy,z+dz 61 | cnt[nx,ny,nz] += 1 62 | 63 | check = set(cnt.keys()).union(set(tiles.keys())) 64 | for x,y,z in check: 65 | if tiles[x,y,z]: 66 | if cnt[x,y,z] == 0 or cnt[x,y,z] > 2: 67 | ntiles[x,y,z] = False 68 | else: 69 | ntiles[x,y,z] = True 70 | else: 71 | if cnt[x,y,z] == 2: 72 | ntiles[x,y,z] = True 73 | else: 74 | pass 75 | return ntiles 76 | 77 | tiles = tiles 78 | for _ in range(100): 79 | tiles = upd(tiles) 80 | 81 | -------------------------------------------------------------------------------- /2022/day15/day15.py: -------------------------------------------------------------------------------- 1 | import string 2 | from collections import defaultdict 3 | from aoc_tools import * 4 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 5 | 6 | with open(r"C:\Users\Neil\Documents\AOC2022\day15\input.txt") as f: 7 | s = f.read().strip() 8 | #print("\n".join(x[:60] for x in s.split("\n")[:6])) 9 | 10 | d = [nums(r) for r in s.split("\n")] 11 | 12 | dist = lambda x1,y1,x2,y2 : abs(y2-y1) + abs(x2-x1) 13 | 14 | c = 0 15 | # (sum>, sum<, x-y>, x-y<) 16 | constraints = [] 17 | 18 | # if sx,sy = (5,5) 19 | # z = 3 20 | # 21 | # 2,5 => 7 22 | # 23 | # 7,6 = 13 24 | # 8,5 = 13 25 | # 9,4 = 13 26 | # 27 | 28 | for sx,sy,bx,by in d: 29 | z = dist(sx,sy,bx,by) 30 | # must be the case that |ty-sy|+|tx-sx|>z 31 | # +,+ ty-sy+tx-sx>z 32 | # +,+ ty+tx>z+sx+sy 33 | # -,+ -ty+sy+tx-sx>z 34 | # -,+ tx-ty>z+sx-sy 35 | # +,- ty-sy-tx+sx>z 36 | # +,- -tx+ty>z-sx+sy 37 | # +,- tx-ty<-z+sx-sy 38 | # -,- -ty+sy-tx+sx>z 39 | # -,- -(tx+ty)>z-sx-sy 40 | # -,- tx+ty<-z+sx+sy 41 | constraints.append( 42 | (z + sx + sy, -z + sx + sy, z + sx - sy, -z + sx - sy) 43 | ) 44 | 45 | from z3 import * 46 | x = Int("x") 47 | y = Int("y") 48 | sumxy = x + y 49 | difxy = x - y 50 | s = Solver() 51 | for a,b,c,d in constraints: 52 | s.add(Or((sumxy > a), (sumxy < b), (difxy > c), (difxy < d))) 53 | s.add(x >= 0) 54 | s.add(x <= 4000000) 55 | s.add(y >= 0) 56 | s.add(y <= 4000000) 57 | print(s.check()) 58 | print(s.model()) 59 | 60 | ###for x in range(-4619876, 4619876): 61 | ##for x in range(-9019876, 9019876): 62 | ## if x % 100_000 == 0: 63 | ## print(x) 64 | ## y = 2000000 65 | ## poss = True 66 | ## for sx,sy,bx,by in d: 67 | ## if (x,y) == (bx,by): 68 | ## poss = True 69 | ## break 70 | ## if dist(sx,sy,x,y) <= dist(sx,sy,bx,by): 71 | ## poss = False 72 | ## break 73 | ## if not poss: 74 | ## c += 1 75 | ##print(c) 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /2020/day24/nirmit_day24.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | #from tqdm import trange 3 | import re 4 | f = open('input.txt').read().strip().split("\n") 5 | dirs = [(1, 0), (.5, -.5), (-.5, -.5), (-1, 0), (-.5, .5), (.5, .5)] 6 | 7 | strs = ["e", "se", "sw", "w", "nw", "ne"] 8 | mapping = dict(zip(strs, dirs)) 9 | 10 | tiles = defaultdict(int) 11 | 12 | def evaluate_line(lined): 13 | x, y = 0, 0 14 | for d in lined: 15 | dx, dy = mapping[d] 16 | x += dx 17 | y += dy 18 | return (x, y) 19 | 20 | 21 | for line in f: 22 | qwer = [] 23 | i = 0 24 | while i < len(line): 25 | if line[i] in "ns": 26 | qwer.append(line[i:i+2]) 27 | i+= 2 28 | else: 29 | qwer.append(line[i]) 30 | i+= 1 31 | tiles[evaluate_line(qwer)] += 1 32 | 33 | print("part 1 answer: ", sum(1 for i, v in tiles.items() if v % 2 == 1)) 34 | 35 | neighbors_to_add = set((x + dx, y + dy) for dx, dy in dirs for x,y in list(tiles.keys())) 36 | for a in neighbors_to_add: 37 | if a in tiles: 38 | pass 39 | else: 40 | tiles[a] = 0 41 | 42 | print("part 2: ") 43 | 44 | import time 45 | 46 | st = time.time() 47 | 48 | def count_neighbors(psat, loc): 49 | x, y = loc 50 | blacks = 0 51 | for dx, dy in dirs: 52 | newx, newy = x + dx, y + dy 53 | 54 | if (newx, newy) in psat: 55 | if psat[(x + dx, y + dy)] % 2 == 1: 56 | blacks += 1 57 | 58 | return blacks 59 | 60 | old = tiles 61 | for i in range(100): 62 | new_day = {} 63 | for loc, v in old.items(): 64 | n = count_neighbors(old, loc) 65 | if v % 2 == 1 and (n == 0 or n > 2): 66 | new_day[loc] = v + 1 67 | elif v % 2 == 0 and n == 2: 68 | new_day[loc] = v + 1 69 | else: 70 | new_day[loc] = v 71 | 72 | neighbors_to_add = set((x + dx, y + dy) for dx, dy in dirs for x,y in list(new_day.keys()) if new_day[x,y] % 2 == 1) 73 | for a in neighbors_to_add: 74 | if a in new_day: 75 | pass 76 | else: 77 | new_day[a] = 0 78 | old = new_day 79 | 80 | print(time.time() - st) 81 | print("answer: ", sum(1 for i, v in old.items() if v % 2 == 1)) 82 | -------------------------------------------------------------------------------- /2017/day21/day21.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n").split("\n") 18 | 19 | rules = {} 20 | for ln in s: 21 | l,r = ln.split(" => ") 22 | rules[tuple(l.split("/"))] = r.split("/") 23 | 24 | def flip(g): 25 | return [r[::-1] for r in g] 26 | 27 | def rot(g): 28 | n = len(g) 29 | m = len(g[0]) 30 | out = [[] for _ in range(m)] 31 | for i in range(len(g)): 32 | for j in range(len(g[0])-1,-1,-1): 33 | out[len(g[0]) - j - 1].append(g[i][j]) 34 | return out 35 | 36 | def apply(block): 37 | for b1 in (block,flip(block)): 38 | for b2 in (b1,rot(b1),rot(rot(b1)),rot(rot(rot(b1)))): 39 | b2 = tuple("".join(r) for r in b2) 40 | if b2 in rules: 41 | return rules[b2] 42 | 43 | 44 | cur = tuple(".#./..#/###".split("/")) 45 | for _ in range(18): 46 | #print() 47 | #print("\n".join(cur)) 48 | w = len(cur) 49 | if w % 2 == 0: 50 | nw = 3*(w//2) 51 | new = ["" for _ in range(nw)] 52 | for i in range(0,w,2): 53 | for j in range(0,w,2): 54 | block = tuple("".join(cur[i+di][j+dj] for dj in range(2)) \ 55 | for di in range(2)) 56 | new_block = apply(block) 57 | for k in range(3): 58 | new[3*(i//2)+k] += new_block[k] 59 | else: 60 | nw = 4*(w//3) 61 | new = ["" for _ in range(nw)] 62 | for i in range(0,w,3): 63 | for j in range(0,w,3): 64 | block = tuple("".join(cur[i+di][j+dj] for dj in range(3)) \ 65 | for di in range(3)) 66 | new_block = apply(block) 67 | for k in range(4): 68 | new[4*(i//3)+k] += new_block[k] 69 | cur = new 70 | 71 | print(sum(1 for r in cur for c in r if c == "#")) 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /2020/day24/day24_clean.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | s = s.split("\n") 20 | 21 | # False = White 22 | tiles = defaultdict(lambda : False) 23 | 24 | for ln in s: 25 | cur = [0,0] 26 | while len(ln)>0: 27 | if ln[0] == 'e' or ln[0] == 'w': 28 | if ln[0] == 'e': 29 | cur[0] += 1 30 | cur[1] += 1 31 | else: 32 | cur[0] -= 1 33 | cur[1] -= 1 34 | ln = ln[1:] 35 | else: 36 | tk = ln[:2] 37 | ln = ln[2:] 38 | if tk == 'se': 39 | cur[1] += 1 40 | elif tk == 'ne': 41 | cur[0] += 1 42 | elif tk == 'nw': 43 | cur[1] -= 1 44 | elif tk == 'sw': 45 | cur[0] -= 1 46 | tiles[tuple(cur)] = not tiles[tuple(cur)] 47 | 48 | 49 | nbs = [(1,0),(-1,0),(0,1),(0,-1),(1,1),(-1,-1)] 50 | 51 | print(sum(tiles.values())) 52 | 53 | def upd(tiles): 54 | ntiles = defaultdict(lambda : False) 55 | 56 | cnt = defaultdict(lambda : 0) 57 | for x,y in tiles: 58 | if tiles[x,y]: 59 | for dx,dy in nbs: 60 | nx,ny = x+dx,y+dy 61 | cnt[nx,ny] += 1 62 | 63 | check = set(cnt.keys()).union(set(tiles.keys())) 64 | for x,y in check: 65 | if tiles[x,y]: 66 | if cnt[x,y] == 0 or cnt[x,y] > 2: 67 | #pass 68 | ntiles[x,y] = False 69 | else: 70 | ntiles[x,y] = True 71 | else: 72 | if cnt[x,y] == 2: 73 | ntiles[x,y] = True 74 | else: 75 | #pass 76 | ntiles[x,y] = False 77 | return ntiles 78 | 79 | import time 80 | 81 | st = time.time() 82 | tiles = tiles 83 | for _ in range(100): 84 | tiles = upd(tiles) 85 | 86 | print(time.time()-st) 87 | print(sum(tiles.values())) 88 | -------------------------------------------------------------------------------- /2018/day-18/day_18.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | mat = [list(line) for line in inp.split("\n")] 5 | 6 | in_range = lambda c : c[0] in range(len(mat)) and c[1] in range(len(mat[c[0]])) 7 | dirs = [(i,j) for i in range(-1,2) for j in range(-1,2) if i != 0 or j != 0] 8 | 9 | inf = [[[0,0,0] for c in line] for line in mat] 10 | 11 | def update(v, adj): 12 | if v == ".": 13 | if adj[1] >= 3: 14 | return "|" 15 | elif v == "|": 16 | if adj[2] >= 3: 17 | return "#" 18 | elif v == "#": 19 | if adj[2] == 0 or adj[1] == 0: 20 | return "." 21 | return v 22 | 23 | for time in range(10): 24 | for i in range(len(mat)): 25 | for j in range(len(mat[i])): 26 | inf[i][j] = [0,0,0] 27 | for i in range(len(mat)): 28 | for j in range(len(mat[i])): 29 | idx = ".|#".find(mat[i][j]) 30 | for d in dirs: 31 | if in_range((i+d[0],j+d[1])): 32 | inf[i+d[0]][j+d[1]][idx] += 1 33 | for i in range(len(mat)): 34 | for j in range(len(mat[i])): 35 | mat[i][j] = update(mat[i][j], inf[i][j]) 36 | time += 1 37 | 38 | mats = "".join("".join(row) for row in mat) 39 | print("Part 1:", sum(c == "|" for c in mats) * sum(c == "#" for c in mats)) 40 | 41 | seen_states = {"".join("".join(row) for row in mat):0} 42 | 43 | while time < 1000000000: 44 | for i in range(len(mat)): 45 | for j in range(len(mat[i])): 46 | inf[i][j] = [0,0,0] 47 | for i in range(len(mat)): 48 | for j in range(len(mat[i])): 49 | idx = ".|#".find(mat[i][j]) 50 | for d in dirs: 51 | if in_range((i+d[0],j+d[1])): 52 | inf[i+d[0]][j+d[1]][idx] += 1 53 | for i in range(len(mat)): 54 | for j in range(len(mat[i])): 55 | mat[i][j] = update(mat[i][j], inf[i][j]) 56 | time += 1 57 | mats = "".join("".join(row) for row in mat) 58 | if mats in seen_states: 59 | break 60 | seen_states[mats] = time 61 | seen_states[time] = mats 62 | 63 | cycle_length = time - seen_states[mats] 64 | mats = seen_states[time - cycle_length + ((1000000000 - time) % cycle_length)] 65 | 66 | print("Part 2:", sum(c == "|" for c in mats) * sum(c == "#" for c in mats)) 67 | -------------------------------------------------------------------------------- /2018/day-22/day_22.py: -------------------------------------------------------------------------------- 1 | from heapq import * 2 | 3 | with open("input.txt") as file: 4 | inp = file.read().strip().split("\n") 5 | 6 | ## This might not have been enough if the path goes really far into positive 7 | ## x, but in practice it works fine 8 | xf = 10 9 | yf = 2 10 | 11 | depth = int(inp[0][7:]) 12 | target = tuple(int(c) for c in inp[1][8:].split(",")) 13 | 14 | region = [[0 for _ in range(yf*(target[1]+1))] for _ in range(xf*(target[0]+1))] 15 | geolog = [[0 for _ in range(yf*(target[1]+1))] for _ in range(xf*(target[0]+1))] 16 | erosio = [[0 for _ in range(yf*(target[1]+1))] for _ in range(xf*(target[0]+1))] 17 | 18 | for i in range(xf*(target[0]+1)): 19 | for j in range(yf*(target[1]+1)): 20 | if i == 0 and j == 0: 21 | geolog[i][j] = 0 22 | elif i == target[0] and j == target[1]: 23 | geolog[i][j] = 0 24 | elif j == 0: 25 | geolog[i][j] = i * 16807 26 | elif i == 0: 27 | geolog[i][j] = j * 48271 28 | else: 29 | geolog[i][j] = erosio[i-1][j] * erosio[i][j-1] 30 | 31 | erosio[i][j] = (geolog[i][j] + depth) % 20183 32 | region[i][j] = erosio[i][j] % 3 33 | 34 | risk_level = sum(sum(row[:target[1]+1]) for row in region[:target[0]+1]) 35 | print("Part 1:",risk_level) 36 | 37 | in_range = lambda c : c[0] in range(len(region)) and c[1] in range(len(region[0])) 38 | h = lambda c : abs(target[0]-c[0])+abs(target[1]-c[1]) 39 | dirs = [(1,0),(0,1),(-1,0),(0,-1)] 40 | pq = [] 41 | best = {((0,0),1):0} 42 | heappush(pq, (0,0,(0,0),1) ) 43 | dist_to_target = 10000 44 | while len(pq) > 0: 45 | cur = heappop(pq) 46 | if cur[2:] == (target, 1): 47 | break 48 | print(cur) 49 | for d in dirs: 50 | nc = (cur[2][0] + d[0], cur[2][1] + d[1]) 51 | if in_range(nc) and region[nc[0]][nc[1]] != cur[3]: 52 | if (nc, cur[3]) not in best or cur[1] + 1 < best[(nc, cur[3])]: 53 | best[(nc, cur[3])] = cur[1] + 1 54 | heappush(pq, (cur[1] + 1 + h(nc), cur[1] + 1, nc, cur[3])) 55 | 56 | new_tool = 3 - region[cur[2][0]][cur[2][1]] - cur[3] 57 | if (cur[2], new_tool) not in best or cur[1] + 7 < best[(cur[2], new_tool)]: 58 | best[(cur[2], new_tool)] = cur[1] + 7 59 | heappush(pq, (cur[0] + 7, cur[1] + 7, cur[2], new_tool)) 60 | 61 | print("Part 2:",cur[1]) 62 | -------------------------------------------------------------------------------- /2020/day23/day23.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | #import regex 4 | import heapq 5 | 6 | #nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | s = "219347865" 17 | 18 | cups = list(map(int,s)) 19 | 20 | for _ in range(100): 21 | tc = cups[1:4] 22 | dest = cups[0] - 1 23 | if dest == 0: 24 | dest = 9 25 | while dest in tc: 26 | dest -= 1 27 | if dest == 0: 28 | dest = 9 29 | new_cups = cups[:1] + cups[4:] 30 | n_idx = new_cups.index(dest) 31 | new_cups = new_cups[:n_idx+1] + tc + new_cups[n_idx+1:] 32 | cups = new_cups[1:] + new_cups[:1] 33 | 34 | print("".join(map(str,cups[cups.index(1)+1:] + cups[:cups.index(1)]))) 35 | 36 | import time 37 | st = time.time() 38 | 39 | class Node: 40 | def __init__(self, val, prev=None, next=None): 41 | self.val = val 42 | self.prev = prev 43 | self.next = next 44 | 45 | def __repr__(self): 46 | return f"({self.val})" 47 | 48 | lookup = {} 49 | 50 | nodes = [Node(int(c)) for c in s] 51 | 52 | cur = 10 53 | while len(nodes) < 1000000: 54 | nodes.append(Node(cur)) 55 | cur += 1 56 | 57 | for a,b in zip(nodes,nodes[1:]): 58 | a.next = b 59 | b.prev = a 60 | 61 | nodes[0].prev = nodes[-1] 62 | nodes[-1].next = nodes[0] 63 | 64 | lookup = {} 65 | for node in nodes: 66 | lookup[node.val] = node 67 | 68 | cur = nodes[0] 69 | 70 | for _ in range(10000000): 71 | a = cur.next 72 | b = a.next 73 | c = b.next 74 | cur.next = c.next 75 | c.next.prev = cur 76 | used = {cur.val,a.val,b.val,c.val} 77 | cval = cur.val 78 | while cval in used: 79 | cval -= 1 80 | if cval == 0: 81 | cval = 1000000 82 | new_ins = lookup[cval] 83 | ins_nxt = new_ins.next 84 | 85 | new_ins.next = a 86 | a.prev = new_ins 87 | c.next = ins_nxt 88 | ins_nxt.prev = c 89 | 90 | cur = cur.next 91 | 92 | cup1 = lookup[1] 93 | a = cup1.next 94 | b = a.next 95 | 96 | et = time.time() 97 | 98 | print(a.val * b.val) 99 | 100 | print(round(et-st,5)) 101 | -------------------------------------------------------------------------------- /2018/day-14/Day14.java: -------------------------------------------------------------------------------- 1 | import java.util.Scanner; 2 | import java.io.File; 3 | 4 | public class Day14 { 5 | public static void main(String[] args) { 6 | int[] starting = new int[] {3, 7}; 7 | Node tail = new Node(null, starting[0]); 8 | Node head = tail; 9 | for (int i = 1; i < starting.length; i++) { 10 | tail = (tail.next = new Node(null, starting[i])); 11 | } 12 | tail.next = head; 13 | 14 | Node elf1 = head; 15 | Node elf2 = head.next; 16 | 17 | int target = 84601; // 084601 18 | 19 | int rolling = 37; 20 | 21 | String part1 = ""; 22 | 23 | int numRecipes = starting.length; 24 | 25 | while(true) { 26 | int elf1val = elf1.val; 27 | int elf2val = elf2.val; 28 | int sum = elf1val + elf2val; 29 | 30 | if (sum > 9) { 31 | tail = (tail.next = new Node(head, 1)); 32 | numRecipes++; 33 | 34 | rolling = ((rolling * 10) + tail.val) % 1000000; 35 | if (rolling == target) { 36 | System.out.println("Part 2: " + (numRecipes-6)); 37 | break; 38 | } 39 | 40 | if (numRecipes > target && numRecipes < (target + 11)) { 41 | part1 += tail.val; 42 | } 43 | } 44 | tail = (tail.next = new Node(head, sum%10)); 45 | numRecipes++; 46 | 47 | rolling = ((rolling * 10) + tail.val) % 1000000; 48 | if (rolling == target) { 49 | System.out.println("Part 2: " + (numRecipes-6)); 50 | break; 51 | } 52 | 53 | if (numRecipes > target && numRecipes < (target + 11)) { 54 | part1 += tail.val; 55 | } 56 | 57 | for (int j = 0; j < 1 + elf1val; j++) elf1 = elf1.next; 58 | for (int j = 0; j < 1 + elf2val; j++) elf2 = elf2.next; 59 | } 60 | 61 | System.out.println("Part 1: " + part1); 62 | } 63 | } 64 | 65 | class Node { 66 | public Node next; 67 | public int val; 68 | 69 | public Node(Node next, int val) { 70 | this.next = next; 71 | this.val = val; 72 | } 73 | } -------------------------------------------------------------------------------- /2020/day16/day16.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip().split("\n\n") 18 | 19 | N = 20 20 | 21 | aa,bb,cc = s 22 | 23 | lookup = {} 24 | ii = 0 25 | ranges = [] 26 | for line in aa.strip().split("\n"): 27 | k,v = line.split(": ") 28 | v1,v2 = v.split(" or ") 29 | v11,v12 = map(int,v1.split("-")) 30 | v21,v22 = map(int,v2.split("-")) 31 | #ranges[k] = ((v11,v12),(v21,v22)) 32 | ranges.append((v11,v12,v21,v22)) 33 | lookup[k] = ii 34 | ii += 1 35 | 36 | def any_valid(v): 37 | return any(a <= v <= b or c <= v <= d for a,b,c,d in ranges) 38 | 39 | good = [] 40 | err = 0 41 | for tick in cc.split("\n")[1:]: 42 | tick = list(map(int,tick.split(","))) 43 | for val in tick: 44 | if not any_valid(val): 45 | err += val 46 | if all(any_valid(v) for v in tick): 47 | good.append(tick) 48 | good.append(list(map(int,bb.split("\n")[1].split(",")))) 49 | 50 | print(err) 51 | 52 | positions = [] 53 | for i,field in enumerate(ranges): 54 | a,b,c,d = field 55 | valid = set(range(N)) 56 | for tick in good: 57 | for j in range(N): 58 | if not (a <= tick[j] <= b or c <= tick[j] <= d): 59 | if j in valid: 60 | valid.remove(j) 61 | positions.append(valid) 62 | 63 | vals = [-1 for _ in range(N)] 64 | 65 | def assign(i,v): 66 | vals[i] = v 67 | for p in positions: 68 | if v in p: 69 | p.remove(v) 70 | 71 | def show(): 72 | for i in range(N): 73 | print(i,":",positions[i]) 74 | 75 | while -1 in vals: 76 | for i in range(N): 77 | if len(positions[i]) == 1: 78 | v = next(iter(positions[i])) 79 | assign(i,v) 80 | 81 | for tick in good: 82 | for i in range(N): 83 | a,b,c,d = ranges[i] 84 | fn = vals[i] 85 | if not (a <= tick[fn] <= b or c <= tick[fn] <= d): 86 | print(tick,i) 87 | 88 | out = 1 89 | for k in lookup: 90 | if k.startswith("departure"): 91 | idx = vals[lookup[k]] 92 | out *= good[-1][idx] 93 | print(out) 94 | 95 | 96 | 97 | 98 | -------------------------------------------------------------------------------- /2018/day-19/day_19.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | with open("input.txt") as file: 4 | inp = file.read().strip() 5 | 6 | prog = inp.split("\n") 7 | prog = [c.split(" ") for c in prog] 8 | prog = [[c[0],*map(int,c[1:])] for c in prog] 9 | 10 | def addr(reg,a,b,c): 11 | reg[c] = reg[a] + reg[b] 12 | 13 | def addi(reg,a,b,c): 14 | reg[c] = reg[a] + b 15 | 16 | def mulr(reg,a,b,c): 17 | reg[c] = reg[a] * reg[b] 18 | 19 | def muli(reg,a,b,c): 20 | reg[c] = reg[a] * b 21 | 22 | def banr(reg,a,b,c): 23 | reg[c] = reg[a] & reg[b] 24 | 25 | def bani(reg,a,b,c): 26 | reg[c] = reg[a] & b 27 | 28 | def borr(reg,a,b,c): 29 | reg[c] = reg[a] | reg[b] 30 | 31 | def bori(reg,a,b,c): 32 | reg[c] = reg[a] | b 33 | 34 | def setr(reg,a,b,c): 35 | reg[c] = reg[a] 36 | 37 | def seti(reg,a,b,c): 38 | reg[c] = a 39 | 40 | def gtir(reg,a,b,c): 41 | if a > reg[b]: 42 | reg[c] = 1 43 | else: 44 | reg[c] = 0 45 | 46 | def gtri(reg,a,b,c): 47 | if reg[a] > b: 48 | reg[c] = 1 49 | else: 50 | reg[c] = 0 51 | 52 | def gtrr(reg,a,b,c): 53 | if reg[a] > reg[b]: 54 | reg[c] = 1 55 | else: 56 | reg[c] = 0 57 | 58 | def eqir(reg,a,b,c): 59 | if a == reg[b]: 60 | reg[c] = 1 61 | else: 62 | reg[c] = 0 63 | 64 | def eqri(reg,a,b,c): 65 | if reg[a] == b: 66 | reg[c] = 1 67 | else: 68 | reg[c] = 0 69 | 70 | def eqrr(reg,a,b,c): 71 | if reg[a] == reg[b]: 72 | reg[c] = 1 73 | else: 74 | reg[c] = 0 75 | 76 | ops = [addr, addi, mulr, muli, banr, bani, 77 | borr, bori, setr, seti, gtir, gtri, 78 | gtrr, eqir, eqri, eqrr] 79 | op_dict = {op.__name__:op for op in ops} 80 | 81 | reg = [0,0,0,0,0,0] 82 | ip = 0 83 | ip_reg = prog[0][1] 84 | prog = prog[1:] 85 | while ip in range(len(prog)): 86 | reg[ip_reg] = ip 87 | op_dict[prog[ip][0]](reg, *prog[ip][1:]) 88 | ip = reg[ip_reg] 89 | ip += 1 90 | 91 | print("Part 1:",reg[0]) 92 | 93 | reg = [1,0,0,0,0,0] 94 | ip = 0 95 | for k in range(30): # enough to generate the input 96 | reg[ip_reg] = ip 97 | op_dict[prog[ip][0]](reg, *prog[ip][1:]) 98 | ip = reg[ip_reg] 99 | ip += 1 100 | 101 | acc = 0 102 | n = max(reg) # janky, but works 103 | # in practice I identified this by hand 104 | 105 | for k in range(1,n+1): 106 | if n % k == 0: 107 | acc += k 108 | print("Part 2:",acc) 109 | -------------------------------------------------------------------------------- /2018/day-24/input.txt: -------------------------------------------------------------------------------- 1 | Immune System: 2 | 1432 units each with 7061 hit points (immune to cold; weak to bludgeoning) with an attack that does 41 slashing damage at initiative 17 3 | 3387 units each with 9488 hit points (weak to bludgeoning) with an attack that does 27 slashing damage at initiative 20 4 | 254 units each with 3249 hit points (immune to fire) with an attack that does 89 cold damage at initiative 1 5 | 1950 units each with 8201 hit points with an attack that does 39 fire damage at initiative 15 6 | 8137 units each with 3973 hit points (weak to slashing; immune to radiation) with an attack that does 4 radiation damage at initiative 6 7 | 4519 units each with 7585 hit points (weak to fire) with an attack that does 15 radiation damage at initiative 8 8 | 763 units each with 7834 hit points (immune to radiation, slashing, cold; weak to fire) with an attack that does 91 radiation damage at initiative 18 9 | 935 units each with 10231 hit points (immune to slashing, cold) with an attack that does 103 bludgeoning damage at initiative 12 10 | 4557 units each with 7860 hit points (immune to slashing) with an attack that does 15 slashing damage at initiative 11 11 | 510 units each with 7363 hit points (weak to fire, radiation) with an attack that does 143 fire damage at initiative 5 12 | 13 | Infection: 14 | 290 units each with 29776 hit points (weak to cold, radiation) with an attack that does 204 bludgeoning damage at initiative 16 15 | 7268 units each with 14114 hit points (immune to radiation; weak to bludgeoning) with an attack that does 3 bludgeoning damage at initiative 19 16 | 801 units each with 5393 hit points with an attack that does 13 slashing damage at initiative 13 17 | 700 units each with 12182 hit points with an attack that does 29 cold damage at initiative 4 18 | 531 units each with 16607 hit points (immune to slashing) with an attack that does 53 bludgeoning damage at initiative 10 19 | 23 units each with 24482 hit points (weak to cold, fire) with an attack that does 2095 bludgeoning damage at initiative 7 20 | 8025 units each with 43789 hit points (weak to cold; immune to radiation) with an attack that does 8 radiation damage at initiative 9 21 | 1405 units each with 53896 hit points with an attack that does 70 slashing damage at initiative 14 22 | 566 units each with 7820 hit points (immune to cold) with an attack that does 26 cold damage at initiative 2 23 | 1641 units each with 7807 hit points (weak to fire; immune to slashing, bludgeoning) with an attack that does 7 radiation damage at initiative 3 24 | -------------------------------------------------------------------------------- /2018/day-13/day_13.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read()[:-1] 3 | 4 | mat = [list(line) for line in inp.split("\n")] 5 | carts = [] 6 | cart_types = {"^":"|","v":"|",">":"-","<":"-"} 7 | dir_types = {"^":(0,-1),"v":(0,1),">":(1,0),"<":(-1,0)} 8 | dir_change = {"^":"<^>","v":">v<",">":"^>v","<":"v<^"} 9 | dir_tch = {"^/" :">", ">/":"^", "v/":"<", "\\":"v","v\\":">","<\\":"^"} 11 | 12 | for y,line in enumerate(mat): 13 | for x,c in enumerate(line): 14 | if c in cart_types: 15 | carts.append([c,0,x,y]) 16 | 17 | for cart in carts: 18 | mat[cart[3]][cart[2]] = cart_types[cart[0]] 19 | 20 | already_moved = [] 21 | not_moved = carts 22 | 23 | ich = 0 24 | first_col = True 25 | while True: 26 | ich += 1 27 | not_moved.sort(key=lambda c : 100000*c[3] + c[2]) 28 | col_coords = {} 29 | for cart in not_moved: 30 | col_coords[(cart[2],cart[3])] = None 31 | while len(not_moved) > 0: 32 | cart = not_moved.pop(0) 33 | col_coords.pop((cart[2],cart[3])) 34 | cart[2] += dir_types[cart[0]][0] 35 | cart[3] += dir_types[cart[0]][1] 36 | if mat[cart[3]][cart[2]] in "/\\": 37 | cart[0] = dir_tch[cart[0]+mat[cart[3]][cart[2]]] 38 | elif mat[cart[3]][cart[2]] == "+": 39 | cart[0] = dir_change[cart[0]][cart[1]%3] 40 | cart[1] += 1 41 | has_col = False 42 | if (cart[2],cart[3]) in col_coords: 43 | if first_col: 44 | print("Part 1:",*cart[2:]) 45 | first_col = False 46 | has_col = True 47 | if not has_col: 48 | col_coords[(cart[2],cart[3])] = None 49 | already_moved.append(cart) 50 | else: 51 | loc_col = False 52 | i = 0 53 | while i < len(not_moved) and not loc_col: 54 | if not_moved[i][2:] == cart[2:]: 55 | loc_col = True 56 | not_moved.pop(i) 57 | i += 1 58 | if not loc_col: 59 | i = 0 60 | while i < len(already_moved) and not loc_col: 61 | if already_moved[i][2:] == cart[2:]: 62 | loc_col = True 63 | already_moved.pop(i) 64 | i += 1 65 | 66 | not_moved = already_moved 67 | already_moved = [] 68 | 69 | if len(not_moved) == 1: 70 | print("Part 2:",*not_moved[0][2:]) 71 | break 72 | -------------------------------------------------------------------------------- /2022/day24/day24.py: -------------------------------------------------------------------------------- 1 | import string 2 | from collections import defaultdict 3 | from aoc_tools import * 4 | import functools 5 | import sys 6 | sys.setrecursionlimit(100000) 7 | dirs = ((0,1),(1,0),(0,-1),(-1,0)) 8 | dirs3 = ((1,0,0),(-1,0,0),(0,1,0),(0,-1,0),(0,0,1),(0,0,-1)) 9 | 10 | with open(r"input.txt") as f: 11 | s = f.read()[:-1]#.strip() 12 | print("\n".join(x[:60] for x in s.split("\n")[:6])) 13 | 14 | g = [list(x) for x in s.split("\n")] 15 | N = len(g) - 2 16 | M = len(g[0]) - 2 17 | 18 | m = {"<":(0,-1), 19 | ">":(0,1), 20 | "v":(1,0), 21 | "^":(-1,0)} 22 | 23 | blizz_locs = [ 24 | [i, j, m[g[i][j]]] 25 | for i in range(1,len(g)-1) 26 | for j in range(1,len(g[0])-1) 27 | if g[i][j] != "." 28 | ] 29 | 30 | def nxt(): 31 | for x in blizz_locs: 32 | i, j, (di, dj) = x 33 | ni = i + di 34 | nj = j + dj 35 | ni = ((ni - 1) % N) + 1 36 | nj = ((nj - 1) % M) + 1 37 | x[0] = ni 38 | x[1] = nj 39 | 40 | 41 | occupied = set() 42 | st = (0,1) 43 | gl = (26,120) 44 | dist = defaultdict(lambda : float("inf")) 45 | q = deque() 46 | q.append((st, False, False, 0)) 47 | dist[st] = 0 48 | dups = set() 49 | maxdist = -1 50 | while True: 51 | (cx,cy),r1,r2,cd = q.popleft() 52 | if (cx,cy)==gl and r1 and r2: 53 | print(cx,cy,cd) 54 | break 55 | if cd > maxdist: 56 | #print(cd,maxdist) 57 | maxdist = cd 58 | nxt() 59 | occupied = set() 60 | for i,j,_ in blizz_locs: 61 | occupied.add((i,j)) 62 | for dx,dy in dirs + ((0,0),): 63 | nx,ny = cx + dx, cy + dy 64 | if (nx,ny) not in occupied and nx in range(1,1+N) and ny in range(1,1+M) or \ 65 | (nx,ny) == st or (nx,ny) == gl: 66 | if (nx,ny,r1,r2,cd) not in dups: 67 | dups.add((nx,ny,r1,r2,cd)) 68 | if (nx,ny) == gl: 69 | q.append(((nx,ny), True, r2, cd + 1)) 70 | elif (nx,ny) == st and r1: 71 | q.append(((nx,ny), r1, True, cd + 1)) 72 | else: 73 | q.append(((nx,ny), r1, r2, cd + 1)) 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | -------------------------------------------------------------------------------- /2020/day19/day19.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | import functools 3 | import regex 4 | import heapq 5 | 6 | nums_regex = regex.compile("^([^\\d]*?)((?P\\-?\\d+)([^\\d]*?))*$") 7 | 8 | # N,E,S,W 9 | dirs = [(0,1),(1,0),(0,-1),(-1,0)] 10 | 11 | def nums(s): 12 | m = nums_regex.match(s) 13 | vals = m.capturesdict()["nums"] 14 | return [int(x) for x in vals] 15 | 16 | with open("input.txt") as f: 17 | s = f.read().strip("\n") 18 | 19 | r,m = s.split("\n\n") 20 | 21 | ms = m.split("\n") 22 | r = r.split("\n") 23 | 24 | rs = {} 25 | for l in r: 26 | a,b = l.split(": ") 27 | b = b.split(" | ") 28 | if '"' in l: 29 | rs[int(a)] = b[0].strip('"') 30 | else: 31 | rs[int(a)] = [[int(x) for x in y.split(" ")] for y in b] 32 | 33 | cache = {} 34 | def m(rn,s): 35 | if type(rs[rn]) is str: 36 | return s == rs[rn] 37 | if (rn,s) not in cache: 38 | for i in range(1,len(s)+1): 39 | a,b = s[:i], s[i:] 40 | for k in rs[rn]: 41 | if len(k) == 1: 42 | if m(k[0],s): 43 | cache[rn,s] = True 44 | return True 45 | else: 46 | c,d = k 47 | if m(c,a) and m(d,b): 48 | cache[rn,s] = True 49 | return True 50 | cache[rn,s] = False 51 | return cache[rn,s] 52 | 53 | import itertools 54 | 55 | pos = {} 56 | def p(rn): 57 | if rn in pos: 58 | return pos[rn] 59 | if type(rs[rn]) is str: 60 | pos[rn] = [rs[rn]] 61 | return pos[rn] 62 | pos[rn] = [] 63 | for vd in rs[rn]: 64 | pos[rn].extend([''.join(nl) for nl in itertools.product(*[p(nr) for nr in vd])]) 65 | return pos[rn] 66 | 67 | 68 | ##c = 0 69 | ##for mi in ms: 70 | ## if m(0, mi): 71 | ## c += 1 72 | ##print(c) 73 | 74 | c = 0 75 | r0 = p(0) 76 | for mi in ms: 77 | if mi in r0: 78 | c += 1 79 | print(c) 80 | 81 | 82 | r42 = p(42) 83 | r31 = p(31) 84 | 85 | c = 0 86 | for mi in ms: 87 | if len(mi)%8 != 0: 88 | continue 89 | bc = [mi[i:i+8] for i in range(0,len(mi),8)] 90 | dx = 0 91 | c42 = 0 92 | while dx < len(bc) and bc[dx] in r42: 93 | dx += 1 94 | c42 += 1 95 | c31 = 0 96 | while dx < len(bc) and bc[dx] in r31: 97 | dx += 1 98 | c31 += 1 99 | if dx == len(bc) and c31 < c42 and c31 > 0: 100 | c += 1 101 | print(c) 102 | -------------------------------------------------------------------------------- /2018/day-16/day_16.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | cases = inp.split("\n\n") 5 | program = cases[-1] 6 | cases = [(eval(c[0][7:]), eval(c[2][7:]), list(map(int, c[1].split(" "))))\ 7 | for c in [case.split("\n") for case in cases][:776]] 8 | 9 | def addr(reg,a,b,c): 10 | reg[c] = reg[a] + reg[b] 11 | 12 | def addi(reg,a,b,c): 13 | reg[c] = reg[a] + b 14 | 15 | def mulr(reg,a,b,c): 16 | reg[c] = reg[a] * reg[b] 17 | 18 | def muli(reg,a,b,c): 19 | reg[c] = reg[a] * b 20 | 21 | def banr(reg,a,b,c): 22 | reg[c] = reg[a] & reg[b] 23 | 24 | def bani(reg,a,b,c): 25 | reg[c] = reg[a] & b 26 | 27 | def borr(reg,a,b,c): 28 | reg[c] = reg[a] | reg[b] 29 | 30 | def bori(reg,a,b,c): 31 | reg[c] = reg[a] | b 32 | 33 | def setr(reg,a,b,c): 34 | reg[c] = reg[a] 35 | 36 | def seti(reg,a,b,c): 37 | reg[c] = a 38 | 39 | def gtir(reg,a,b,c): 40 | if a > reg[b]: 41 | reg[c] = 1 42 | else: 43 | reg[c] = 0 44 | 45 | def gtri(reg,a,b,c): 46 | if reg[a] > b: 47 | reg[c] = 1 48 | else: 49 | reg[c] = 0 50 | 51 | def gtrr(reg,a,b,c): 52 | if reg[a] > reg[b]: 53 | reg[c] = 1 54 | else: 55 | reg[c] = 0 56 | 57 | def eqir(reg,a,b,c): 58 | if a == reg[b]: 59 | reg[c] = 1 60 | else: 61 | reg[c] = 0 62 | 63 | def eqri(reg,a,b,c): 64 | if reg[a] == b: 65 | reg[c] = 1 66 | else: 67 | reg[c] = 0 68 | 69 | def eqrr(reg,a,b,c): 70 | if reg[a] == reg[b]: 71 | reg[c] = 1 72 | else: 73 | reg[c] = 0 74 | 75 | ops = [addr, addi, mulr, muli, banr, bani, 76 | borr, bori, setr, seti, gtir, gtri, 77 | gtrr, eqir, eqri, eqrr] 78 | 79 | mapping = {} 80 | rmapping = {} 81 | 82 | num_over_3 = 0 83 | for case in cases: 84 | num_ops = 0 85 | the_ops = [] 86 | for op in ops: 87 | cp = case[0][:] 88 | op(cp, *case[2][1:]) 89 | if cp == case[1]: 90 | num_ops += 1 91 | the_ops.append(op) 92 | if num_ops >= 3: 93 | num_over_3 += 1 94 | the_ops = [p for p in the_ops if p not in rmapping] 95 | if len(the_ops) == 1: 96 | mapping[case[2][0]] = the_ops[0] 97 | rmapping[the_ops[0]] = case[2][0] 98 | print("Part 1:",num_over_3) 99 | 100 | regs = [0,0,0,0] 101 | for s in program.strip().split("\n"): 102 | ins = [int(x) for x in s.split(" ")] 103 | mapping[ins[0]](regs, ins[1], ins[2], ins[3]) 104 | 105 | print("Part 2:",regs[0]) 106 | -------------------------------------------------------------------------------- /2018/day-18/input.txt: -------------------------------------------------------------------------------- 1 | |.||#.##..|....|.|.|.|#||#...|.#.||#.....##|..#... 2 | .......#.|#...|.|#........#...|..#|.|...#.|..#..## 3 | |.#...##.|.|.|#..#..#.||#..#.##|#.|..##..|.##..|.# 4 | |.|...||#|###..|#|#...#|.....||.....|.#.......|... 5 | .||..|.#|.|.....||..|.#.#.|##.|..|.##|..|...#....# 6 | .###....#.#....#..#..||.|.#..|....#||........|..#| 7 | #..#...||#.#.|#..#..|.|.||.#|.|..#..|..|.....#.... 8 | |.#|...#|||.|.#.#|.#.|.##|||#|.#.....#..|.#....... 9 | .....|.|....#||...|#||#|||.#..#|#..#...#|...|||... 10 | .|......#......|.#..##......#|.##.#..#..|.#||..### 11 | ..|..|###|.|.|###.||#|.#.|..##...|..|#.|....#..#.# 12 | ||...#.#.#.......||.||..#.#|#||...|.|......#...||. 13 | |.#.#.#|#|...#..|..#|||#|...|...|#...#..##|...|||. 14 | |....|.....#.##|#....|##.....|..|#.|||..|##..#|..# 15 | ...||.#|#||#...|.||.#.#....||.|.|#.##.#.#|....##.. 16 | ..|.#..#.##|##..........|......#..#|.|.#......#... 17 | |..|..#....|.#|..|...##..|.#|||#...#.|..|...##|... 18 | .|....|##...|.|.|##....||..##.|#.#..|...|..|#....# 19 | .|#..##.||...#.|...|#........#|#|.#.|##.|##...|.|. 20 | .|....|.|.....|...#......|...###....|.#...|...|.#. 21 | |.||..#.#..#||..#||.|.|.####.#...#.#.#......#...#. 22 | #|||...|#......|...#..##..##||...|#.|...#.##..||.. 23 | .||...#...#.#.........#....|#..|.#||...|.##||..|## 24 | |....||.....#.....|..#.#.#...#.#......|...##.|#... 25 | |.|.|#..||#.|..|.|..#.##|...#.#....#|...#|#.###|.. 26 | #...........|.#..||.|..#|.....|..|..|....##....|.| 27 | ..|.|.....#......|.|..|.....##.#.#........|....#.# 28 | |..|..#.|..|..#.....|.|#..#..|#.|...|.#...|#|#|..| 29 | |..#.......|###|.|..#...#.#..#|.#.####....##...|.. 30 | ..#.#..#|#.|...|.#.....##.#|#..#..|..|###.....|.#. 31 | .#....##.|..#|.#.|||.|.|......####.||...#.#..##.|| 32 | ....||#..##..#|...#|.....|#.|..........|.|.|.#.#|. 33 | |....#|..|.|#|#.....#|..|...#..|...|....|.||..||## 34 | ....#..|.#..#...|..|..#.#....|..#.#..||#....##.||# 35 | .|....###.............||.#......#..||.#.|#..#|...| 36 | .||..||....|#..|....#.|..|....###..#.#......|#.#.. 37 | ||.|#.#.|.#.#.#.#.#|...............|#|#..||..|.... 38 | #...|#...#...|.#...|.....##...##.||#|#...#....|||. 39 | .#..|..|.|||#......|#......|.#..........#......|#| 40 | .....#.#..||....##.#.||##..#.|.#.|#.#...|#.#|.|... 41 | ||#.#...#.#|...|...#...##..|..|......#....|##..... 42 | ..#.#.....|#.#.#.|...|.#..#..#.|.....#...|||...|.| 43 | |#..#..|......|.|.||.|.......#.#|....|#.|.|.|#|... 44 | .|.....|..#...||..##.#...#..|...#.|#..||.|.#....|| 45 | .#..#...##|.|#.#...#....|.|....|......##..|.|.|... 46 | ...#.#|#.|#||#...#|..#......||#|#|.#...||.|.#.#.## 47 | |#|.###....#...#.#....#.#....#.##...#......##..#.. 48 | ....||..#.#.||......#.#|#....|.|...###|#.|||...#|. 49 | .#.|#...||.|.....#.|.|#.#....#.....|.|...||#.|||#| 50 | |.|.##...#..#.............||..|.||.#..|..........# 51 | -------------------------------------------------------------------------------- /2018/day-21/day_21.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | with open("input.txt") as file: 4 | inp = file.read().strip() 5 | 6 | prog = inp.split("\n") 7 | prog = [c.split(" ") for c in prog] 8 | prog = [[c[0],*map(int,c[1:])] for c in prog] 9 | 10 | def addr(reg,a,b,c): 11 | reg[c] = reg[a] + reg[b] 12 | 13 | def addi(reg,a,b,c): 14 | reg[c] = reg[a] + b 15 | 16 | def mulr(reg,a,b,c): 17 | reg[c] = reg[a] * reg[b] 18 | 19 | def muli(reg,a,b,c): 20 | reg[c] = reg[a] * b 21 | 22 | def banr(reg,a,b,c): 23 | reg[c] = reg[a] & reg[b] 24 | 25 | def bani(reg,a,b,c): 26 | reg[c] = reg[a] & b 27 | 28 | def borr(reg,a,b,c): 29 | reg[c] = reg[a] | reg[b] 30 | 31 | def bori(reg,a,b,c): 32 | reg[c] = reg[a] | b 33 | 34 | def setr(reg,a,b,c): 35 | reg[c] = reg[a] 36 | 37 | def seti(reg,a,b,c): 38 | reg[c] = a 39 | 40 | def gtir(reg,a,b,c): 41 | if a > reg[b]: 42 | reg[c] = 1 43 | else: 44 | reg[c] = 0 45 | 46 | def gtri(reg,a,b,c): 47 | if reg[a] > b: 48 | reg[c] = 1 49 | else: 50 | reg[c] = 0 51 | 52 | def gtrr(reg,a,b,c): 53 | if reg[a] > reg[b]: 54 | reg[c] = 1 55 | else: 56 | reg[c] = 0 57 | 58 | def eqir(reg,a,b,c): 59 | if a == reg[b]: 60 | reg[c] = 1 61 | else: 62 | reg[c] = 0 63 | 64 | def eqri(reg,a,b,c): 65 | if reg[a] == b: 66 | reg[c] = 1 67 | else: 68 | reg[c] = 0 69 | 70 | def eqrr(reg,a,b,c): 71 | if reg[a] == reg[b]: 72 | reg[c] = 1 73 | else: 74 | reg[c] = 0 75 | 76 | ops = [addr, addi, mulr, muli, banr, bani, 77 | borr, bori, setr, seti, gtir, gtri, 78 | gtrr, eqir, eqri, eqrr] 79 | 80 | op_dict = {op.__name__:op for op in ops} 81 | 82 | st = time.time() 83 | 84 | reg = [1,0,0,0,0,0] 85 | ip = 0 86 | ip_reg = prog[0][1] 87 | prog = prog[1:] 88 | ctr = 0 89 | flag = False 90 | check_seq = [] 91 | cc = 0 92 | while ip in range(len(prog)): 93 | reg[ip_reg] = ip 94 | if ip == 28: 95 | print("Part 1:",reg[2]) 96 | break 97 | op_dict[prog[ip][0]](reg, *prog[ip][1:]) 98 | ip = reg[ip_reg] 99 | ip += 1 100 | ctr += 1 101 | 102 | seen_checks = set() 103 | last_check = 0 104 | r2, r3, r5 = 2208870, 0, 65536 105 | while True: 106 | if r5 < 256: 107 | if r2 in seen_checks: 108 | print("Part 2:",last_check) 109 | break 110 | seen_checks.add(r2) 111 | last_check = r2 112 | r5 = r2 | 65536 113 | r2 = 2238642 114 | else: 115 | r5 //= 256 116 | r3 = r5 & 255 117 | r2 += r3 118 | r2 &= 16777215 119 | r2 *= 65899 120 | r2 &= 16777215 121 | -------------------------------------------------------------------------------- /2022/day01/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day02/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day03/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day04/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day05/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day06/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day07/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day08/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day09/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day10/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2022/day11/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | 71 | 72 | # underscored names are in case functions get shadowed by accident 73 | adjp = _adjp = adjacent_pairs 74 | ap = _ap = all_pairs 75 | at = _at = all_tuples 76 | rw = _rw = rolling_window 77 | rsum = _rsum = rolling_sum 78 | 79 | dd = _dd = defaultdict 80 | ctr = _ctr = Counter 81 | -------------------------------------------------------------------------------- /2020/day11/day11.py: -------------------------------------------------------------------------------- 1 | import sys, os 2 | sys.path.append(os.path.join(os.path.dirname(__file__), "..")) 3 | from aoc_lib import * 4 | 5 | s = get_input() 6 | 7 | n,m,g = grid(s) 8 | 9 | nb = [(i,j) for i in range(-1,2) for j in range(-1,2) if (i,j) != (0,0)] 10 | 11 | def apply(g): 12 | ng = [[-1 for _ in range(m)] for _ in range(n)] 13 | for i in range(n): 14 | for j in range(m): 15 | e_n = 0 16 | o_n = 0 17 | for di,dj in nb: 18 | ni, nj = i + di, j + dj 19 | if ni in range(n) and nj in range(m): 20 | if g[ni][nj] == "L": 21 | e_n += 1 22 | elif g[ni][nj] == "#": 23 | o_n += 1 24 | if g[i][j] == "L": 25 | if o_n == 0: 26 | ng[i][j] = "#" 27 | else: 28 | ng[i][j] = "L" 29 | elif g[i][j] == "#": 30 | if o_n >= 4: 31 | ng[i][j] = "L" 32 | else: 33 | ng[i][j] = "#" 34 | else: 35 | ng[i][j] = g[i][j] 36 | return ng 37 | 38 | ts = lambda gg : "\n".join("".join(v for v in r) for r in gg) 39 | 40 | ct = 0 41 | cg = g 42 | while ts(cg) != ts((ng := apply(cg))): 43 | cg = ng 44 | ct += 1 45 | 46 | print(ct) 47 | print(sum(1 for i in range(n) for j in range(m) if cg[i][j] == "#")) 48 | 49 | 50 | adj = {} 51 | for i in range(n): 52 | for j in range(m): 53 | adj[i,j] = [] 54 | for di,dj in nb: 55 | ni, nj = i + di, j + dj 56 | while ni in range(n) and nj in range(m) and g[ni][nj] == ".": 57 | ni += di 58 | nj += dj 59 | if ni in range(n) and nj in range(m): 60 | adj[i,j].append((ni,nj)) 61 | 62 | 63 | def apply(g): 64 | ng = [[-1 for _ in range(m)] for _ in range(n)] 65 | for i in range(n): 66 | for j in range(m): 67 | e_n = 0 68 | o_n = 0 69 | for ni,nj in adj[i,j]: 70 | if ni in range(n) and nj in range(m): 71 | if g[ni][nj] == "L": 72 | e_n += 1 73 | elif g[ni][nj] == "#": 74 | o_n += 1 75 | if g[i][j] == "L": 76 | if o_n == 0: 77 | ng[i][j] = "#" 78 | else: 79 | ng[i][j] = "L" 80 | elif g[i][j] == "#": 81 | if o_n >= 5: 82 | ng[i][j] = "L" 83 | else: 84 | ng[i][j] = "#" 85 | else: 86 | ng[i][j] = g[i][j] 87 | return ng 88 | 89 | cg = g 90 | while ts(cg) != ts((ng := apply(cg))): 91 | cg = ng 92 | 93 | print(sum(1 for i in range(n) for j in range(m) if cg[i][j] == "#")) 94 | -------------------------------------------------------------------------------- /2018/day-20/day_20.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as file: 2 | inp = file.read().strip() 3 | 4 | path = inp[1:-1] 5 | mp = [["#" for i in range(2000)] for j in range(2000)] 6 | in_map = lambda c : c[0] in range(len(mp)) and c[1] in range(len(mp[c[0]])) 7 | 8 | drs = {"N":(-1,0),"S":(1,0),"W":(0,-1),"E":(0,1)} 9 | 10 | stack = [] 11 | 12 | def dfs(co, pto): 13 | stack.append((co, pto)) 14 | seen = set() 15 | while len(stack) > 0: 16 | cur = stack.pop(-1) 17 | c = cur[0] 18 | pt = cur[1] 19 | if (c[0],c[1],pt) in seen: 20 | continue 21 | seen.add((c[0],c[1],pt)) 22 | if len(cur[1]) == 0: 23 | continue 24 | if cur[1][0] == "(": 25 | r_s = 1 26 | r_i = 1 27 | while r_s > 0: 28 | if pt[r_i] == "(": 29 | r_s += 1 30 | elif pt[r_i] == ")": 31 | r_s -= 1 32 | r_i += 1 33 | group = pt[1:r_i-1] 34 | opts = [] 35 | r_s2 = 0 36 | r_i2 = 0 37 | lc = 0 38 | while r_s2 >= 0 and r_i2 < len(group): 39 | if group[r_i2] == "(": 40 | r_s2 += 1 41 | elif group[r_i2] == ")": 42 | r_s2 -= 1 43 | if r_s2 == 0 and group[r_i2] == "|": 44 | opts.append(group[lc:r_i2]) 45 | lc = r_i2 + 1 46 | r_i2 += 1 47 | opts.append(group[lc:]) 48 | for poss in opts: 49 | stack.append((c, poss + pt[r_i:])) 50 | else: 51 | bf = pt.find("(") 52 | if bf == -1: 53 | bf = len(pt) 54 | cloc = [c[0],c[1]] 55 | for b in range(bf): 56 | tdr = pt[b] 57 | mp[cloc[0]+drs[tdr][0]][cloc[1]+drs[tdr][1]] = "|" if tdr in "WE" else "-" 58 | mp[cloc[0]+2*drs[tdr][0]][cloc[1]+2*drs[tdr][1]] = "." 59 | cloc[0] += 2*drs[tdr][0] 60 | cloc[1] += 2*drs[tdr][1] 61 | stack.append((cloc,pt[bf:])) 62 | 63 | mp[1000][1000] = "X" 64 | 65 | dfs((1000,1000), path) 66 | 67 | #now find farthest 68 | cur = (1000,1000) 69 | import queue 70 | q = queue.Queue() 71 | q.put((cur,0)) 72 | furthest = 0 73 | far_count = 0 74 | while not q.empty(): 75 | cur = q.get() 76 | if cur[1] > furthest: 77 | furthest = cur[1] 78 | if cur[1] >= 1000: 79 | far_count += 1 80 | dist = cur[1] 81 | cur = cur[0] 82 | mp[cur[0]][cur[1]] = "v" 83 | for d in [(-1,0),(1,0),(0,-1),(0,1)]: 84 | if mp[cur[0]+d[0]][cur[1]+d[1]] in "-|": #traversable 85 | if mp[cur[0]+2*d[0]][cur[1]+2*d[1]] != "v": 86 | mp[cur[0]+2*d[0]][cur[1]+2*d[1]] = "v" 87 | q.put(((cur[0]+2*d[0],cur[1]+2*d[1]),dist+1)) 88 | 89 | print("Part 1:",furthest) 90 | print("Part 2:",far_count) 91 | -------------------------------------------------------------------------------- /2021/day16/day16.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict, Counter 2 | import regex 3 | import itertools 4 | 5 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 6 | 7 | def nums(s): 8 | m = nums_regex.match(s) 9 | vals = m.capturesdict()["nums"] 10 | return [int(x) for x in vals] 11 | 12 | with open("input.txt") as f: 13 | s = f.read().strip() 14 | 15 | class PackDec: 16 | def __init__(self, s): 17 | self.vals = list(s) 18 | self.buffer = "" 19 | self.i = 0 # next to add to buffer 20 | 21 | def get(self, n): 22 | r = "" 23 | while n > 0: 24 | a = self.buffer[:n] 25 | self.buffer = self.buffer[n:] 26 | r += a 27 | n -= len(a) 28 | if len(self.buffer) == 0: 29 | self.i += 1 30 | self.buffer = bin(16 + int(self.vals[self.i - 1],16))[3:] 31 | return r 32 | 33 | def align(self): 34 | if len(self.buffer) < 4: 35 | self.buffer = "" 36 | 37 | class BitsOnly: 38 | def __init__(self, s): 39 | self.s = s 40 | 41 | def get(self, n): 42 | a, self.s = self.s[:n], self.s[n:] 43 | return a 44 | 45 | global version_sum 46 | version_sum = 0 47 | 48 | pd = PackDec(s) 49 | 50 | def parse(pd): 51 | global version_sum 52 | version = int(pd.get(3),2) 53 | typeid = int(pd.get(3),2) 54 | version_sum += version 55 | 56 | if typeid == 4: #literal 57 | leadbit = pd.get(1) 58 | value = pd.get(4) 59 | while leadbit == "1": 60 | leadbit = pd.get(1) 61 | value += pd.get(4) 62 | return int(value, 2) 63 | else: 64 | ltid = pd.get(1) 65 | if ltid == "0": 66 | total_length = int(pd.get(15),2) 67 | bo = BitsOnly(pd.get(total_length)) 68 | packs = [] 69 | while len(bo.s) > 0: 70 | try: 71 | packs.append(parse(bo)) 72 | except: 73 | pass 74 | else: 75 | num_packs = int(pd.get(11),2) 76 | packs = [] 77 | for _ in range(num_packs): 78 | packs.append(parse(pd)) 79 | 80 | if typeid == 0: 81 | return sum(packs) 82 | elif typeid == 1: 83 | r = 1 84 | for p in packs: 85 | r *= p 86 | return r 87 | elif typeid == 2: 88 | return min(packs) 89 | elif typeid == 3: 90 | return max(packs) 91 | elif typeid == 5: 92 | return 1 if packs[0] > packs[1] else 0 93 | elif typeid == 6: 94 | return 1 if packs[0] < packs[1] else 0 95 | elif typeid == 7: 96 | return 1 if packs[0] == packs[1] else 0 97 | else: 98 | print("uhoh") 99 | 100 | print(parse(pd)) 101 | -------------------------------------------------------------------------------- /2022/day12/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | import re 5 | from typing import TypeVar, Generator, Iterable, Tuple, List 6 | 7 | _T = TypeVar("T") 8 | 9 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 10 | elements_iter = iter(elements) 11 | last_element = next(elements_iter) 12 | for element in elements_iter: 13 | yield (last_element, element) 14 | last_element = element 15 | 16 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 17 | elements_list = list(elements) 18 | for i in range(len(elements_list)): 19 | for j in range(i + 1, len(elements_list)): 20 | yield (elements_list[i], elements_list[j]) 21 | 22 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 23 | elements_list = list(elements) 24 | for i in range(len(elements_list)): 25 | for j in range(len(elements_list)): 26 | if j == i: continue 27 | yield (elements_list[i], elements_list[j]) 28 | 29 | 30 | # yes, everyone else calls this "cumsum" but ohwell 31 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 32 | rsum = [] 33 | elements_iter = iter(elements) 34 | 35 | if start is None: 36 | rsum.append(next(elements_iter)) 37 | else: 38 | rsum.append(start) 39 | 40 | for element in elements_iter: 41 | rsum.append(rsum[-1] + element) 42 | return rsum 43 | 44 | 45 | # I did some rough experiments with this version vs. a version that uses a deque, which 46 | # has efficient popleft, and it seems like this version actually wins because of how slow 47 | # iterating over a deque is (which you have to do if you want to use the results) 48 | def rolling_window( 49 | elements: Iterable[_T], 50 | window_size: int, 51 | ) -> Generator[Tuple[_T, ...], None, None]: 52 | current_window = [] 53 | for element in elements: 54 | current_window.append(element) 55 | if len(current_window) > window_size: 56 | del current_window[0] 57 | if len(current_window) == window_size: 58 | yield current_window 59 | 60 | 61 | 62 | # this is like slightly borked because it doesn't get negative numbers 63 | # oh well i guess 64 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 65 | 66 | def nums(s): 67 | m = nums_regex.match(s) 68 | vals = m.capturesdict()["nums"] 69 | return [int(x) for x in vals] 70 | 71 | def nums(s): 72 | m = re.findall("-?\d+", s) 73 | return [int(x) for x in m] 74 | 75 | def numsp(s): 76 | m = re.findall("-?\d+", s) 77 | return [int(x) for x in m] 78 | 79 | 80 | # underscored names are in case functions get shadowed by accident 81 | adjp = _adjp = adjacent_pairs 82 | ap = _ap = all_pairs 83 | at = _at = all_tuples 84 | rw = _rw = rolling_window 85 | rsum = _rsum = rolling_sum 86 | 87 | dd = _dd = defaultdict 88 | ctr = _ctr = Counter 89 | -------------------------------------------------------------------------------- /2022/day13/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import regex 4 | import re 5 | from typing import TypeVar, Generator, Iterable, Tuple, List 6 | 7 | _T = TypeVar("T") 8 | 9 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 10 | elements_iter = iter(elements) 11 | last_element = next(elements_iter) 12 | for element in elements_iter: 13 | yield (last_element, element) 14 | last_element = element 15 | 16 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 17 | elements_list = list(elements) 18 | for i in range(len(elements_list)): 19 | for j in range(i + 1, len(elements_list)): 20 | yield (elements_list[i], elements_list[j]) 21 | 22 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 23 | elements_list = list(elements) 24 | for i in range(len(elements_list)): 25 | for j in range(len(elements_list)): 26 | if j == i: continue 27 | yield (elements_list[i], elements_list[j]) 28 | 29 | 30 | # yes, everyone else calls this "cumsum" but ohwell 31 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 32 | rsum = [] 33 | elements_iter = iter(elements) 34 | 35 | if start is None: 36 | rsum.append(next(elements_iter)) 37 | else: 38 | rsum.append(start) 39 | 40 | for element in elements_iter: 41 | rsum.append(rsum[-1] + element) 42 | return rsum 43 | 44 | 45 | # I did some rough experiments with this version vs. a version that uses a deque, which 46 | # has efficient popleft, and it seems like this version actually wins because of how slow 47 | # iterating over a deque is (which you have to do if you want to use the results) 48 | def rolling_window( 49 | elements: Iterable[_T], 50 | window_size: int, 51 | ) -> Generator[Tuple[_T, ...], None, None]: 52 | current_window = [] 53 | for element in elements: 54 | current_window.append(element) 55 | if len(current_window) > window_size: 56 | del current_window[0] 57 | if len(current_window) == window_size: 58 | yield current_window 59 | 60 | 61 | 62 | # this is like slightly borked because it doesn't get negative numbers 63 | # oh well i guess 64 | nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 65 | 66 | def nums(s): 67 | m = nums_regex.match(s) 68 | vals = m.capturesdict()["nums"] 69 | return [int(x) for x in vals] 70 | 71 | def nums(s): 72 | m = re.findall("-?\d+", s) 73 | return [int(x) for x in m] 74 | 75 | def numsp(s): 76 | m = re.findall("-?\d+", s) 77 | return [int(x) for x in m] 78 | 79 | 80 | # underscored names are in case functions get shadowed by accident 81 | adjp = _adjp = adjacent_pairs 82 | ap = _ap = all_pairs 83 | at = _at = all_tuples 84 | rw = _rw = rolling_window 85 | rsum = _rsum = rolling_sum 86 | 87 | dd = _dd = defaultdict 88 | ctr = _ctr = Counter 89 | -------------------------------------------------------------------------------- /2022/day15/aoc_tools.py: -------------------------------------------------------------------------------- 1 | from collections import deque, defaultdict, Counter 2 | import itertools 3 | import re 4 | from typing import TypeVar, Generator, Iterable, Tuple, List 5 | 6 | _T = TypeVar("T") 7 | 8 | def adjacent_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 9 | elements_iter = iter(elements) 10 | last_element = next(elements_iter) 11 | for element in elements_iter: 12 | yield (last_element, element) 13 | last_element = element 14 | 15 | def all_pairs(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 16 | elements_list = list(elements) 17 | for i in range(len(elements_list)): 18 | for j in range(i + 1, len(elements_list)): 19 | yield (elements_list[i], elements_list[j]) 20 | 21 | def all_tuples(elements: Iterable[_T]) -> Generator[Tuple[_T, _T], None, None]: 22 | elements_list = list(elements) 23 | for i in range(len(elements_list)): 24 | for j in range(len(elements_list)): 25 | if j == i: continue 26 | yield (elements_list[i], elements_list[j]) 27 | 28 | 29 | # yes, everyone else calls this "cumsum" but ohwell 30 | def rolling_sum(elements: Iterable[_T], start: _T = None) -> List[_T]: 31 | rsum = [] 32 | elements_iter = iter(elements) 33 | 34 | if start is None: 35 | rsum.append(next(elements_iter)) 36 | else: 37 | rsum.append(start) 38 | 39 | for element in elements_iter: 40 | rsum.append(rsum[-1] + element) 41 | return rsum 42 | 43 | 44 | # I did some rough experiments with this version vs. a version that uses a deque, which 45 | # has efficient popleft, and it seems like this version actually wins because of how slow 46 | # iterating over a deque is (which you have to do if you want to use the results) 47 | def rolling_window( 48 | elements: Iterable[_T], 49 | window_size: int, 50 | ) -> Generator[Tuple[_T, ...], None, None]: 51 | current_window = [] 52 | for element in elements: 53 | current_window.append(element) 54 | if len(current_window) > window_size: 55 | del current_window[0] 56 | if len(current_window) == window_size: 57 | yield current_window 58 | 59 | 60 | 61 | # this is like slightly borked because it doesn't get negative numbers 62 | # oh well i guess 63 | #nums_regex = regex.compile("([^\\d]*)((?P\\d+)([^\\d]*))*") 64 | 65 | def nums(s): 66 | m = nums_regex.match(s) 67 | vals = m.capturesdict()["nums"] 68 | return [int(x) for x in vals] 69 | 70 | def nums(s): 71 | m = re.findall("-?\d+", s) 72 | return [int(x) for x in m] 73 | 74 | def numsp(s): 75 | m = re.findall("-?\d+", s) 76 | return [int(x) for x in m] 77 | 78 | def sign(x): 79 | if x < 0: 80 | return -1 81 | elif x == 0: 82 | return 0 83 | else: 84 | return 1 85 | 86 | 87 | # underscored names are in case functions get shadowed by accident 88 | adjp = _adjp = adjacent_pairs 89 | ap = _ap = all_pairs 90 | at = _at = all_tuples 91 | rw = _rw = rolling_window 92 | rsum = _rsum = rolling_sum 93 | 94 | dd = _dd = defaultdict 95 | ctr = _ctr = Counter 96 | --------------------------------------------------------------------------------