├── 2021 ├── day1.py ├── day10.py ├── day11.py ├── day12.py ├── day13.py ├── day14.py ├── day15.cpp ├── day16.py ├── day17.py ├── day18.py ├── day19.py ├── day2.py ├── day20.py ├── day21.py ├── day22.py ├── day23.py ├── day24.py ├── day25.py ├── day3.py ├── day4.py ├── day5.py ├── day6.py ├── day7.py ├── day8.py ├── day9.py └── util.py ├── 2022 ├── day1.py ├── day10.py ├── day11.py ├── day12.py ├── day13.py ├── day14.py ├── day15.py ├── day16.py ├── day17.py ├── day18.py ├── day19.py ├── day2.py ├── day20.py ├── day21.py ├── day22.py ├── day23.py ├── day24.py ├── day25.py ├── day3.py ├── day4.py ├── day5.py ├── day6.py ├── day7.py ├── day8.py ├── day9.py └── util.py ├── LICENSE └── README /2021/day1.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | 8 | from util import * 9 | 10 | """ 11 | echo "=== sample ===" ; py day1.py < sample.in ; echo "=== real ===" ; py day1.py < day1.in 12 | """ 13 | 14 | L = read_input('/dev/stdin', t=int) 15 | N = len(L) 16 | 17 | s = set() 18 | 19 | res = 0 20 | for i in range(1, N): 21 | if L[i] > L[i-1]: 22 | res += 1 23 | print(res) 24 | 25 | res = 0 26 | 27 | for i in range(1, N): 28 | a = sum(L[i:i+3]) 29 | b = sum(L[i - 1:i+2]) 30 | if a > b: 31 | res += 1 32 | print(res) -------------------------------------------------------------------------------- /2021/day10.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day10.py < sample.in 14 | echo "=== real ===" ; py day10.py < day10.in 15 | echo "=== sample ===" ; py day10.py < sample.in ; echo "=== real ===" ; py day10.py < day10.in 16 | """ 17 | A = read_input('/dev/stdin') 18 | N = len(A) 19 | 20 | res = 0 21 | 22 | table = { 23 | ')': 3, 24 | ']': 57, 25 | '}': 1197, 26 | '>': 25137, 27 | } 28 | 29 | opp = { 30 | ')': '(', 31 | ']': '[', 32 | '}': '{', 33 | '>': '<', 34 | } 35 | 36 | t2 = { 37 | '(': 1, 38 | '[': 2, 39 | '{': 3, 40 | '<': 4, 41 | } 42 | 43 | scores = [] 44 | for line in A: 45 | stack = [] 46 | found = None 47 | for i, e in enumerate(line): 48 | if e in opp: 49 | if stack: 50 | if stack[-1] == opp[e]: 51 | stack.pop() 52 | else: 53 | found = i 54 | break 55 | else: 56 | assert False 57 | else: 58 | stack.append(e) 59 | if found is not None: 60 | ill = line[i] 61 | res += table[ill] 62 | else: 63 | score = 0 64 | while stack: 65 | c = stack.pop() 66 | score *= 5 67 | score += t2[c] 68 | scores.append(score) 69 | 70 | 71 | print(res) 72 | scores.sort() 73 | print(scores[len(scores) // 2]) 74 | -------------------------------------------------------------------------------- /2021/day11.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day11.py < sample.in 14 | echo "=== real ===" ; py day11.py < day11.in 15 | echo "=== sample ===" ; py day11.py < sample.in ; echo "=== real ===" ; py day11.py < day11.in 16 | """ 17 | A = read_input('/dev/stdin', t=lambda line: map(int, list(line))) 18 | N = len(A) 19 | 20 | res = 0 21 | R = N 22 | C = len(A[0]) 23 | 24 | def get_neighbors(i, j): 25 | # for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)): 26 | for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1), (i - 1, j - 1), (i - 1, j + 1), (i + 1, j - 1), (i + 1, j + 1)): 27 | if 0 <= ii < R and 0 <= jj < C: 28 | yield ii, jj 29 | 30 | 31 | def step(grid): 32 | new_grid = copy.deepcopy(grid) 33 | flashed = set() 34 | for i in range(R): 35 | for j in range(C): 36 | new_grid[i][j] += 1 37 | changed = True 38 | while changed: 39 | changed = False 40 | for i in range(R): 41 | for j in range(C): 42 | if new_grid[i][j] > 9 and (i, j) not in flashed: 43 | flashed.add((i, j)) 44 | for ii, jj in get_neighbors(i, j): 45 | new_grid[ii][jj] += 1 46 | changed = True 47 | for i, j in flashed: 48 | new_grid[i][j] = 0 49 | return new_grid, len(flashed) 50 | 51 | res = 0 52 | grid = copy.deepcopy(A) 53 | for i in range(100): 54 | grid, flashed = step(grid) 55 | res += flashed 56 | print(res) 57 | 58 | res = 0 59 | grid = copy.deepcopy(A) 60 | while True: 61 | res += 1 62 | grid, flashed = step(grid) 63 | if flashed == R * C: 64 | print(res) 65 | break 66 | -------------------------------------------------------------------------------- /2021/day12.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day12.py < sample.in 14 | echo "=== real ===" ; py day12.py < day12.in 15 | echo "=== sample ===" ; py day12.py < sample.in ; echo "=== real ===" ; py day12.py < day12.in 16 | """ 17 | 18 | A = read_input('/dev/stdin') 19 | N = len(A) 20 | 21 | res = 0 22 | 23 | def is_small(s): 24 | return all(x.islower() for x in s) 25 | 26 | graph = defaultdict(list) 27 | 28 | for line in A: 29 | u, v = line.split('-') 30 | graph[u].append(v) 31 | graph[v].append(u) 32 | 33 | stack = [] 34 | ctr = Counter() 35 | ree = False 36 | 37 | def dfs(root): 38 | global res, ree 39 | if root == 'start' and ctr['start'] >= 1: 40 | return 41 | if root == 'end': 42 | # print(','.join(stack)) 43 | res += 1 44 | return 45 | if is_small(root) and root in stack: 46 | # maybe visit again 47 | if not ree: 48 | ree = True 49 | stack.append(root) 50 | ctr[root] += 1 51 | for v in graph[root]: 52 | dfs(v) 53 | ctr[root] -= 1 54 | stack.pop() 55 | ree = False 56 | return 57 | stack.append(root) 58 | ctr[root] += 1 59 | for v in graph[root]: 60 | dfs(v) 61 | ctr[root] -= 1 62 | stack.pop() 63 | 64 | dfs('start') 65 | print(res) 66 | -------------------------------------------------------------------------------- /2021/day13.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day13.py < sample.in 14 | echo "=== real ===" ; py day13.py < day13.in 15 | echo "=== sample ===" ; py day13.py < sample.in ; echo "=== real ===" ; py day13.py < day13.in 16 | """ 17 | 18 | # A = read_input('/dev/stdin') 19 | 20 | f = open('/dev/stdin') 21 | parts = f.read().strip().split('\n\n') 22 | assert len(parts) == 2 23 | 24 | A = parts[0].strip().splitlines() 25 | inst = parts[1].strip().splitlines() 26 | 27 | grid = defaultdict(int) 28 | 29 | for line in A: 30 | x, y = map(int, line.split(',')) 31 | grid[(x, y)] = 1 32 | 33 | 34 | for line in inst: 35 | _, _, p = line.split(' ') 36 | what, coord = p.split('=') 37 | coord = int(coord) 38 | to_yeet = [] 39 | to_add = [] 40 | if what == 'x': 41 | for k, v in list(grid.items()): 42 | if not v: 43 | continue 44 | x, y = k 45 | assert x != coord 46 | if x < coord: 47 | pass 48 | else: 49 | d = x - coord 50 | to_yeet.append((x, y)) 51 | to_add.append((coord - d, y)) 52 | assert coord - d >= 0 53 | else: 54 | for k, v in list(grid.items()): 55 | if not v: 56 | continue 57 | x, y = k 58 | assert y != coord 59 | if y < coord: 60 | pass 61 | else: 62 | d = y - coord 63 | to_yeet.append((x, y)) 64 | to_add.append((x, coord - d)) 65 | assert coord - d >= 0 66 | for x, y in to_yeet: 67 | del grid[(x, y)] 68 | for x, y in to_add: 69 | grid[(x, y)] = 1 70 | res = 0 71 | 72 | for k, v in grid.items(): 73 | if v: 74 | res += 1 75 | 76 | print(res) 77 | dump_dict_grid(grid, lambda t: '#' if t else '.') 78 | -------------------------------------------------------------------------------- /2021/day14.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day14.py < sample.in 14 | echo "=== real ===" ; py day14.py < day14.in 15 | echo "=== sample ===" ; py day14.py < sample.in ; echo "=== real ===" ; py day14.py < day14.in 16 | """ 17 | 18 | f = open('/dev/stdin') 19 | parts = f.read().strip().split('\n\n') 20 | 21 | A = parts[0].strip() 22 | B = parts[1].strip().splitlines() 23 | 24 | dct = {} 25 | 26 | for line in B: 27 | u, v = line.split(' -> ') 28 | dct[u] = v 29 | 30 | def apply(s): 31 | N = len(s) 32 | insert_at = [] 33 | for i in range(N - 1): 34 | a, b = s[i], s[i + 1] 35 | if a + b in dct: 36 | c = dct[a + b] 37 | insert_at.append(((i + 1), c)) 38 | else: 39 | assert False 40 | assert insert_at 41 | i = 0 42 | j = 0 43 | res = [] 44 | while True: 45 | if i == len(s): 46 | break 47 | if j == len(insert_at): 48 | res.append(s[i]) 49 | i += 1 50 | continue 51 | if i == insert_at[j][0]: 52 | res.append(insert_at[j][1]) 53 | j += 1 54 | else: 55 | res.append(s[i]) 56 | i += 1 57 | return ''.join(res) 58 | 59 | ctr = Counter() 60 | 61 | for i in range(len(A) - 1): 62 | a, b = A[i], A[i + 1] 63 | d = a + b 64 | ctr[d] += 1 65 | 66 | for it in range(40): 67 | nc = Counter() 68 | for k, v in ctr.items(): 69 | middle = dct[k] 70 | a, b = k[0], k[1] 71 | nc[a + middle] += v 72 | nc[middle + b] += v 73 | ctr = nc 74 | 75 | ctr2 = Counter() 76 | 77 | for k, v in ctr.items(): 78 | ctr2[k[0]] += v 79 | ctr2[k[1]] += v 80 | ctr2[A[0]] += 1 81 | ctr2[A[-1]] += 1 82 | 83 | L = list(ctr2.items()) 84 | L.sort(key=lambda x: x[1]) 85 | print((L[-1][1] - L[0][1]) // 2) 86 | -------------------------------------------------------------------------------- /2021/day15.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | using namespace std; 3 | #define rep(i, a, b) for(int i = a; i < (b); ++i) 4 | #define all(x) begin(x), end(x) 5 | #define sz(x) (int)(x).size() 6 | typedef long long ll; 7 | typedef pair pii; 8 | typedef vector vi; 9 | template::value,typename C::value_type>::type> 10 | ostream& operator<<(ostream&os,const C&v){os<<"[";bool f=1;for(const T&x:v){if(!f)os<<", ";os< 12 | ostream& operator<<(ostream&os,const pair&p){return os<<"("< 19 | class y_combinator_result { 20 | Fun fun_; 21 | public: 22 | template 23 | explicit y_combinator_result(T&& fun): fun_(std::forward(fun)) {} 24 | template 25 | decltype(auto) operator()(Args&& ...args) { 26 | return fun_(std::ref(*this), std::forward(args)...); 27 | } 28 | }; 29 | template 30 | decltype(auto) y_combinator(Fun &&fun) { 31 | return y_combinator_result>(std::forward(fun)); 32 | } 33 | template T& setmin(T& tgt, const T& src) { return (tgt = min(tgt, src)); } 34 | template T& setmax(T& tgt, const T& src) { return (tgt = max(tgt, src)); } 35 | const uint64_t RANDOM = chrono::high_resolution_clock::now().time_since_epoch().count(); 36 | constexpr uint64_t HASH_C = 0x57325bbf44649447; // large odd number for C; ll(4e18 * acos(0)) | 71 37 | struct chash { 38 | ll operator()(ll x) const { return __builtin_bswap64((x ^ RANDOM) * HASH_C); } 39 | ll operator()(pii x) const { return operator()((ll(x.first) << 32) | x.second); } 40 | }; 41 | 42 | 43 | const int dx[4]{-1,0,1,0}; 44 | const int dy[4]{0,1,0,-1}; 45 | 46 | const int inf = 2e9; 47 | 48 | int main() { 49 | vector grid; 50 | int R, C; 51 | string s; 52 | while (cin >> s) { 53 | grid.push_back(s); 54 | } 55 | R = sz(grid); 56 | C = sz(grid[0]); 57 | int R2 = R * 5, C2 = C * 5; 58 | vector grid2(R2, vi(C2)); 59 | rep(ii, 0, 5) { 60 | rep(jj, 0, 5) { 61 | rep(i, 0, R) { 62 | rep(j, 0, C) { 63 | int orig = grid[i][j] - '0'; 64 | int extra = ii + jj; 65 | int nv = orig + extra; 66 | while (nv > 9) { 67 | nv -= 9; 68 | } 69 | grid2[ii * R + i][jj * C + j] = nv; 70 | } 71 | } 72 | } 73 | } 74 | priority_queue, vector>, greater>> pq; 75 | vector dist(R2, vi(C2, inf)); 76 | dist[0][0] = 0; 77 | pq.push({0, 0, 0}); 78 | while (!pq.empty()) { 79 | auto [d, i, j] = pq.top(); pq.pop(); 80 | if (i == R2 - 1 && j == C2 - 1) { 81 | cout << d << nl; 82 | return 0; 83 | } 84 | if (d > dist[i][j]) { 85 | continue; 86 | } 87 | rep(k, 0, 4) { 88 | int ni = i + dx[k]; 89 | int nj = j + dy[k]; 90 | if (ni < 0 || ni >= R2 || nj < 0 || nj >= C2) continue; 91 | int nd = dist[i][j] + grid2[ni][nj]; 92 | if (nd < dist[ni][nj]) { 93 | dist[ni][nj] = nd; 94 | pq.push({nd, ni, nj}); 95 | } 96 | } 97 | } 98 | return 0; 99 | } 100 | -------------------------------------------------------------------------------- /2021/day16.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day16.py < sample.in 14 | echo "=== real ===" ; py day16.py < day16.in 15 | echo "=== sample ===" ; py day16.py < sample.in ; echo "=== real ===" ; py day16.py < day16.in 16 | """ 17 | 18 | A = read_input('/dev/stdin') 19 | 20 | # f = open('/dev/stdin') 21 | # parts = f.read().strip().split('\n\n') 22 | 23 | N = len(A) 24 | 25 | 26 | bignum = int(A, 16) 27 | bits = map(int, bin(bignum)[2:]) 28 | if len(bits) % 4 != 0: 29 | bits = ([0] * (4 - len(bits) % 4)) + bits 30 | 31 | def reassemble(bits): 32 | r = [] 33 | for b in bits: 34 | r.append(str(b)) 35 | return int(''.join(r), 2) 36 | sv = 0 37 | def decode(packet, idx): 38 | global sv 39 | version = reassemble(packet[idx:idx + 3]) 40 | sv += version 41 | typ = reassemble(packet[idx + 3:idx + 6]) 42 | idx += 6 43 | if typ == 4: 44 | r = [] 45 | while True: 46 | group = packet[idx:idx + 5] 47 | idx += 5 48 | r += group[1:] 49 | if group[0] == 0: 50 | # done 51 | break 52 | return idx, reassemble(r) 53 | else: 54 | length_typ = packet[idx] 55 | idx += 1 56 | r = [] 57 | if length_typ == 0: 58 | total_length = reassemble(packet[idx:idx + 15]) 59 | idx += 15 60 | orig_idx = idx 61 | while True: 62 | idx, rest = decode(packet, idx) 63 | r.append(rest) 64 | if idx - orig_idx == total_length: 65 | break 66 | assert idx - orig_idx < total_length 67 | 68 | else: 69 | count_packets = reassemble(packet[idx:idx + 11]) 70 | idx += 11 71 | for i in range(count_packets): 72 | idx, rest = decode(packet, idx) 73 | r.append(rest) 74 | if typ == 0: 75 | # sum 76 | return idx, sum(r) 77 | elif typ == 1: 78 | # product 79 | return idx, prod(r) 80 | elif typ == 2: 81 | # min 82 | return idx, min(r) 83 | elif typ == 3: 84 | # max 85 | return idx, max(r) 86 | elif typ == 5: 87 | assert len(r) == 2 88 | return idx, 1 if r[0] > r[1] else 0 89 | elif typ == 6: 90 | assert len(r) == 2 91 | return idx, 1 if r[0] < r[1] else 0 92 | elif typ == 7: 93 | assert len(r) == 2 94 | return idx, 1 if r[0] == r[1] else 0 95 | else: 96 | assert False 97 | 98 | 99 | t = decode(bits, 0) 100 | print(sv) 101 | print(t) 102 | -------------------------------------------------------------------------------- /2021/day17.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | XMIN = 79 13 | XMAX = 137 14 | YMIN = -176 15 | YMAX = -117 16 | 17 | # XMIN = 20 18 | # XMAX = 30 19 | # YMIN = -10 20 | # YMAX = -5 21 | 22 | res = 0 23 | 24 | def simulate(vx, vy): 25 | x, y = 0, 0 26 | peak_y = 0 27 | good = False 28 | while True: 29 | x += vx 30 | y += vy 31 | peak_y = max(peak_y, y) 32 | if vx > 0: 33 | vx -= 1 34 | elif vx < 0: 35 | vx += 1 36 | vy -= 1 37 | if XMIN <= x <= XMAX and YMIN <= y <= YMAX: 38 | good = True 39 | break 40 | if x > XMAX: 41 | good = False 42 | break 43 | if y < YMIN: 44 | good = False 45 | break 46 | return good, peak_y 47 | 48 | res = 0 49 | 50 | s = set() 51 | for vx in range(1, 1000): 52 | for vy in range(-1000, 1000): 53 | g, r = simulate(vx, vy) 54 | if g: 55 | res = max(res, r) 56 | s.add((vx, vy)) 57 | 58 | print(res) 59 | print(len(s)) 60 | -------------------------------------------------------------------------------- /2021/day18.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day18.py < sample.in 14 | echo "=== real ===" ; py day18.py < day18.in 15 | echo "=== sample ===" ; py day18.py < sample.in ; echo "=== real ===" ; py day18.py < day18.in 16 | """ 17 | 18 | A = read_input('/dev/stdin') 19 | 20 | # f = open('/dev/stdin') 21 | # parts = f.read().strip().split('\n\n') 22 | 23 | N = len(A) 24 | 25 | def geti(num, L): 26 | for x in L: 27 | num = num[x] 28 | return num 29 | 30 | def seti(num, L, val): 31 | for x in L[:-1]: 32 | num = num[x] 33 | num[L[-1]] = val 34 | 35 | def addi(num, L, val): 36 | for x in L[:-1]: 37 | num = num[x] 38 | num[L[-1]] += val 39 | 40 | def explode_wrapper(num): 41 | explode_pair_loc = None 42 | explode_history_idx = None 43 | def explode(num, depth, stack, num_history): 44 | nonlocal explode_pair_loc 45 | nonlocal explode_history_idx 46 | depth += 1 47 | if isinstance(num, list): 48 | assert len(num) == 2 49 | if isinstance(num[0], int) and isinstance(num[1], int) and depth == 5 and explode_pair_loc is None: 50 | explode_pair_loc = stack 51 | explode_history_idx = len(num_history) 52 | explode(num[0], depth, stack + [0], num_history) 53 | explode(num[1], depth, stack + [1], num_history) 54 | else: 55 | num_history.append(stack) 56 | num_history = [] 57 | explode(num, 0, [], num_history) 58 | if explode_pair_loc is not None: 59 | val_l, val_r = geti(num, explode_pair_loc) 60 | # the add step 61 | idx_left = explode_history_idx - 1 62 | idx_right = explode_history_idx + 2 63 | if idx_left >= 0: 64 | addi(num, num_history[idx_left], val_l) 65 | if idx_right < len(num_history): 66 | addi(num, num_history[idx_right], val_r) 67 | seti(num, explode_pair_loc, 0) 68 | return True 69 | return False 70 | 71 | def split_wrapper(num): 72 | split_num_loc = None 73 | def split(num, stack): 74 | nonlocal split_num_loc 75 | if isinstance(num, list): 76 | assert len(num) == 2 77 | split(num[0], stack + [0]) 78 | split(num[1], stack + [1]) 79 | else: 80 | if num >= 10 and split_num_loc is None: 81 | split_num_loc = stack 82 | split(num, []) 83 | if split_num_loc is not None: 84 | val = geti(num, split_num_loc) 85 | val_l = val // 2 86 | val_r = val // 2 87 | if val % 2 == 1: 88 | val_r += 1 89 | seti(num, split_num_loc, [val_l, val_r]) 90 | return True 91 | return False 92 | 93 | def mag(num): 94 | if isinstance(num, list): 95 | return mag(num[0]) * 3 + mag(num[1]) * 2 96 | return num 97 | 98 | acc = eval(A[0]) 99 | 100 | for i in range(1, N): 101 | b = eval(A[i]) 102 | acc = [acc, b] 103 | while True: 104 | if explode_wrapper(acc): 105 | continue 106 | if split_wrapper(acc): 107 | continue 108 | break 109 | 110 | print(acc) 111 | print(mag(acc)) 112 | 113 | res = 0 114 | for i in range(N): 115 | for j in range(N): 116 | if i == j: 117 | continue 118 | a = eval(A[i]) 119 | b = eval(A[j]) 120 | acc = [a, b] 121 | while True: 122 | if explode_wrapper(acc): 123 | continue 124 | if split_wrapper(acc): 125 | continue 126 | break 127 | res = max(res, mag(acc)) 128 | print(res) 129 | -------------------------------------------------------------------------------- /2021/day19.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day19.py < sample.in 14 | echo "=== real ===" ; py day19.py < day19.in 15 | echo "=== sample ===" ; py day19.py < sample.in ; echo "=== real ===" ; py day19.py < day19.in 16 | """ 17 | 18 | # A = read_input('/dev/stdin') 19 | 20 | f = open('/dev/stdin') 21 | parts = f.read().strip().split('\n\n') 22 | 23 | def gen_orientations(): 24 | # 48 25 | for i in (-1, 1): 26 | for j in (-1, 1): 27 | for k in (-1, 1): 28 | for rest in itertools.permutations(range(3)): 29 | yield rest + (i, j, k) 30 | 31 | def transform_forward(orientation, coord): 32 | x, y, z, xp, yp, zp = orientation 33 | res = [None] * 3 34 | res[x] = coord[0] * xp 35 | res[y] = coord[1] * yp 36 | res[z] = coord[2] * zp 37 | return tuple(res) 38 | 39 | def transform_backward(orientation, coord): 40 | x, y, z, xp, yp, zp = orientation 41 | res = [None] * 3 42 | res[0] = coord[x] * xp 43 | res[1] = coord[y] * yp 44 | res[2] = coord[z] * zp 45 | return tuple(res) 46 | 47 | def addc(a, b): 48 | res = [] 49 | for i in range(len(a)): 50 | res.append(a[i] + b[i]) 51 | return tuple(res) 52 | def subc(a, b): 53 | res = [] 54 | for i in range(len(a)): 55 | res.append(a[i] - b[i]) 56 | return tuple(res) 57 | 58 | COMMON = 12 59 | RADIUS = 1000 60 | 61 | res = 0 62 | 63 | A = defaultdict(list) 64 | 65 | for part in parts: 66 | part = part.strip().splitlines() 67 | sid = int(part[0].strip().split()[2]) 68 | for line in part[1:]: 69 | coord = tuple(map(int, line.strip().split(','))) 70 | A[sid].append(coord) 71 | 72 | SCANNERS = len(A) 73 | 74 | locations = [None] * SCANNERS 75 | orientations = [None] * SCANNERS 76 | locations[0] = (0, 0, 0) 77 | orientations[0] = (0, 1, 2, 1, 1, 1) 78 | 79 | all_beacons = set() 80 | 81 | bad = set() 82 | 83 | while True: 84 | for s1 in range(SCANNERS): 85 | if locations[s1] is None: 86 | continue 87 | i_absolute_seen = [] 88 | for coord in A[s1]: 89 | i_absolute_seen.append(addc(locations[s1], transform_forward(orientations[s1], coord))) 90 | i_absolute_seen_set = set(i_absolute_seen) 91 | for s2 in range(SCANNERS): 92 | if s1 == s2 or locations[s2] is not None or (s1, s2) in bad: 93 | continue 94 | # s1 known, s2 is not known 95 | print('trying', s1, s2) 96 | for ori in gen_orientations(): 97 | all_rel = [] 98 | for coord in A[s2]: 99 | # try it 100 | rel = transform_forward(ori, coord) 101 | all_rel.append(rel) 102 | # could s1 and s2 see some subset? 103 | for i in range(len(i_absolute_seen)): 104 | for j in range(len(all_rel)): 105 | # assume j is at the absolute position of the point i 106 | shift = subc(i_absolute_seen[i], all_rel[j]) 107 | match_count = 0 108 | for j in range(len(all_rel)): 109 | pt = addc(all_rel[j], shift) 110 | if pt in i_absolute_seen_set: 111 | match_count += 1 112 | if match_count >= COMMON: 113 | break 114 | if match_count >= COMMON: 115 | # found it 116 | print('found', s2) 117 | locations[s2] = shift 118 | orientations[s2] = ori 119 | break 120 | if locations[s2] is not None: 121 | break 122 | if locations[s2] is not None: 123 | break 124 | if locations[s2] is None: 125 | bad.add((s1, s2)) 126 | if locations[s2] is not None: 127 | break 128 | 129 | if all(locations): 130 | break 131 | 132 | for s1 in range(SCANNERS): 133 | for coord in A[s1]: 134 | all_beacons.add(addc(locations[s1], transform_forward(orientations[s1], coord))) 135 | 136 | 137 | print(len(all_beacons)) 138 | print(locations) 139 | res = 0 140 | for s1 in range(SCANNERS): 141 | for s2 in range(SCANNERS): 142 | x, y, z = subc(locations[s1], locations[s2]) 143 | res = max(res, abs(x) + abs(y) + abs(z)) 144 | print(res) 145 | -------------------------------------------------------------------------------- /2021/day2.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | 8 | from util import * 9 | 10 | """ 11 | echo "=== sample ===" ; py day2.py < sample.in ; echo "=== real ===" ; py day2.py < day2.in 12 | """ 13 | A = read_input('/dev/stdin') 14 | N = len(A) 15 | 16 | s = set() 17 | 18 | horiz = 0 19 | depth = 0 20 | aim = 0 21 | 22 | for line in A: 23 | cmd, x = line.split() 24 | x = int(x) 25 | if cmd == 'forward': 26 | horiz += x 27 | depth += aim * x 28 | elif cmd == 'down': 29 | aim += x 30 | elif cmd == 'up': 31 | aim -= x 32 | 33 | print(horiz, depth, horiz * depth) 34 | -------------------------------------------------------------------------------- /2021/day20.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day20.py < sample.in 14 | echo "=== real ===" ; py day20.py < day20.in 15 | echo "=== sample ===" ; py day20.py < sample.in ; echo "=== real ===" ; py day20.py < day20.in 16 | """ 17 | 18 | # A = read_input('/dev/stdin') 19 | 20 | f = open('/dev/stdin') 21 | parts = f.read().strip().split('\n\n') 22 | 23 | alg = ''.join(parts[0].strip().splitlines()) 24 | 25 | img = parts[1].strip().splitlines() 26 | 27 | 28 | def automata(grid, iterations): 29 | flip = False 30 | for _ in range(iterations): 31 | if not flip: 32 | new_grid = defaultdict(lambda: '#') 33 | else: 34 | new_grid = defaultdict(lambda: '.') 35 | 36 | min_x = min(x for x, y in grid.keys()) 37 | max_x = max(x for x, y in grid.keys()) 38 | min_y = min(y for x, y in grid.keys()) 39 | max_y = max(y for x, y in grid.keys()) 40 | for i in range(min_x - 1, max_x + 2): 41 | for j in range(min_y - 1, max_y + 2): 42 | s = '' 43 | for ii in range(-1, 2): 44 | for jj in range(-1, 2): 45 | s += grid[(i + ii, j + jj)] 46 | s = s.replace('#', '1') 47 | s = s.replace('.', '0') 48 | idx = int(s, 2) 49 | new_grid[i, j] = alg[idx] 50 | grid = new_grid 51 | flip = not flip 52 | return grid 53 | 54 | res = 0 55 | R = len(img) 56 | C = len(img[0]) 57 | grid = defaultdict(lambda: '.') 58 | 59 | 60 | for i in range(R): 61 | for j in range(C): 62 | grid[i, j] = img[i][j] 63 | 64 | 65 | res = automata(grid, 50) 66 | t = 0 67 | for k, v in res.items(): 68 | if v == '#': 69 | t += 1 70 | print(t) 71 | # != 5944 72 | # != 7604 73 | -------------------------------------------------------------------------------- /2021/day21.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day21.py < sample.in 14 | echo "=== real ===" ; py day21.py < day21.in 15 | echo "=== sample ===" ; py day21.py < sample.in ; echo "=== real ===" ; py day21.py < day21.in 16 | """ 17 | 18 | res = 0 19 | 20 | START = [7, 5] 21 | # START = [4, 8] 22 | THRESHOLD = 1000 23 | 24 | ree = 1 25 | rc = 0 26 | def roll(): 27 | global ree, rc 28 | res = ree 29 | ree += 1 30 | if ree > 100: 31 | ree = 1 32 | rc += 1 33 | return res 34 | 35 | 36 | p1, p2 = START 37 | p1s, p2s = 0, 0 38 | while True: 39 | s = sum([roll() for _ in range(3)]) 40 | p1 += s 41 | while p1 > 10: 42 | p1 -= 10 43 | p1s += p1 44 | if p1s >= THRESHOLD: 45 | break 46 | s = sum([roll() for _ in range(3)]) 47 | p2 += s 48 | while p2 > 10: 49 | p2 -= 10 50 | p2s += p2 51 | if p2s >= THRESHOLD: 52 | break 53 | 54 | print(min(p1s, p2s) * rc) 55 | 56 | 57 | p1win = 0 58 | p2win = 0 59 | THRESHOLD = 21 60 | ctr = Counter() 61 | for i in range(1, 4): 62 | for j in range(1, 4): 63 | for k in range(1, 4): 64 | d = i + j + k 65 | ctr[d] += 1 66 | 67 | def go(p1turn, p1, p2, p1s, p2s, mult): 68 | global p1win, p2win 69 | 70 | for k, v in ctr.items(): 71 | np1 = p1 + k 72 | while np1 > 10: 73 | np1 -= 10 74 | np1s = p1s + np1 75 | if np1s >= THRESHOLD: 76 | if p1turn: 77 | p1win += v * mult 78 | else: 79 | p2win += v * mult 80 | continue 81 | go(not p1turn, p2, np1, p2s, np1s, mult * v) 82 | 83 | go(True, START[0], START[1], 0, 0, 1) 84 | 85 | print(p1win, p2win, max(p1win, p2win)) 86 | -------------------------------------------------------------------------------- /2021/day22.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day22.py < sample.in 14 | echo "=== real ===" ; py day22.py < day22.in 15 | echo "=== sample ===" ; py day22.py < sample.in ; echo "=== real ===" ; py day22.py < day22.in 16 | """ 17 | 18 | A = read_input('/dev/stdin') 19 | 20 | def add_interval(ss, L, R): 21 | assert L <= R 22 | if L == R: 23 | return None 24 | idx = ss.bisect_left((L, R)) 25 | while idx < len(ss): 26 | ival = ss[idx] 27 | if ival[0] > R: 28 | break 29 | R = max(R, ival[1]) 30 | ss.pop(idx) 31 | if idx > 0: 32 | idx -= 1 33 | ival = ss[idx] 34 | if ival[1] >= L: 35 | L = min(L, ival[0]) 36 | R = max(R, ival[1]) 37 | ss.pop(idx) 38 | res = (L, R) 39 | ss.add(res) 40 | return res 41 | 42 | def remove_interval(ss, L, R): 43 | assert L <= R 44 | if L == R: 45 | return 46 | added = add_interval(ss, L, R) 47 | r2 = added[1] 48 | ss.remove(added) 49 | if added[0] != L: 50 | ss.add((added[0], L)) 51 | if R != r2: 52 | ss.add((R, r2)) 53 | 54 | res = 0 55 | 56 | N = len(A) 57 | 58 | d = defaultdict(int) 59 | 60 | B = [] 61 | 62 | for line in A: 63 | a, b = line.split(' ') 64 | x, y, z = b.split(',') 65 | 66 | L = [] 67 | for c in (x, y, z): 68 | lo, hi = map(int, c[2:].split('..')) 69 | # if lo < -50: lo = -50 70 | # if hi > 50: hi = 50 71 | L.append((lo, hi)) 72 | if a == 'on': 73 | L.append(1) 74 | elif a == 'off': 75 | L.append(0) 76 | else: 77 | assert False 78 | B.append(L) 79 | 80 | 81 | z_events = defaultdict(list) 82 | 83 | for x, y, z, a in B: 84 | z_events[z[0]].append(0) 85 | z_events[z[1] + 1].append(1) 86 | 87 | 88 | 89 | def get_cells_on_at_z(desired_z): 90 | events = defaultdict(list) 91 | for x, y, z, a in B: 92 | if z[0] <= desired_z <= z[1]: 93 | xlo, xhi = x[0], x[1] 94 | ylo, yhi = y[0], y[1] 95 | events[ylo].append(0) 96 | events[yhi + 1].append(1) 97 | active = 0 98 | last_y = -99999999999 99 | res = 0 100 | for evt in sorted(events.keys()): 101 | ree = SortedSet() 102 | for x, y, z, a in B: 103 | if y[0] <= evt <= y[1] and z[0] <= desired_z <= z[1]: 104 | if a: 105 | add_interval(ree, x[0], x[1] + 1) 106 | else: 107 | remove_interval(ree, x[0], x[1] + 1) 108 | nactive = sum(b - a for a, b in ree) 109 | res += (evt - last_y) * active 110 | active = nactive 111 | last_y = evt 112 | assert active == 0 113 | return res 114 | 115 | res = 0 116 | active = 0 117 | last_z = -99999999999 118 | for evt in sorted(z_events.keys()): 119 | grid = defaultdict(int) 120 | nactive = get_cells_on_at_z(evt) 121 | res += (evt - last_z) * active 122 | active = nactive 123 | last_z = evt 124 | assert active == 0 125 | print(res) 126 | 127 | 128 | 129 | 130 | 131 | # for x in range(L[0][0], L[0][1] + 1): 132 | # for y in range(L[1][0], L[1][1] + 1): 133 | # for z in range(L[2][0], L[2][1] + 1): 134 | # if a == 'on': 135 | # d[(x, y, z)] = 1 136 | # elif a == 'off': 137 | # d[(x, y, z)] = 0 138 | # else: 139 | # assert False 140 | 141 | # for k in range(-50, 51): 142 | # res += d[(i, j, k)] 143 | 144 | # print(res) 145 | -------------------------------------------------------------------------------- /2021/day23.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day23.py < sample.in 14 | echo "=== real ===" ; py day23.py < day23.in 15 | echo "=== sample ===" ; py day23.py < sample.in ; echo "=== real ===" ; py day23.py < day23.in 16 | """ 17 | 18 | 19 | from heapq import * 20 | 21 | 22 | A = read_input('/dev/stdin', strip_lines=False) 23 | 24 | N = len(A) 25 | 26 | energy = { 27 | 'A': 1, 28 | 'B': 10, 29 | 'C': 100, 30 | 'D': 1000, 31 | } 32 | 33 | 34 | free_cells = [] 35 | res = 0 36 | 37 | # ..x.x.x.x.. 38 | # . . . . 39 | # . . . . 40 | 41 | 42 | Q = [] 43 | 44 | R = len(A) 45 | C = len(A[0]) 46 | 47 | for i in range(R): 48 | if len(A[i]) < C: 49 | A[i] += ' ' * (C - len(A[i])) 50 | 51 | for i in range(2): 52 | for j in range(C): 53 | if A[i][j] != '#': 54 | if i == 1 and j in (3, 5, 7, 9): 55 | continue 56 | free_cells.append((i, j)) 57 | dest_cells = defaultdict(list) 58 | 59 | m1 = { 60 | 'A': 3, 61 | 'B': 5, 62 | 'C': 7, 63 | 'D': 9, 64 | } 65 | 66 | thing_to_dest_col = { 67 | 0: 3, 68 | 1: 5, 69 | 2: 7, 70 | 3: 9, 71 | } 72 | 73 | dest_col_to_thing = {v: k for k, v in thing_to_dest_col.items()} 74 | 75 | energy2 = [ 76 | 1, 77 | 10, 78 | 100, 79 | 1000, 80 | ] 81 | 82 | state = defaultdict(list) 83 | 84 | for i in range(2, R): 85 | for j in range(C): 86 | if A[i][j] != '#' and A[i][j] != ' ': 87 | thing = A[i][j] 88 | state[thing].append((i, j)) 89 | dest_cells[j].append((i, j)) 90 | DIV = len(dest_cells[3]) 91 | NUM_THINGS = DIV * 4 92 | 93 | def solved(state): 94 | good = True 95 | for i in range(4): 96 | idx = i * DIV 97 | for j in range(DIV): 98 | a = state[i * DIV + j] 99 | if a[0] >= 2 and a[1] == thing_to_dest_col[i]: 100 | pass 101 | else: 102 | good = False 103 | break 104 | return good 105 | 106 | def get_neighbors(i, j): 107 | for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)): 108 | if 0 <= ii < R and 0 <= jj < C and A[ii][jj] != '#': 109 | yield ii, jj 110 | 111 | 112 | initial = [] 113 | for x in 'ABCD': 114 | initial += state[x] 115 | assert len(initial) == NUM_THINGS 116 | 117 | 118 | def bfs(src, dst, state): 119 | Q = [] 120 | visited = set() 121 | Q.append((0, src)) 122 | visited.add(src) 123 | for d, u in Q: 124 | if u == dst: 125 | return d 126 | for ii, jj in get_neighbors(*u): 127 | if (ii, jj) in state: 128 | continue 129 | if (ii, jj) in visited: 130 | continue 131 | visited.add((ii, jj)) 132 | Q.append((d + 1, (ii, jj))) 133 | return INF 134 | 135 | assert len(free_cells) == 7 136 | 137 | heappush(Q, (0, tuple(initial))) 138 | dist = defaultdict(lambda: INF) 139 | 140 | def dump_state(state): 141 | for i in range(R): 142 | r = [] 143 | for j in range(C): 144 | try: 145 | idx = state.index((i, j)) 146 | r.append(str(idx)) 147 | except ValueError: 148 | if A[i][j] >= 'A' and A[i][j] <= 'D': 149 | r.append('.') 150 | else: 151 | r.append(A[i][j]) 152 | print(''.join(r)) 153 | 154 | import random 155 | dist[tuple(initial)] = 0 156 | dump_state(tuple(initial)) 157 | 158 | def get_contents(state, coord): 159 | if coord in state: 160 | return state.index(coord) 161 | return None 162 | 163 | def get_valid_room_dest(state): 164 | for col in dest_col_to_thing.keys(): 165 | order = dest_cells[col][::-1] 166 | for cell in order: 167 | if cell not in state: 168 | yield cell 169 | break 170 | 171 | def cell_is_done(state, cell): 172 | if cell[0] < 2: 173 | return False 174 | idx = state.index(cell) 175 | thing = idx // DIV 176 | if cell[1] != thing_to_dest_col[thing]: 177 | return False 178 | for c in dest_cells[cell[1]]: 179 | if c[0] <= cell[0]: 180 | continue 181 | fuck = get_contents(state, c) 182 | if fuck is None: 183 | return False 184 | if fuck // DIV != thing: 185 | return False 186 | return True 187 | 188 | 189 | while Q: 190 | d, state = heappop(Q) 191 | 192 | if solved(state): 193 | print('ans:', d) 194 | break 195 | if d > dist[state]: 196 | continue 197 | valid_room_dest = list(get_valid_room_dest(state)) 198 | valid_room_dest_by_thing = [None] * 4 199 | for a, b in valid_room_dest: 200 | valid_room_dest_by_thing[dest_col_to_thing[b]] = a, b 201 | for i in range(NUM_THINGS): 202 | # try moving thing i 203 | if cell_is_done(state, state[i]): 204 | continue 205 | thing = i // DIV 206 | dest_col = thing_to_dest_col[thing] 207 | if state[i][0] == 1: 208 | # free 209 | valid_targets = [] 210 | for a, b in valid_room_dest: 211 | if b == dest_col: 212 | valid_targets = [(a, b)] 213 | else: 214 | # in a room 215 | valid_targets = itertools.chain(free_cells, valid_room_dest) 216 | 217 | 218 | # valid_targets = itertools.chain(free_cells, valid_room_dest) 219 | 220 | for dst in valid_targets: 221 | new_state = tuple(dst if j == i else state[j] for j in range(NUM_THINGS)) 222 | if dst in state: 223 | continue 224 | if dst[0] >= 2: 225 | if dst[1] != dest_col: 226 | # wrong room 227 | continue 228 | if not cell_is_done(new_state, dst): 229 | continue 230 | 231 | if state[i][0] == 1 and dst[0] == 1: 232 | # shuffling 233 | continue 234 | delta = bfs(state[i], dst, state) 235 | if delta >= INF: 236 | continue 237 | new_d = d + energy2[thing] * delta 238 | if new_d < dist[new_state]: 239 | dist[new_state] = new_d 240 | heappush(Q, (new_d, new_state)) 241 | -------------------------------------------------------------------------------- /2021/day24.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day24.py < sample.in 14 | echo "=== real ===" ; py day24.py < day24.in 15 | echo "=== sample ===" ; py day24.py < sample.in ; echo "=== real ===" ; py day24.py < day24.in 16 | """ 17 | 18 | A = read_input('/dev/stdin', strip_lines=False) 19 | 20 | N = len(A) 21 | 22 | 23 | def simulate(inp): 24 | ip = 0 25 | assert len(A) % 18 == 0 26 | res = [] 27 | regs = defaultdict(int) 28 | while ip < len(A): 29 | ins = A[ip].split() 30 | op = ins[0] 31 | if op == 'inp': 32 | val = int(next(inp)) 33 | regs[ins[1]] = val 34 | elif op == 'add': 35 | a, b = map(maybe_int, ins[1:]) 36 | if isinstance(b, str): 37 | regs[a] += regs[b] 38 | else: 39 | regs[a] += b 40 | elif op == 'mul': 41 | a, b = map(maybe_int, ins[1:]) 42 | if isinstance(b, str): 43 | regs[a] *= regs[b] 44 | else: 45 | regs[a] *= b 46 | elif op == 'div': 47 | a, b = map(maybe_int, ins[1:]) 48 | if isinstance(b, str): 49 | regs[a] //= regs[b] 50 | else: 51 | regs[a] //= b 52 | elif op == 'mod': 53 | a, b = map(maybe_int, ins[1:]) 54 | if isinstance(b, str): 55 | regs[a] %= regs[b] 56 | else: 57 | regs[a] %= b 58 | elif op == 'eql': 59 | a, b = map(maybe_int, ins[1:]) 60 | if isinstance(b, str): 61 | val = regs[b] 62 | else: 63 | val = b 64 | if regs[a] == val: 65 | regs[a] = 1 66 | else: 67 | regs[a] = 0 68 | ip += 1 69 | if ip % 18 == 0: 70 | res.append(regs['z']) 71 | return res 72 | 73 | res = 0 74 | 75 | # x = ((z % 26 + A) != inp) 76 | # z /= divz 77 | # z *= (25 * x) + 1 78 | # z += (inp + B) * x 79 | 80 | parts = N // 18 81 | 82 | B = [] 83 | 84 | for i in range(parts): 85 | base = i * 18 86 | divz = int(A[base + 4].split()[2]) 87 | add1 = int(A[base + 5].split()[2]) 88 | add2 = int(A[base + 15].split()[2]) 89 | B.append((divz, add1, add2)) 90 | 91 | from z3 import * 92 | 93 | inp = [Int('inp{}'.format(i)) for i in range(14)] 94 | s = Solver() 95 | for x in inp: 96 | s.add(x >= 1) 97 | s.add(x <= 9) 98 | 99 | s.add(inp[0] == 4) 100 | s.add(inp[1] <= 5) 101 | # s.add(inp[2] == 9) 102 | # s.add(inp[3] == 9) 103 | # s.add(inp[4] == 9) 104 | # s.add(inp[5] >= 7) 105 | 106 | print(B) 107 | 108 | z = Int('z') 109 | s.add(z == 0) 110 | for i in range(len(B)): 111 | divz, add1, add2 = B[i] 112 | x = If((z % 26 + add1) != inp[i], 1, 0) 113 | if divz != 1: 114 | z = z / divz 115 | # z = z * ((25 * x) + 1) 116 | z = If(x == 1, z * 26, z) 117 | z = If(x == 1, z + inp[i] + add2, z) 118 | 119 | s.add(z == 0) 120 | 121 | 122 | best = int(1e19) 123 | 124 | while s.check() == sat: 125 | m = s.model() 126 | res = [] 127 | dup = [] 128 | for i, e in enumerate(inp): 129 | x = m[e].as_long() 130 | res.append(x) 131 | dup.append(e != x) 132 | y = int(''.join(map(str, res))) 133 | if y < best: 134 | print(y) 135 | best = y 136 | best = min(best, y) 137 | s.add(Or(*dup)) 138 | print('ans:', best) 139 | -------------------------------------------------------------------------------- /2021/day25.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day25.py < sample.in 14 | echo "=== real ===" ; py day25.py < day25.in 15 | echo "=== sample ===" ; py day25.py < sample.in ; echo "=== real ===" ; py day25.py < day25.in 16 | """ 17 | 18 | A = read_input('/dev/stdin', t=list, strip_lines=True) 19 | 20 | N = len(A) 21 | 22 | res = 0 23 | 24 | R = N 25 | C = len(A[0]) 26 | 27 | it = 0 28 | while True: 29 | moved = False 30 | 31 | delete = set() 32 | new = {} 33 | for i in range(R): 34 | for j in range(C): 35 | t = A[i][j] 36 | if t == '>': 37 | ii, jj = i, j + 1 38 | if jj >= C: 39 | jj = 0 40 | if A[ii][jj] == '.': 41 | moved = True 42 | delete.add((i, j)) 43 | new[ii, jj] = '>' 44 | for i, j in delete: 45 | A[i][j] = '.' 46 | for (ii, jj), t in new.items(): 47 | A[ii][jj] = t 48 | 49 | delete = set() 50 | new = {} 51 | for i in range(R): 52 | for j in range(C): 53 | t = A[i][j] 54 | if t == 'v': 55 | ii, jj = i + 1, j 56 | if ii >= R: 57 | ii = 0 58 | if A[ii][jj] == '.': 59 | moved = True 60 | delete.add((i, j)) 61 | new[ii, jj] = 'v' 62 | for i, j in delete: 63 | A[i][j] = '.' 64 | for (ii, jj), t in new.items(): 65 | A[ii][jj] = t 66 | 67 | it += 1 68 | if not moved: 69 | break 70 | 71 | print(it) 72 | -------------------------------------------------------------------------------- /2021/day3.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | 8 | from util import * 9 | 10 | """ 11 | echo "=== sample ===" ; py day3.py < sample.in ; echo "=== real ===" ; py day3.py < day3.in 12 | """ 13 | A = read_input('/dev/stdin') 14 | N = len(A) 15 | 16 | M = len(A[0]) 17 | 18 | def go(arr, b): 19 | At = transpose(arr) 20 | ones = At[b].count('1') 21 | zeros = At[b].count('0') 22 | total = len(At[b]) 23 | if ones >= zeros: 24 | res = '1' 25 | res2 = '0' 26 | else: 27 | res = '0' 28 | res2 = '1' 29 | 30 | B = [] 31 | C = [] 32 | for line in arr: 33 | if line[b] == res: 34 | B.append(line) 35 | if line[b] == res2: 36 | C.append(line) 37 | return B, C 38 | 39 | arr1 = A 40 | arr2 = A 41 | 42 | for i in range(M): 43 | if len(arr1) > 1: 44 | arr1, _ = go(arr1, i) 45 | if len(arr2) > 1: 46 | _, arr2 = go(arr2, i) 47 | if len(arr1) == 1 and len(arr2) == 1: 48 | break 49 | 50 | print(arr1, arr2, int(arr1[0], 2) * int(arr2[0], 2)) 51 | -------------------------------------------------------------------------------- /2021/day4.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day4.py < sample.in 14 | echo "=== real ===" ; py day4.py < day4.in 15 | echo "=== sample ===" ; py day4.py < sample.in ; echo "=== real ===" ; py day4.py < day4.in 16 | """ 17 | A = read_input('/dev/stdin') 18 | N = len(A) 19 | R = N 20 | 21 | order = map(int, A[0].split(',')) 22 | 23 | A = A[2:] 24 | 25 | idx = 0 26 | 27 | boards = [] 28 | marked = [] 29 | 30 | def gen_empty_mark(): 31 | return [[False] * 5 for _ in range(5)] 32 | 33 | def is_win(idx): 34 | mark = marked[idx] 35 | # row 36 | for row in mark: 37 | if all(row): 38 | return True 39 | # col 40 | for j in range(5): 41 | if all(mark[i][j] for i in range(5)): 42 | return True 43 | return False 44 | 45 | while True: 46 | if idx >= len(A): 47 | break 48 | 49 | board = [] 50 | for i in range(5): 51 | line = A[idx] 52 | board.append(map(maybe_int, line.split())) 53 | idx += 1 54 | idx += 1 55 | boards.append(board) 56 | marked.append(gen_empty_mark()) 57 | 58 | # print(boards) 59 | 60 | N = len(boards) 61 | 62 | def sum_unmarked(idx): 63 | res = 0 64 | for i in range(5): 65 | for j in range(5): 66 | if not marked[idx][i][j]: 67 | res += boards[idx][i][j] 68 | return res 69 | 70 | already_won = [False] * N 71 | 72 | for num in order: 73 | for idx in range(N): 74 | for i in range(5): 75 | for j in range(5): 76 | if boards[idx][i][j] == num: 77 | marked[idx][i][j] = True 78 | 79 | for idx in range(N): 80 | if is_win(idx) and not already_won[idx]: 81 | already_won[idx] = True 82 | print(num * sum_unmarked(idx)) 83 | -------------------------------------------------------------------------------- /2021/day5.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day5.py < sample.in 14 | echo "=== real ===" ; py day5.py < day5.in 15 | echo "=== sample ===" ; py day5.py < sample.in ; echo "=== real ===" ; py day5.py < day5.in 16 | """ 17 | A = read_input('/dev/stdin') 18 | N = len(A) 19 | R = N 20 | 21 | lines = [] 22 | 23 | for line in A: 24 | a, b = line.split(' -> ') 25 | a1, a2 = map(int, a.split(',')) 26 | b1, b2 = map(int, b.split(',')) 27 | lines.append(((a1, a2), (b1, b2))) 28 | 29 | count = defaultdict(int) 30 | 31 | for line in lines: 32 | a, b = line 33 | if a[0] == b[0]: 34 | u, v = sorted((a[1], b[1])) 35 | for i in range(u, v + 1): 36 | count[(a[0], i)] += 1 37 | elif a[1] == b[1]: 38 | u, v = sorted((a[0], b[0])) 39 | for i in range(u, v + 1): 40 | count[(i, a[1])] += 1 41 | else: 42 | if a[0] > b[0]: 43 | a, b = b, a 44 | # diag up 45 | if a[1] < b[1]: 46 | for i in range(a[0], b[0] + 1): 47 | count[(i, a[1] + i - a[0])] += 1 48 | else: 49 | for i in range(a[0], b[0] + 1): 50 | count[(i, a[1] - i + a[0])] += 1 51 | # diag down 52 | 53 | res = 0 54 | for k, v in count.items(): 55 | if v >= 2: 56 | res += 1 57 | print(res) 58 | -------------------------------------------------------------------------------- /2021/day6.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day6.py < sample.in 14 | echo "=== real ===" ; py day6.py < day6.in 15 | echo "=== sample ===" ; py day6.py < sample.in ; echo "=== real ===" ; py day6.py < day6.in 16 | """ 17 | A = read_input('/dev/stdin') 18 | 19 | A = map(int, A.split(',')) 20 | N = len(A) 21 | 22 | DAYS = 256 23 | 24 | ctr = [0] * 9 25 | 26 | for x in A: 27 | ctr[x] += 1 28 | 29 | 30 | for d in range(DAYS): 31 | new_ctr = [0] * 9 32 | for i, e in enumerate(ctr): 33 | if i == 0: 34 | new_ctr[8] += e 35 | new_ctr[6] += e 36 | else: 37 | new_ctr[i - 1] += e 38 | ctr = new_ctr 39 | 40 | print(sum(ctr)) -------------------------------------------------------------------------------- /2021/day7.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day7.py < sample.in 14 | echo "=== real ===" ; py day7.py < day7.in 15 | echo "=== sample ===" ; py day7.py < sample.in ; echo "=== real ===" ; py day7.py < day7.in 16 | """ 17 | A = read_input('/dev/stdin') 18 | 19 | A = map(int, A.split(',')) 20 | N = len(A) 21 | 22 | A.sort() 23 | 24 | res = int(2e9) 25 | 26 | 27 | for i in range(min(A), max(A) + 1): 28 | acc = 0 29 | for x in A: 30 | dist = abs(x - i) 31 | # acc += dist 32 | acc += dist * (dist + 1) // 2 33 | res = min(res, acc) 34 | 35 | print(res) -------------------------------------------------------------------------------- /2021/day8.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day8.py < sample.in 14 | echo "=== real ===" ; py day8.py < day8.in 15 | echo "=== sample ===" ; py day8.py < sample.in ; echo "=== real ===" ; py day8.py < day8.in 16 | """ 17 | A = read_input('/dev/stdin') 18 | N = len(A) 19 | 20 | valid = [ 21 | 'abcefg', 22 | 'cf', # 23 | 'acdeg', 24 | 'acdfg', 25 | 'bcdf', # 26 | 'abdfg', 27 | 'abdefg', 28 | 'acf', # 29 | 'abcdefg', # 30 | 'abcdfg', 31 | ] 32 | 33 | def identify(ten): 34 | assert len(ten) == 10 35 | res = {} 36 | easy = { 37 | 2: 1, 38 | 3: 7, 39 | 4: 4, 40 | 7: 8, 41 | } 42 | for mapping in itertools.permutations(range(7)): 43 | good = True 44 | for thing in ten: 45 | canon = ''.join(sorted(thing)) 46 | t = '' 47 | for ch in canon: 48 | x = mapping[ord(ch) - ord('a')] 49 | x = chr(x + ord('a')) 50 | t += x 51 | t = ''.join(sorted(t)) 52 | if t not in valid: 53 | good = False 54 | break 55 | if good: 56 | return mapping 57 | assert False 58 | 59 | 60 | 61 | res = 0 62 | for line in A: 63 | a, b = line.split(' | ') 64 | for part in b.split(' '): 65 | if len(part) in (2, 3, 7, 4): 66 | res += 1 67 | print(res) 68 | 69 | res = 0 70 | 71 | for line in A: 72 | a, b = line.split(' | ') 73 | a = a.split(' ') 74 | m = identify(a) 75 | 76 | tt = '' 77 | for digit in b.split(' '): 78 | t = '' 79 | canon = ''.join(sorted(digit)) 80 | for ch in canon: 81 | x = m[ord(ch) - ord('a')] 82 | x = chr(x + ord('a')) 83 | t += x 84 | t = ''.join(sorted(t)) 85 | d = valid.index(t) 86 | tt += str(d) 87 | 88 | tt = int(tt) 89 | res += tt 90 | print(res) -------------------------------------------------------------------------------- /2021/day9.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day9.py < sample.in 14 | echo "=== real ===" ; py day9.py < day9.in 15 | echo "=== sample ===" ; py day9.py < sample.in ; echo "=== real ===" ; py day9.py < day9.in 16 | """ 17 | A = read_input('/dev/stdin') 18 | N = len(A) 19 | 20 | res = 0 21 | 22 | R = N 23 | C = len(A[0]) 24 | 25 | def get_neighbors(i, j): 26 | for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)): 27 | # for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1), (i - 1, j - 1), (i - 1, j + 1), (i + 1, j - 1), (i + 1, j + 1)): 28 | if 0 <= ii < R and 0 <= jj < C: 29 | yield ii, jj 30 | 31 | lpts = [] 32 | 33 | for i in range(R): 34 | for j in range(C): 35 | low = True 36 | for ii, jj in get_neighbors(i, j): 37 | if A[ii][jj] <= A[i][j]: 38 | low = False 39 | break 40 | if low: 41 | lpts.append((i, j)) 42 | res += 1 + int(A[i][j]) 43 | print(res) 44 | 45 | ls = set(lpts) 46 | 47 | def flow(i, j, s): 48 | s.add((i, j)) 49 | if (i, j) in ls: 50 | return (i, j) 51 | 52 | basin = None 53 | for ii, jj in get_neighbors(i, j): 54 | if A[ii][jj] < A[i][j]: 55 | basin = flow(ii, jj, s) 56 | return basin 57 | 58 | r2 = defaultdict(set) 59 | 60 | for i in range(R): 61 | for j in range(C): 62 | if A[i][j] == '9': 63 | continue 64 | s = set() 65 | basin = flow(i, j, s) 66 | r2[basin] |= s 67 | 68 | L = sorted(r2.items(), key=lambda x: -len(x[1])) 69 | 70 | print(prod(map(lambda x: len(x[1]), L[:3]))) 71 | -------------------------------------------------------------------------------- /2021/util.py: -------------------------------------------------------------------------------- 1 | import re 2 | from operator import add 3 | from collections import deque, defaultdict, Counter 4 | import copy 5 | 6 | import sys 7 | sys.setrecursionlimit(int(1e7)) 8 | 9 | # convention that positive y is down 10 | # increment to clockwise/turn right, decrement to counterclockwise/turn left 11 | DIRS = { 12 | 0: (0, -1), 13 | 1: (1, 0), 14 | 2: (0, 1), 15 | 3: (-1, 0), 16 | } 17 | 18 | DIRS_M = { 19 | 'U': 0, 20 | 'R': 1, 21 | 'D': 2, 22 | 'L': 3, 23 | 'N': 0, 24 | 'E': 1, 25 | 'S': 2, 26 | 'W': 3, 27 | } 28 | 29 | INF = float('inf') 30 | 31 | class UniqueQueue(): 32 | def __init__(self, contents=None): 33 | self.deque = deque() 34 | self.set = set() 35 | if contents is not None: 36 | for x in contents: 37 | self.push(x) 38 | 39 | def __len__(self): 40 | return len(self.deque) 41 | 42 | def push(self, x): 43 | if x not in self.set: 44 | self.deque.appendleft(x) 45 | self.set.add(x) 46 | 47 | def pop(self): 48 | x = self.deque.pop() 49 | self.set.remove(x) 50 | return x 51 | 52 | def read_input(fname, t=lambda x: x, strip_lines=True, force_multi=False): 53 | with open(fname, 'r') as f: 54 | contents = f.read() 55 | if strip_lines: 56 | lines = contents.strip().split('\n') 57 | else: 58 | lines = contents.split('\n') 59 | if len(lines) == 1 and not force_multi: 60 | return t(lines[0]) 61 | return list(map(t, lines)) 62 | 63 | def maybe_int(s): 64 | try: 65 | return int(s) 66 | except ValueError: 67 | return s 68 | 69 | def keep_by_index(indices, arr): 70 | result = [] 71 | for i in sorted(indices): 72 | if i < len(arr): 73 | result.append(arr[i]) 74 | return result 75 | 76 | def remove_by_index(indices, arr): 77 | result = [] 78 | to_remove = set(indices) 79 | for i in range(len(arr)): 80 | if i not in to_remove: 81 | result.append(arr[i]) 82 | return result 83 | 84 | def min_by(f, arr): 85 | return min([(f(x), x) for x in arr])[1] 86 | 87 | def max_by(f, arr): 88 | return max([(f(x), x) for x in arr])[1] 89 | 90 | def parse_coord(line): 91 | return tuple(map(int, line.split(','))) 92 | 93 | def metric_taxi(a, b): 94 | return sum(abs(a[i] - b[i]) for i in range(len(a))) 95 | 96 | def move_by(d, p): 97 | if isinstance(d, int): 98 | d = DIRS[d] 99 | return tuple(map(add, d, p)) 100 | 101 | def parse_list(s): 102 | s = s.strip() 103 | return [int(x.strip('()[]<>')) for x in s.split(',')] 104 | 105 | def fatal(*args, **kwargs): 106 | print(*args, **kwargs) 107 | exit() 108 | 109 | def automata(grid, rule, iterations): 110 | R = len(grid) 111 | C = len(grid[0]) 112 | def get_neighbors(i, j): 113 | # for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)): 114 | for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1), (i - 1, j - 1), (i - 1, j + 1), (i + 1, j - 1), (i + 1, j + 1)): 115 | if 0 <= ii < R and 0 <= jj < C: 116 | yield ii, jj 117 | 118 | for _ in range(iterations): 119 | new_grid = [[None] * C for _ in range(R)] 120 | for i in range(R): 121 | for j in range(C): 122 | neighbors = map(lambda x: grid[x[0]][x[1]], get_neighbors(i, j)) 123 | new_grid[i][j] = rule(grid[i][j], Counter(neighbors)) 124 | grid = new_grid 125 | return grid 126 | 127 | def print_grid(grid, t=lambda x: x): 128 | for row in grid: 129 | print(''.join(map(t, row))) 130 | 131 | def rule_gol(me, neighbors): 132 | if me == '*': 133 | return '*' if 2 <= neighbors['*'] <= 3 else '.' 134 | else: 135 | return '*' if neighbors['*'] == 3 else '.' 136 | 137 | def prod(L): 138 | result = 1 139 | for x in L: 140 | result *= x 141 | return result 142 | 143 | def reverse_dict(d): 144 | result = defaultdict(list) 145 | for k, v in d.items(): 146 | for x in v: 147 | result[x].append(k) 148 | return result 149 | 150 | builtin_map = map 151 | 152 | def map(*args, **kwargs): 153 | return list(builtin_map(*args, **kwargs)) 154 | 155 | def do_ps(lst): 156 | prefix = [0] 157 | for x in lst: 158 | prefix.append(prefix[-1] + x) 159 | return prefix 160 | 161 | def transpose(A): 162 | N = len(A) 163 | M = len(A[0]) 164 | res = [] 165 | for j in range(M): 166 | row = [A[i][j] for i in range(N)] 167 | res.append(row) 168 | return res 169 | 170 | def crt(n, a): 171 | from functools import reduce 172 | sum = 0 173 | prod = reduce(lambda a, b: a * b, n) 174 | for n_i, a_i in zip(n, a): 175 | p = prod // n_i 176 | sum += a_i * pow(p, -1, n_i) * p 177 | return sum % prod 178 | 179 | def dump_dict_grid(d, t=lambda x: x): 180 | min_x = min(x for x, y in d.keys()) 181 | max_x = max(x for x, y in d.keys()) 182 | min_y = min(y for x, y in d.keys()) 183 | max_y = max(y for x, y in d.keys()) 184 | for y in range(min_y, max_y + 1): 185 | for x in range(min_x, max_x + 1): 186 | print(t(d[(x, y)]), end='') 187 | print() 188 | 189 | def ordch(ch: str) -> int: 190 | assert len(ch) == 1 191 | x = ord(ch) 192 | if x >= ord('a') and x <= ord('z'): return x - ord('a') 193 | if x >= ord('A') and x <= ord('Z'): return x - ord('A') 194 | raise Exception(f"{ch} is not alphabetic") 195 | 196 | def add_interval(ss, L, R): 197 | # [L, R) 198 | assert L <= R 199 | if L == R: 200 | return None 201 | idx = ss.bisect_left((L, R)) 202 | while idx < len(ss): 203 | ival = ss[idx] 204 | if ival[0] > R: 205 | break 206 | R = max(R, ival[1]) 207 | ss.pop(idx) 208 | if idx > 0: 209 | idx -= 1 210 | ival = ss[idx] 211 | if ival[1] >= L: 212 | L = min(L, ival[0]) 213 | R = max(R, ival[1]) 214 | ss.pop(idx) 215 | res = (L, R) 216 | ss.add(res) 217 | return res 218 | 219 | def remove_interval(ss, L, R): 220 | # [L, R) 221 | assert L <= R 222 | if L == R: 223 | return 224 | added = add_interval(ss, L, R) 225 | r2 = added[1] 226 | ss.remove(added) 227 | if added[0] != L: 228 | ss.add((added[0], L)) 229 | if R != r2: 230 | ss.add((R, r2)) 231 | 232 | def pad_grid(grid, ch=' '): 233 | C = max(len(row) for row in grid) 234 | for i in range(len(grid)): 235 | if len(grid[i]) < C: 236 | grid[i] += ch * (C - len(grid[i])) 237 | return grid 238 | -------------------------------------------------------------------------------- /2022/day1.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== real ===" ; py day1.py < day1.in 14 | """ 15 | 16 | A = sys.stdin.read().strip().split('\n\n') 17 | 18 | b = [] 19 | res = 0 20 | for group in A: 21 | val = sum(map(int, group.splitlines())) 22 | b.append(val) 23 | 24 | b.sort() 25 | b = b[::-1] 26 | print(b[0]) 27 | print(sum(b[:3])) 28 | -------------------------------------------------------------------------------- /2022/day10.py: -------------------------------------------------------------------------------- 1 | # from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; pypy3 day10.py < sample.in 14 | echo "=== real ===" ; pypy3 day10.py < day10.in 15 | echo "=== sample ===" ; pypy3 day10.py < sample.in ; echo "=== real ===" ; pypy3 day10.py < day10.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | 20 | rows = 6 21 | cols = 40 22 | 23 | interesting = [20, 60, 100, 140, 180, 220] # 1 index 24 | idx = 0 25 | 26 | cycles = 0 27 | x = 1 28 | res = 0 29 | 30 | def do_work(): 31 | global idx, cycles, x, res 32 | col = cycles % cols 33 | row = cycles // cols 34 | if x - 1 <= col <= x + 1: 35 | res2[row].append('#') 36 | else: 37 | res2[row].append('.') 38 | 39 | if idx < len(interesting) and (cycles + 1) == interesting[idx]: 40 | idx += 1 41 | res += x * (cycles + 1) 42 | cycles += 1 43 | 44 | res2 = [[] for _ in range(rows)] 45 | 46 | for line in A: 47 | if line == 'noop': 48 | do_work() 49 | else: 50 | a = int(line.split()[1]) 51 | for i in range(2): 52 | do_work() 53 | x += a 54 | print(res) 55 | for row in res2: 56 | print(''.join(row)) -------------------------------------------------------------------------------- /2022/day11.py: -------------------------------------------------------------------------------- 1 | # from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; pypy3 day11.py < sample.in 14 | echo "=== real ===" ; pypy3 day11.py < day11.in 15 | echo "=== sample ===" ; pypy3 day11.py < sample.in ; echo "=== real ===" ; pypy3 day11.py < day11.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().split('\n\n') 19 | 20 | N = len(A) 21 | 22 | 23 | def gen_add(a): 24 | return lambda x: x + a 25 | 26 | def gen_mul(a): 27 | return lambda x: x * a 28 | 29 | monkeys = [] 30 | items = [] 31 | inspected = [0] * N 32 | 33 | all_div = 1 34 | 35 | for block in A: 36 | lines = block.splitlines() 37 | item = map(int, lines[1].split(': ')[1].split(', ')) 38 | op = lines[2].split(': ')[1] 39 | if 'old * old' in op: 40 | func = lambda x: x * x 41 | elif 'old +' in op: 42 | val = op.split('+')[-1] 43 | func = gen_add(int(val)) 44 | elif 'old *' in op: 45 | val = op.split('*')[-1] 46 | func = gen_mul(int(val)) 47 | else: 48 | assert False 49 | div = int(lines[3].split()[-1]) 50 | t = int(lines[4].split()[-1]) 51 | f = int(lines[5].split()[-1]) 52 | all_div *= div 53 | monkeys.append((func, div, t, f)) 54 | items.append(item) 55 | 56 | for ri in range(10000): 57 | for mi in range(N): 58 | monkey = monkeys[mi] 59 | for item in items[mi]: 60 | item = monkey[0](item) 61 | # item //= 3 62 | if item % monkey[1] == 0: 63 | target = monkey[2] 64 | else: 65 | target = monkey[3] 66 | assert target != mi 67 | items[target].append(item % all_div) 68 | inspected[mi] += 1 69 | items[mi] = [] 70 | 71 | print(inspected) 72 | 73 | inspected.sort() 74 | inspected = inspected[::-1] 75 | print(inspected[0] * inspected[1]) -------------------------------------------------------------------------------- /2022/day12.py: -------------------------------------------------------------------------------- 1 | # from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; pypy3 day12.py < sample.in 14 | echo "=== real ===" ; pypy3 day12.py < day12.in 15 | echo "=== sample ===" ; pypy3 day12.py < sample.in ; echo "=== real ===" ; pypy3 day12.py < day12.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | # A = sys.stdin.read().strip().split('\n\n') 20 | 21 | N = len(A) 22 | M = len(A[0]) 23 | 24 | for i in range(N): 25 | for j in range(M): 26 | if A[i][j] == 'S': 27 | end_i = i 28 | end_j = j 29 | elif A[i][j] == 'E': 30 | start_i = i 31 | start_j = j 32 | 33 | 34 | Q = deque() 35 | Q.append((start_i, start_j, 0)) 36 | visited = set() 37 | 38 | def conv(c): 39 | if c == 'S': 40 | return 'a' 41 | elif c == 'E': 42 | return 'z' 43 | return c 44 | 45 | 46 | res = 1e18 47 | while Q: 48 | i, j, d = Q.popleft() 49 | if (i, j) in visited: 50 | continue 51 | visited.add((i, j)) 52 | for ii, jj in n4(i, j, N, M): 53 | if ord(conv(A[i][j])) <= ord(conv(A[ii][jj])) + 1: 54 | Q.append((ii, jj, d + 1)) 55 | if conv(A[i][j]) == 'a': 56 | res = min(res, d) 57 | print(res) 58 | -------------------------------------------------------------------------------- /2022/day13.py: -------------------------------------------------------------------------------- 1 | # from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; pypy3 day13.py < sample.in 14 | echo "=== real ===" ; pypy3 day13.py < day13.in 15 | echo "=== sample ===" ; pypy3 day13.py < sample.in ; echo "=== real ===" ; pypy3 day13.py < day13.in 16 | """ 17 | 18 | # A = sys.stdin.read().strip().splitlines() 19 | A = sys.stdin.read().strip().split('\n\n') 20 | 21 | N = len(A) 22 | 23 | def cmp(a, b): 24 | if isinstance(a, int) and isinstance(b, int): 25 | return a < b 26 | elif isinstance(a, list) and isinstance(b, list): 27 | for i in range(len(a)): 28 | if i >= len(b): 29 | return False 30 | if cmp(a[i], b[i]): 31 | return True 32 | elif cmp(b[i], a[i]): 33 | return False 34 | else: 35 | pass 36 | if len(a) < len(b): 37 | return True 38 | return False 39 | else: 40 | if isinstance(a, int): 41 | a = [a] 42 | if isinstance(b, int): 43 | b = [b] 44 | return cmp(a, b) 45 | 46 | def cmp2(a, b): 47 | if cmp(a, b): 48 | return -1 49 | elif cmp(b, a): 50 | return 1 51 | else: 52 | return 0 53 | 54 | res = [] 55 | B = [] 56 | for i, pair in enumerate(A): 57 | p1, p2 = pair.splitlines() 58 | p1, p2 = map(eval, (p1, p2)) 59 | B.append(p1) 60 | B.append(p2) 61 | if cmp(p1, p2): 62 | res.append(i + 1) 63 | B.append([[2]]) 64 | B.append([[6]]) 65 | print(res, sum(res)) 66 | 67 | B.sort(key=cmp_to_key(cmp2)) 68 | 69 | l = -1 70 | m = -1 71 | 72 | for i, arr in enumerate(B): 73 | if arr == [[2]]: 74 | l = i + 1 75 | if arr == [[6]]: 76 | m = i + 1 77 | 78 | print(l, m, l * m) -------------------------------------------------------------------------------- /2022/day14.py: -------------------------------------------------------------------------------- 1 | # from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; pypy3 day14.py < sample.in 14 | echo "=== real ===" ; pypy3 day14.py < day14.in 15 | echo "=== sample ===" ; pypy3 day14.py < sample.in ; echo "=== real ===" ; pypy3 day14.py < day14.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | # A = sys.stdin.read().strip().split('\n\n') 20 | 21 | N = len(A) 22 | 23 | # source is 500, 0 24 | 25 | SIZE = int(2e3) 26 | 27 | grid = [['.'] * SIZE for _ in range(SIZE)] 28 | 29 | max_y = 0 30 | 31 | for line in A: 32 | pts = line.split(' -> ') 33 | pts = [parse_coord(pt) for pt in pts] 34 | max_y = max(max_y, max(pts[i][1] for i in range(len(pts)))) 35 | for i in range(1, len(pts)): 36 | x0, y0 = pts[i-1] 37 | x1, y1 = pts[i] 38 | assert x0 == x1 or y0 == y1 39 | for x, y in rline(x0, y0, x1, y1): 40 | grid[y][x] = '#' 41 | for x in range(0, SIZE): 42 | grid[max_y + 2][x] = '#' 43 | 44 | def drop_sand(x, y): 45 | while True: 46 | if y > SIZE - 39: 47 | return None 48 | assert x > 0 and x < SIZE - 1 49 | if grid[y + 1][x] == '.': 50 | y += 1 51 | elif grid[y + 1][x - 1] == '.': 52 | y += 1 53 | x -= 1 54 | elif grid[y + 1][x + 1] == '.': 55 | y += 1 56 | x += 1 57 | else: 58 | return x, y 59 | res = 0 60 | while True: 61 | r = drop_sand(500, 0) 62 | res += 1 63 | if r == (500, 0): 64 | print(res) 65 | break 66 | else: 67 | x, y = r 68 | grid[y][x] = 'o' 69 | # not 41513, 20757, 20756 70 | -------------------------------------------------------------------------------- /2022/day15.py: -------------------------------------------------------------------------------- 1 | # from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; python3 day15.py < sample.in 14 | echo "=== real ===" ; python3 day15.py < day15.in 15 | echo "=== sample ===" ; python3 day15.py < sample.in ; echo "=== real ===" ; python3 day15.py < day15.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | # A = sys.stdin.read().strip().split('\n\n') 20 | 21 | N = len(A) 22 | 23 | sensors = [] 24 | 25 | for line in A: 26 | l, r = line.split(': ') 27 | ll, lr = l.split(', ') 28 | sx = int(ll.split(' ')[-1].split('=')[1]) 29 | sy = int(lr.split('=')[1]) 30 | rl, rr = r.split(', ') 31 | bx = int(rl.split(' ')[-1].split('=')[1]) 32 | by = int(rr.split('=')[1]) 33 | sensors.append((sx, sy, bx, by)) 34 | 35 | def solve(y_target): 36 | beacons = set() 37 | m = {} 38 | for sx, sy, bx, by in sensors: 39 | d = abs(sx - bx) + abs(sy - by) 40 | m[(sx, sy)] = d 41 | beacons.add((bx, by)) 42 | res = 0 43 | 44 | for x in range(int(-5e6), int(5e6)): 45 | good = True 46 | for sx, sy, _, _ in sensors: 47 | my_dist = abs(sx - x) + abs(sy - y_target) 48 | if my_dist <= m[(sx, sy)]: 49 | good = False 50 | break 51 | if not good: 52 | if (x, y_target) not in beacons: 53 | res += 1 54 | return res 55 | 56 | from z3 import * 57 | 58 | def solve2(bound): 59 | beacons = set() 60 | m = {} 61 | for sx, sy, bx, by in sensors: 62 | d = abs(sx - bx) + abs(sy - by) 63 | m[(sx, sy)] = d 64 | beacons.add((bx, by)) 65 | 66 | s = Solver() 67 | x, y = Int('x'), Int('y') 68 | s.add(x >= 0) 69 | s.add(x <= bound) 70 | s.add(y >= 0) 71 | s.add(y <= bound) 72 | for sx, sy, bx, by in sensors: 73 | my_dist = Abs(sx - x) + Abs(sy - y) 74 | s.add(my_dist > m[(sx, sy)]) 75 | s.add(And(x != bx, y != by)) 76 | 77 | 78 | assert s.check() == sat 79 | m = s.model() 80 | return m[x].as_long(), m[y].as_long() 81 | 82 | # print(solve(2000000)) 83 | 84 | x, y = solve2(4000000) 85 | print(x, y, x * 4000000 + y) -------------------------------------------------------------------------------- /2022/day16.py: -------------------------------------------------------------------------------- 1 | # from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; pypy3 day16.py < sample.in 14 | echo "=== real ===" ; pypy3 day16.py < day16.in 15 | echo "=== sample ===" ; pypy3 day16.py < sample.in ; echo "=== real ===" ; pypy3 day16.py < day16.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | # A = sys.stdin.read().strip().split('\n\n') 20 | 21 | graph = {} 22 | 23 | m = {} 24 | m2 = {} 25 | 26 | flow = {} 27 | 28 | target = -1 29 | 30 | for line in A: 31 | tokens = line.split(' ') 32 | valve = tokens[1] 33 | neighbors = ''.join(tokens[9:]).split(',') 34 | graph[valve] = neighbors 35 | flow[valve] = ints(line, 1)[0] 36 | idx = len(m) 37 | m[valve] = idx 38 | m2[idx] = valve 39 | 40 | if valve == 'AA': 41 | target = idx 42 | ans = 0 43 | 44 | @cache 45 | def dp(time, idx, mask): 46 | global ans 47 | cur_flow = 0 48 | for i, e in enumerate(mask): 49 | if e: 50 | cur_flow += flow[m2[i]] 51 | if time == 30: 52 | return 0 53 | else: 54 | # turn it on 55 | nm = list(mask) 56 | r1 = 0 57 | if not mask[idx] and flow[m2[idx]] > 0: 58 | nm[idx] = True 59 | r1 = cur_flow + dp(time + 1, idx, tuple(nm)) 60 | 61 | # don't turn it on 62 | r2 = 0 63 | for nb in graph[m2[idx]]: 64 | r2 = max(r2, dp(time + 1, m[nb], tuple(mask))) 65 | 66 | return max(r1, cur_flow + r2) 67 | 68 | dpc = {} 69 | 70 | def dp2(time, idx, idx2, mask): 71 | if time == 26: 72 | return 0 73 | if idx > idx2: 74 | idx, idx2 = idx2, idx 75 | do_work = False 76 | cur_flow = 0 77 | for i in range(len(m)): 78 | if mask & (1 << i): 79 | cur_flow += flow[m2[i]] 80 | else: 81 | if flow[m2[i]] > 0: 82 | do_work = True 83 | key = (time, idx, idx2, mask) 84 | if key in dpc: 85 | return dpc[key] 86 | 87 | if not do_work: 88 | return cur_flow * (26 - time) 89 | 90 | r1 = r2 = 0 91 | if do_work: 92 | nm = mask 93 | if not (mask & (1 << idx)) and flow[m2[idx]] > 0: 94 | # i turn on 95 | nm |= 1 << idx 96 | for nb2 in graph[m2[idx2]]: 97 | r1 = max(r1, dp2(time + 1, idx, m[nb2], nm)) 98 | if not (mask & (1 << idx2)) and flow[m2[idx2]] > 0: 99 | nm |= 1 << idx2 100 | r1 = max(r1, dp2(time + 1, idx, idx2, nm)) 101 | # i move 102 | nm = mask 103 | if not (mask & (1 << idx2)) and flow[m2[idx2]] > 0: 104 | nm |= 1 << idx2 105 | for nb in graph[m2[idx]]: 106 | r2 = max(r2, dp2(time + 1, m[nb], idx2, nm)) 107 | nm = mask 108 | for nb in graph[m2[idx]]: 109 | for nb2 in graph[m2[idx2]]: 110 | r2 = max(r2, dp2(time + 1, m[nb], m[nb2], nm)) 111 | 112 | res = cur_flow + max(r1, r2) 113 | dpc[key] = res 114 | return res 115 | 116 | def get_flow(mask): 117 | do_work = False 118 | cur_flow = 0 119 | for i in range(len(m)): 120 | if mask & (1 << i): 121 | cur_flow += flow[m2[i]] 122 | else: 123 | if flow[m2[i]] > 0: 124 | do_work = True 125 | return cur_flow, do_work 126 | 127 | def bfs(s): 128 | q = deque() 129 | q.append((s, -1)) 130 | prv = {} 131 | prv[s] = -1 132 | while q: 133 | u, p = q.popleft() 134 | for v in graph[m2[u]]: 135 | v = m[v] 136 | if v not in prv: 137 | prv[v] = u 138 | q.append((v, u)) 139 | paths = {} 140 | for i in range(len(m)): 141 | if i == s: 142 | continue 143 | cur = i 144 | path = [] 145 | while cur != -1: 146 | path.append(cur) 147 | cur = prv[cur] 148 | paths[i] = path[::-1] 149 | return paths 150 | 151 | interesting = [i for i in range(len(m)) if flow[m2[i]] > 0] 152 | all_paths = {} 153 | for v in interesting: 154 | all_paths[v] = bfs(v) 155 | 156 | 157 | @cache 158 | def dp3(time, idx1, idx2, mask, t1, t2): 159 | if time == 26: 160 | return 0 161 | if idx1 > idx2: 162 | idx1, idx2 = idx2, idx1 163 | t1, t2 = t2, t1 164 | cur_flow, do_work = get_flow(mask) 165 | if not do_work: 166 | return cur_flow * (26 - time) 167 | 168 | # am i going somewhere? 169 | # if t1 == 170 | 171 | 172 | # not 2137 173 | print(dp2(0, target, target, 0)) 174 | -------------------------------------------------------------------------------- /2022/day17.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from math import gcd, lcm 9 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 10 | 11 | from util import * 12 | 13 | """ 14 | echo "=== sample ===" ; pypy3 day17.py < sample.in 15 | echo "=== real ===" ; pypy3 day17.py < day17.in 16 | echo "=== sample ===" ; pypy3 day17.py < sample.in ; echo "=== real ===" ; pypy3 day17.py < day17.in 17 | """ 18 | 19 | A = sys.stdin.read().strip() 20 | N = len(A) 21 | 22 | BLOCKS =''' 23 | #### 24 | 25 | .#. 26 | ### 27 | .#. 28 | 29 | ..# 30 | ..# 31 | ### 32 | 33 | # 34 | # 35 | # 36 | # 37 | 38 | ## 39 | ##'''.strip().split('\n\n') 40 | 41 | WIDTH = 7 42 | 43 | # alternate push, fall 44 | 45 | grid = set() 46 | # (x, y), y increases with height 47 | 48 | ITER = 1000000000000 49 | time = 0 50 | next_block = 0 51 | 52 | 53 | def parse_block(b): 54 | b = b.splitlines() 55 | height = len(b) 56 | width = len(b[0]) 57 | points = [] 58 | 59 | for i in range(height): 60 | for j in range(width): 61 | if b[i][j] == '#': 62 | points.append((j, height - i - 1)) 63 | return points 64 | 65 | BP = [parse_block(b) for b in BLOCKS] 66 | 67 | top_y = -1 68 | 69 | def add(block, dx, dy): 70 | return [(dx + x, dy + y) for (x, y) in block] 71 | 72 | 73 | def collide(b): 74 | for x, y in b: 75 | if (x, y) in grid: 76 | return True 77 | if x < 0 or x >= WIDTH: 78 | return True 79 | if y < 0: 80 | return True 81 | return False 82 | 83 | def keep_top(n): 84 | to_yeet = [] 85 | for (x, y) in grid: 86 | if y < top_y - n: 87 | to_yeet.append((x, y)) 88 | for (x, y) in to_yeet: 89 | grid.remove((x, y)) 90 | 91 | def serialize_state(): 92 | return tuple(sorted([(x, y - top_y) for (x, y) in grid])) 93 | 94 | oct = 0 95 | dp = {} 96 | offset = 0 97 | while True: 98 | keep_top(50) 99 | 100 | if next_block == 0: 101 | state = serialize_state() 102 | key = (time, state) 103 | if key in dp: 104 | # print('got repeated state:') 105 | # print('time:', time) 106 | # print('oct:', oct) 107 | (prev_oct, prev_top_y) = dp[key] 108 | # print('prev_oct:', prev_oct) 109 | # print('prev_top_y:', prev_top_y) 110 | # print('top_y:', top_y) 111 | # thx copilot 112 | period = oct - prev_oct 113 | y_diff = top_y - prev_top_y 114 | offset += y_diff * ((ITER - oct) // period) 115 | oct += (ITER - oct) // period * period 116 | else: 117 | dp[key] = (oct, top_y) 118 | assert oct <= ITER 119 | if oct == ITER: 120 | break 121 | 122 | b = BP[next_block] 123 | b = add(b, 2, top_y + 4) 124 | while True: 125 | # jet of gas 126 | dir = A[time] 127 | time = (time + 1) % len(A) 128 | if dir == '>': 129 | dx = 1 130 | else: 131 | dx = -1 132 | newb = add(b, dx, 0) 133 | if not collide(newb): 134 | b = newb 135 | # fall down 136 | newb = add(b, 0, -1) 137 | if not collide(newb): 138 | b = newb 139 | else: 140 | break 141 | for x, y in b: 142 | top_y = max(top_y, y) 143 | grid.add((x, y)) 144 | next_block = (next_block + 1) % len(BP) 145 | oct += 1 146 | if oct == ITER: 147 | break 148 | 149 | print(top_y + offset + 1) 150 | -------------------------------------------------------------------------------- /2022/day18.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from math import gcd, lcm 9 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 10 | 11 | from util import * 12 | 13 | """ 14 | echo "=== sample ===" ; pypy3 day18.py < sample.in 15 | echo "=== real ===" ; pypy3 day18.py < day18.in 16 | echo "=== sample ===" ; python3 day18.py < sample.in ; echo "=== real ===" ; python3 day18.py < day18.in 17 | """ 18 | 19 | A = sys.stdin.read().strip().splitlines() 20 | N = len(A) 21 | 22 | 23 | grid = set() 24 | 25 | for line in A: 26 | x, y, z = ints(line, 3) 27 | grid.add((x, y, z)) 28 | res = 0 29 | 30 | possible = Counter() 31 | 32 | for pt in grid: 33 | for x, y, z in n4d(pt): 34 | if (x, y, z) not in grid: 35 | possible[(x, y, z)] += 1 36 | res += 1 37 | 38 | dp = {} 39 | def dfs(x, y, z, prv): 40 | stack = [(x, y, z, prv)] 41 | seen = set() 42 | while stack: 43 | x, y, z, prv = stack.pop() 44 | if (x, y, z) in dp: 45 | return dp[x, y, z] 46 | if abs(x) >= 25 or abs(y) >= 25 or abs(z) >= 25: 47 | dp[x, y, z] = True 48 | return True 49 | seen.add((x, y, z)) 50 | for xx, yy, zz in n4d((x, y, z)): 51 | if (xx, yy, zz) in grid: 52 | continue 53 | if (xx, yy, zz) == prv: 54 | continue 55 | if (xx, yy, zz) in seen: 56 | continue 57 | stack.append((xx, yy, zz, (x, y, z))) 58 | dp[x, y, z] = False 59 | return False 60 | 61 | 62 | for x, y, z in possible.keys(): 63 | b = dfs(x, y, z, None) 64 | if not b: 65 | res -= possible[(x, y, z)] 66 | 67 | print(res) -------------------------------------------------------------------------------- /2022/day19.py: -------------------------------------------------------------------------------- 1 | from bisect import * 2 | from collections import * 3 | from functools import * 4 | from heapq import * 5 | import itertools 6 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 7 | 8 | from util import * 9 | 10 | """ 11 | echo "=== sample ===" ; pypy3 day19.py < sample.in 12 | echo "=== real ===" ; pypy3 day19.py < day19.in 13 | echo "=== sample ===" ; pypy3 day19.py < sample.in ; echo "=== real ===" ; pypy3 day19.py < day19.in 14 | """ 15 | 16 | A = sys.stdin.read().strip().splitlines() 17 | N = len(A) 18 | 19 | blueprints = [] 20 | 21 | TL = 32 22 | 23 | for line in A: 24 | idx, ore_ore, clay_ore, obsidian_ore, obsidian_clay, geode_ore, geode_obsidian = ints(line) 25 | blueprints.append((ore_ore, clay_ore, obsidian_ore, obsidian_clay, geode_ore, geode_obsidian)) 26 | 27 | blueprints = blueprints[:3] 28 | 29 | BP_GLOBAL = blueprints[0] 30 | 31 | cur_best = 0 32 | 33 | dp = {} 34 | def solve(time, state): 35 | global cur_best 36 | bp = BP_GLOBAL 37 | max_ore_cost = max(bp[0], bp[1], bp[2], bp[4]) 38 | max_clay_cost = bp[3] 39 | max_obsidian_cost = bp[5] 40 | if time >= TL: 41 | cur_best = max(cur_best, state[7]) 42 | return state[7] 43 | new_states = [] 44 | # construct any robots? 45 | orebot, claybot, obsidianbot, geodebot, ore, clay, obsidian, geode = state 46 | if orebot >= max_ore_cost and ore >= max_ore_cost: 47 | ore = max_ore_cost 48 | if claybot >= max_clay_cost and clay >= max_clay_cost: 49 | clay = max_clay_cost 50 | if obsidianbot >= max_obsidian_cost and obsidian >= max_obsidian_cost: 51 | obsidian = max_obsidian_cost 52 | best_case = geode 53 | tmp = geodebot 54 | for i in range(time, TL): 55 | best_case += tmp 56 | tmp += 1 57 | if best_case < cur_best: 58 | return 0 59 | state = (orebot, claybot, obsidianbot, geodebot, ore, clay, obsidian, geode) 60 | key = (time, state) 61 | if key in dp: 62 | return dp[key] 63 | harvest = [0] * 4 + list(state[:4]) 64 | if orebot >= bp[4] and obsidianbot >= bp[5]: 65 | if ore >= bp[4] and obsidian >= bp[5]: 66 | new_states.append((orebot, claybot, obsidianbot, geodebot + 1, ore - bp[4], clay, obsidian - bp[5], geode)) 67 | else: 68 | new_states.append(state) 69 | else: 70 | if ore >= bp[4] and obsidian >= bp[5]: 71 | new_states.append((orebot, claybot, obsidianbot, geodebot + 1, ore - bp[4], clay, obsidian - bp[5], geode)) 72 | if ore >= bp[2] and clay >= bp[3] and obsidianbot < max_obsidian_cost: 73 | new_states.append((orebot, claybot, obsidianbot + 1, geodebot, ore - bp[2], clay - bp[3], obsidian, geode)) 74 | if ore >= bp[0] and orebot < max_ore_cost: 75 | new_states.append((orebot + 1, claybot, obsidianbot, geodebot, ore - bp[0], clay, obsidian, geode)) 76 | if ore >= bp[1] and claybot < max_clay_cost: 77 | new_states.append((orebot, claybot + 1, obsidianbot, geodebot, ore - bp[1], clay, obsidian, geode)) 78 | new_states.append(state) 79 | # add the harvest 80 | ns2 = [] 81 | for state in new_states: 82 | ns2.append(tuple(map(add, state, harvest))) 83 | res = 0 84 | for ns in ns2: 85 | res = max(res, solve(time + 1, ns)) 86 | dp[key] = res 87 | return res 88 | 89 | 90 | res = 1 91 | for idx, bp in enumerate(blueprints): 92 | cur_best = 0 93 | dp.clear() 94 | BP_GLOBAL = bp 95 | num = solve(0, (1, 0, 0, 0, 0, 0, 0, 0)) 96 | print(idx + 1, num) 97 | res *= num 98 | # not 1898, 130, 1818, 1936 99 | print('final:', res) 100 | -------------------------------------------------------------------------------- /2022/day2.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day2.py < sample.in 14 | echo "=== real ===" ; py day2.py < day2.in 15 | echo "=== sample ===" ; py day2.py < sample.in ; echo "=== real ===" ; py day2.py < day2.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | 20 | m = { 21 | 'R': 1, 22 | 'P': 2, 23 | 'S': 3, 24 | } 25 | 26 | m2 = { 27 | 'X': 'R', 28 | 'Y': 'P', 29 | 'Z': 'S', 30 | } 31 | 32 | 33 | m3 = { 34 | 'A': 'R', 35 | 'B': 'P', 36 | 'C': 'S', 37 | } 38 | 39 | def win(a, b): 40 | return (a == 'R' and b == 'S') or (a == 'S' and b == 'P') or (a == 'P' and b == 'R') 41 | 42 | def get_out(opp, desired): 43 | if desired == 'Y': 44 | return opp 45 | elif desired == 'X': 46 | # lose 47 | t = { 48 | 'R': 'S', 49 | 'S': 'P', 50 | 'P': 'R', 51 | } 52 | return t[opp] 53 | else: 54 | t = { 55 | 'R': 'P', 56 | 'S': 'R', 57 | 'P': 'S', 58 | } 59 | return t[opp] 60 | 61 | res = 0 62 | for line in A: 63 | a, b = line.split() 64 | a = m3[a] 65 | b = get_out(a, b) 66 | res += m[b] 67 | if a == b: 68 | res += 3 69 | elif win(b, a): 70 | res += 6 71 | print(res) 72 | -------------------------------------------------------------------------------- /2022/day20.py: -------------------------------------------------------------------------------- 1 | try: 2 | from sortedcontainers import * 3 | from math import gcd, lcm 4 | except ImportError: 5 | pass 6 | from bisect import * 7 | from collections import * 8 | from functools import * 9 | from heapq import * 10 | import itertools 11 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 12 | 13 | from util import * 14 | 15 | """ 16 | echo "=== sample ===" ; pypy3 day20.py < sample.in 17 | echo "=== real ===" ; pypy3 day20.py < day20.in 18 | echo "=== sample ===" ; pypy3 day20.py < sample.in ; echo "=== real ===" ; pypy3 day20.py < day20.in 19 | """ 20 | 21 | A = sys.stdin.read().strip().splitlines() 22 | N = len(A) 23 | 24 | A = map(int, A) 25 | 26 | KEY = 811589153 27 | A = map(lambda x: x * KEY, A) 28 | 29 | A = list(enumerate(A)) 30 | 31 | def mix(A): 32 | def f(idx): 33 | for i in range(len(A)): 34 | if A[i][0] == idx: 35 | return i 36 | assert False 37 | for i in range(len(A)): 38 | idx = f(i) 39 | val = A[idx][1] 40 | if val > 0: 41 | val %= (N - 1) 42 | elif val < 0: 43 | val = -((-val) % (N - 1)) 44 | while val != 0: 45 | if val > 0: 46 | tgt = (idx + 1) % N 47 | A[tgt], A[idx] = A[idx], A[tgt] 48 | idx += 1 49 | idx %= N 50 | val -= 1 51 | else: 52 | tgt = (idx - 1) % N 53 | A[tgt], A[idx] = A[idx], A[tgt] 54 | idx -= 1 55 | idx %= N 56 | val += 1 57 | return A 58 | 59 | for i in range(10): 60 | A = mix(A) 61 | 62 | tgt = -1 63 | for i in range(N): 64 | if A[i][1] == 0: 65 | tgt = i 66 | break 67 | assert tgt >= 0 68 | 69 | offsets = [1000, 2000, 3000] 70 | res = [] 71 | for offset in offsets: 72 | idx = tgt + offset 73 | idx %= N 74 | res.append(A[idx][1]) 75 | print(res, sum(res)) 76 | # not -872458339475 77 | # not -1470 -------------------------------------------------------------------------------- /2022/day21.py: -------------------------------------------------------------------------------- 1 | try: 2 | from sortedcontainers import * 3 | from math import gcd, lcm 4 | except ImportError: 5 | pass 6 | from bisect import * 7 | from collections import * 8 | from functools import * 9 | from heapq import * 10 | import itertools 11 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 12 | 13 | from util import * 14 | 15 | """ 16 | echo "=== sample ===" ; python3 day21.py < sample.in 17 | echo "=== real ===" ; python3 day21.py < day21.in 18 | echo "=== sample ===" ; python3 day21.py < sample.in ; echo "=== real ===" ; python3 day21.py < day21.in 19 | """ 20 | 21 | A = sys.stdin.read().strip().splitlines() 22 | N = len(A) 23 | 24 | graph = {} 25 | 26 | 27 | from z3 import * 28 | 29 | for line in A: 30 | result, ops = line.split(': ') 31 | ops = ops.split(' ') 32 | if len(ops) == 1: 33 | graph[result] = int(ops[0]) 34 | else: 35 | graph[result] = ops 36 | 37 | me = Int('humn') 38 | def get(node): 39 | assert node in graph 40 | if node == 'humn': 41 | return me 42 | if isinstance(graph[node], int): 43 | return graph[node] 44 | else: 45 | a, op, b = graph[node] 46 | a = get(a) 47 | b = get(b) 48 | if op == '+': 49 | return a + b 50 | elif op == '*': 51 | return a * b 52 | elif op == '-': 53 | return a - b 54 | elif op == '/': 55 | return a / b 56 | 57 | a = get('wrvq') 58 | b = get('vqfc') 59 | 60 | s = Solver() 61 | s.add(a == b) 62 | assert s.check() == sat 63 | m = s.model() 64 | a = m[me].as_long() 65 | 66 | print(a) 67 | -------------------------------------------------------------------------------- /2022/day22.py: -------------------------------------------------------------------------------- 1 | try: 2 | from sortedcontainers import * 3 | from math import gcd, lcm 4 | except ImportError: 5 | pass 6 | from bisect import * 7 | from collections import * 8 | from functools import * 9 | from heapq import * 10 | import itertools 11 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 12 | 13 | from util import * 14 | 15 | """ 16 | echo "=== sample ===" ; python3 day22.py < sample.in 17 | echo "=== real ===" ; python3 day22.py < day22.in 18 | echo "=== sample ===" ; python3 day22.py < sample.in ; echo "=== real ===" ; python3 day22.py < day22.in 19 | """ 20 | 21 | A = sys.stdin.read().rstrip('\n') 22 | N = len(A) 23 | 24 | grid, inst = A.split('\n\n') 25 | grid = grid.splitlines() 26 | grid = pad_grid(grid) 27 | 28 | dir = DIRS_M['R'] 29 | 30 | def tokenize(inst): 31 | res = [] 32 | cur = '' 33 | for c in inst: 34 | if c == 'L' or c == 'R': 35 | if cur: 36 | res.append(int(cur)) 37 | cur = '' 38 | res.append(c) 39 | else: 40 | cur += c 41 | if cur: 42 | res.append(int(cur)) 43 | return res 44 | 45 | inst = tokenize(inst) 46 | 47 | y = 0 48 | x = grid[0].index('.') 49 | 50 | R = len(grid) 51 | C = len(grid[0]) 52 | 53 | def try_move_dir(x, y, d): 54 | dx, dy = DIRS[d] 55 | nx, ny = x, y 56 | while True: 57 | nx += dx 58 | ny += dy 59 | if nx < 0: 60 | nx = C - 1 61 | elif nx >= C: 62 | nx = 0 63 | if ny < 0: 64 | ny = R - 1 65 | elif ny >= R: 66 | ny = 0 67 | if grid[ny][nx] == '.': 68 | return nx, ny 69 | elif grid[ny][nx] == '#': 70 | return x, y 71 | else: 72 | assert grid[ny][nx] == ' ' 73 | 74 | DIM = 50 75 | 76 | def check_wall(x, y, d, nx, ny, nd): 77 | assert 0 <= ny < R, (nx, ny) 78 | assert 0 <= nx < C, (nx, ny) 79 | if grid[ny][nx] == '#': 80 | return x, y, d 81 | else: 82 | assert grid[ny][nx] == '.' 83 | return nx, ny, nd 84 | 85 | def try_move_dir_cube(x, y, d): 86 | dx, dy = DIRS[d] 87 | nx, ny = x + dx, y + dy 88 | if 0 <= nx < C and 0 <= ny < R: 89 | if grid[ny][nx] == '.': 90 | return nx, ny, d 91 | elif grid[ny][nx] == '#': 92 | return x, y, d 93 | else: 94 | assert grid[ny][nx] == ' ' 95 | if 0 <= nx < DIM and 0 <= ny < DIM: 96 | # 1 -> 4 97 | assert d == DIRS_M['L'] 98 | nd = DIRS_M['R'] 99 | assert dy == 0 100 | ny = 2 * DIM + (DIM - y - 1) 101 | nx = 0 102 | return check_wall(x, y, d, nx, ny, nd) 103 | elif 0 <= nx < DIM and DIM <= ny < 2 * DIM: 104 | # 3 <-> 4 105 | if d == DIRS_M['L']: 106 | nd = DIRS_M['D'] 107 | ny = 2 * DIM 108 | nx = y - DIM 109 | return check_wall(x, y, d, nx, ny, nd) 110 | elif d == DIRS_M['U']: 111 | nd = DIRS_M['R'] 112 | ny = x + DIM 113 | nx = DIM 114 | return check_wall(x, y, d, nx, ny, nd) 115 | else: 116 | assert False 117 | elif 2 * DIM <= nx < 3 * DIM and DIM <= ny < 2 * DIM: 118 | # 2 -> 3 119 | if d == DIRS_M['D']: 120 | nd = DIRS_M['L'] 121 | nx = 2 * DIM - 1 122 | ny = x - 2 * DIM + DIM 123 | return check_wall(x, y, d, nx, ny, nd) 124 | else: 125 | # 3 -> 2 126 | assert d == DIRS_M['R'] 127 | nd = DIRS_M['U'] 128 | ny = DIM - 1 129 | nx = y - DIM + 2 * DIM 130 | return check_wall(x, y, d, nx, ny, nd) 131 | elif 3 * DIM <= nx < 4 * DIM: 132 | # 2 -> 5 133 | assert d == DIRS_M['R'] 134 | nd = DIRS_M['L'] 135 | nx = 2 * DIM - 1 136 | ny = 2 * DIM + (DIM - y - 1) 137 | return check_wall(x, y, d, nx, ny, nd) 138 | elif 2 * DIM <= nx < 3 * DIM and 2 * DIM <= ny < 3 * DIM: 139 | # 5 -> 2 140 | assert d == DIRS_M['R'] 141 | nd = DIRS_M['L'] 142 | nx = 3 * DIM - 1 143 | ny = (DIM - (y - 2 * DIM) - 1) 144 | return check_wall(x, y, d, nx, ny, nd) 145 | elif 1 * DIM <= nx < 2 * DIM and 3 * DIM <= ny < 4 * DIM: 146 | # 5 <-> 6 147 | if d == DIRS_M['D']: 148 | nd = DIRS_M['L'] 149 | nx = DIM - 1 150 | ny = x - DIM + 3 * DIM 151 | return check_wall(x, y, d, nx, ny, nd) 152 | else: 153 | assert d == DIRS_M['R'] 154 | nd = DIRS_M['U'] 155 | ny = 3 * DIM - 1 156 | nx = y - 3 * DIM + DIM 157 | return check_wall(x, y, d, nx, ny, nd) 158 | else: 159 | assert False 160 | else: 161 | if nx < 0: 162 | if 2 * DIM <= ny < 3 * DIM: 163 | # 4 -> 1 164 | assert d == DIRS_M['L'] 165 | nd = DIRS_M['R'] 166 | nx = DIM 167 | ny = (DIM - (y - 2 * DIM) - 1) 168 | return check_wall(x, y, d, nx, ny, nd) 169 | elif 3 * DIM <= ny < 4 * DIM: 170 | # 6 -> 1 171 | assert d == DIRS_M['L'] 172 | nd = DIRS_M['D'] 173 | ny = 0 174 | nx = y - 3 * DIM + DIM 175 | return check_wall(x, y, d, nx, ny, nd) 176 | else: 177 | assert False 178 | if ny < 0: 179 | if DIM <= nx < 2 * DIM: 180 | # 1 -> 6 181 | assert d == DIRS_M['U'] 182 | nd = DIRS_M['R'] 183 | nx = 0 184 | ny = 3 * DIM + x - DIM 185 | return check_wall(x, y, d, nx, ny, nd) 186 | if 2 * DIM <= nx < 3 * DIM: 187 | # 2 -> 6 188 | assert d == DIRS_M['U'] 189 | nd = d 190 | nx = x - 2 * DIM 191 | ny = 4 * DIM - 1 192 | return check_wall(x, y, d, nx, ny, nd) 193 | if nx >= C: 194 | # 2 -> 5 195 | nd = DIRS_M['L'] 196 | nx = 2 * DIM - 1 197 | ny = 2 * DIM + (DIM - y - 1) 198 | return check_wall(x, y, d, nx, ny, nd) 199 | 200 | if ny >= R: 201 | # 6 -> 2 202 | nd = d 203 | ny = 0 204 | nx = x + 2 * DIM 205 | return check_wall(x, y, d, nx, ny, nd) 206 | 207 | 208 | 209 | 210 | for op in inst: 211 | if isinstance(op, str): 212 | if op == 'L': 213 | dir = (dir - 1) % 4 214 | else: 215 | assert op == 'R' 216 | dir = (dir + 1) % 4 217 | else: 218 | for i in range(op): 219 | x, y, dir = try_move_dir_cube(x, y, dir) 220 | assert 0 <= x < C 221 | assert 0 <= y < R 222 | assert grid[y][x] == '.' 223 | 224 | row = y + 1 225 | col = x + 1 226 | facing = (dir - 1) % 4 227 | # not 37396 228 | print(row, col, facing, row * 1000 + col * 4 + facing) -------------------------------------------------------------------------------- /2022/day23.py: -------------------------------------------------------------------------------- 1 | try: 2 | from sortedcontainers import * 3 | from math import gcd, lcm 4 | except ImportError: 5 | pass 6 | from bisect import * 7 | from collections import * 8 | from functools import * 9 | from heapq import * 10 | import itertools 11 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 12 | 13 | from util import * 14 | 15 | """ 16 | echo "=== sample ===" ; python3 day23.py < sample.in 17 | echo "=== real ===" ; python3 day23.py < day23.in 18 | echo "=== sample ===" ; python3 day23.py < sample.in ; echo "=== real ===" ; python3 day23.py < day23.in 19 | """ 20 | 21 | A = sys.stdin.read().rstrip('\n').splitlines() 22 | N = len(A) 23 | 24 | grid = set() 25 | 26 | look_order = [ 27 | (0, -1), # north 28 | (0, 1), # south 29 | (-1, 0), # west 30 | (1, 0), # east 31 | ] 32 | 33 | adj = [ 34 | [(0, -1), (1, -1), (-1, -1)], 35 | [(0, 1), (1, 1), (-1, 1)], 36 | [(-1, 0), (-1, -1), (-1, 1)], 37 | [(1, 0), (1, -1), (1, 1)], 38 | ] 39 | 40 | for i, line in enumerate(A): 41 | for j, ch in enumerate(line): 42 | if ch == '#': 43 | grid.add((j, i)) # (x, y) 44 | 45 | start_idx = 0 46 | 47 | ROUNDS = 10 48 | 49 | def print_grid(grid): 50 | min_x = min(x for x, y in grid) 51 | max_x = max(x for x, y in grid) 52 | min_y = min(y for x, y in grid) 53 | max_y = max(y for x, y in grid) 54 | for y in range(min_y, max_y + 1): 55 | for x in range(min_x, max_x + 1): 56 | print('#' if (x, y) in grid else '.', end='') 57 | print() 58 | print() 59 | 60 | 61 | ri = 0 62 | while True: 63 | ri += 1 64 | ctr = Counter() 65 | proposal = [] 66 | for x, y in grid: 67 | something_adj = False 68 | for nx, ny in n8(x, y): 69 | if (nx, ny) in grid: 70 | something_adj = True 71 | break 72 | if not something_adj: 73 | continue 74 | for i in range(4): 75 | idx = (start_idx + i) % 4 76 | something_adj = False 77 | for dx, dy in adj[idx]: 78 | nx, ny = x + dx, y + dy 79 | if (nx, ny) in grid: 80 | something_adj = True 81 | break 82 | if not something_adj: 83 | dx, dy = look_order[idx] 84 | proposal.append((x, y, x + dx, y + dy)) 85 | ctr[(x + dx, y + dy)] += 1 86 | break 87 | moved = False 88 | for x, y, nx, ny in proposal: 89 | if ctr[(nx, ny)] == 1: 90 | moved = True 91 | grid.remove((x, y)) 92 | grid.add((nx, ny)) 93 | if not moved: 94 | print(ri) 95 | exit(0) 96 | start_idx = (start_idx + 1) % 4 97 | 98 | 99 | 100 | min_x = min(x for x, y in grid) 101 | max_x = max(x for x, y in grid) 102 | min_y = min(y for x, y in grid) 103 | max_y = max(y for x, y in grid) 104 | 105 | res = 0 106 | for y in range(min_y, max_y + 1): 107 | for x in range(min_x, max_x + 1): 108 | if (x, y) not in grid: 109 | res += 1 110 | print(res) -------------------------------------------------------------------------------- /2022/day24.py: -------------------------------------------------------------------------------- 1 | try: 2 | from sortedcontainers import * 3 | from math import gcd, lcm 4 | except ImportError: 5 | pass 6 | from bisect import * 7 | from collections import * 8 | from functools import * 9 | from heapq import * 10 | import itertools 11 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 12 | 13 | from util import * 14 | 15 | """ 16 | echo "=== sample ===" ; pypy3 day24.py < sample.in 17 | echo "=== real ===" ; pypy3 day24.py < day24.in 18 | echo "=== sample ===" ; pypy3 day24.py < sample.in ; echo "=== real ===" ; pypy3 day24.py < day24.in 19 | """ 20 | 21 | A = sys.stdin.read().rstrip('\n').splitlines() 22 | N = len(A) 23 | 24 | res = 0 25 | H = len(A) 26 | W = len(A[0]) 27 | 28 | period = lcm(H - 2, W - 2) 29 | 30 | grid = A 31 | 32 | blizzards = [] 33 | 34 | for i in range(H): 35 | for j in range(W): 36 | if grid[i][j] == '>': 37 | blizzards.append((j, i, 1, 0)) 38 | elif grid[i][j] == '<': 39 | blizzards.append((j, i, -1, 0)) 40 | elif grid[i][j] == '^': 41 | blizzards.append((j, i, 0, -1)) 42 | elif grid[i][j] == 'v': 43 | blizzards.append((j, i, 0, 1)) 44 | 45 | src = (grid[0].index('.'), 0) 46 | tgt = (grid[-1].index('.'), H - 1) 47 | 48 | assert grid[src[1]][src[0]] == '.' 49 | assert grid[tgt[1]][tgt[0]] == '.' 50 | 51 | 52 | def get_loc_after_time2(b, t): 53 | x, y, dx, dy = b 54 | x -= 1 55 | y -= 1 56 | 57 | x += dx * t 58 | y += dy * t 59 | x %= W - 2 60 | y %= H - 2 61 | 62 | x += 1 63 | y += 1 64 | return (x, y) 65 | 66 | 67 | Q = deque() 68 | Q.append((src, 574)) # partials: 0, 297, 574 69 | seen = set() 70 | 71 | last_t = -1 72 | 73 | while Q: 74 | me, time = Q.popleft() 75 | nt = time + 1 76 | bad_set = set() 77 | if nt != last_t: 78 | last_t = nt 79 | for b in blizzards: 80 | bad_set.add(get_loc_after_time2(b, nt)) 81 | for x, y in n5(*me, W, H): 82 | if (x, y) in bad_set: 83 | continue 84 | if grid[y][x] == '#': 85 | continue 86 | if (x, y) == tgt: 87 | print(nt) 88 | exit() 89 | ntp = nt % period 90 | if ((x, y), ntp) not in seen: 91 | seen.add(((x, y), ntp)) 92 | Q.append(((x, y), nt)) 93 | -------------------------------------------------------------------------------- /2022/day25.py: -------------------------------------------------------------------------------- 1 | try: 2 | from sortedcontainers import * 3 | from math import gcd, lcm 4 | except ImportError: 5 | pass 6 | from bisect import * 7 | from collections import * 8 | from functools import * 9 | from heapq import * 10 | import itertools 11 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 12 | 13 | from util import * 14 | 15 | """ 16 | echo "=== sample ===" ; pypy3 day25.py < sample.in 17 | echo "=== real ===" ; pypy3 day25.py < day25.in 18 | echo "=== sample ===" ; pypy3 day25.py < sample.in ; echo "=== real ===" ; pypy3 day25.py < day25.in 19 | """ 20 | 21 | A = sys.stdin.read().rstrip('\n').splitlines() 22 | N = len(A) 23 | 24 | val = { 25 | '2': 2, 26 | '1': 1, 27 | '0': 0, 28 | '-': -1, 29 | '=': -2, 30 | } 31 | 32 | sum = 0 33 | 34 | for line in A: 35 | acc = 0 36 | for i, c in enumerate(reversed(line)): 37 | p = i 38 | acc += val[c] * 5 ** p 39 | 40 | sum += acc 41 | 42 | 43 | print(sum) 44 | 45 | 46 | val_rev = { 47 | 2: '2', 48 | 1: '1', 49 | 0: '0', 50 | -1: '-', 51 | -2: '=', 52 | } 53 | 54 | res = '' 55 | 56 | while True: 57 | if not sum: 58 | break 59 | digit = sum % 5 60 | if digit == 4: 61 | digit = -1 62 | if digit == 3: 63 | digit = -2 64 | res += val_rev[digit] 65 | sum -= digit 66 | sum //= 5 67 | 68 | print(''.join(res[::-1])) 69 | 70 | -------------------------------------------------------------------------------- /2022/day3.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day3.py < sample.in 14 | echo "=== real ===" ; py day3.py < day3.in 15 | echo "=== sample ===" ; py day3.py < sample.in ; echo "=== real ===" ; py day3.py < day3.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | 20 | def get_prio(c): 21 | if c in ascii_lowercase: 22 | return ascii_lowercase.index(c) + 1 23 | else: 24 | return ascii_uppercase.index(c) + 1 + 26 25 | 26 | res = 0 27 | for i in range(0, len(A), 3): 28 | a, b, c = A[i], A[i + 1], A[i + 2] 29 | d = set(ascii_lowercase) | set(ascii_uppercase) 30 | for line in (a, b, c): 31 | # l, r = line[:len(line)//2], line[len(line)//2:] 32 | d = d & set(line) 33 | # d = d & set(l) 34 | # d = d & set(r) 35 | for x in d: 36 | res += get_prio(x) 37 | 38 | 39 | 40 | print(res) 41 | -------------------------------------------------------------------------------- /2022/day4.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day4.py < sample.in 14 | echo "=== real ===" ; py day4.py < day4.in 15 | echo "=== sample ===" ; py day4.py < sample.in ; echo "=== real ===" ; py day4.py < day4.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | 20 | res = 0 21 | res2 = 0 22 | 23 | for line in A: 24 | p1, p2 = line.split(',') 25 | p1l, p1r = map(int, p1.split('-')) 26 | p2l, p2r = map(int, p2.split('-')) 27 | # check if one contains the other 28 | if p1l <= p2l <= p2r <= p1r: 29 | res += 1 30 | elif p2l <= p1l <= p1r <= p2r: 31 | res += 1 32 | 33 | # check if one overlaps 34 | if p1l <= p2l <= p1r or p2l <= p1l <= p2r: 35 | res2 += 1 36 | 37 | print(res) 38 | print(res2) 39 | -------------------------------------------------------------------------------- /2022/day5.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day5.py < sample.in 14 | echo "=== real ===" ; py day5.py < day5.in 15 | echo "=== sample ===" ; py day5.py < sample.in ; echo "=== real ===" ; py day5.py < day5.in 16 | """ 17 | 18 | A = sys.stdin.read().strip() 19 | 20 | _, moves = A.split('\n\n') 21 | 22 | moves = moves.strip().splitlines() 23 | 24 | """ 25 | [V] [B] [F] 26 | [N] [Q] [W] [R] [B] 27 | [F] [D] [S] [B] [L] [P] 28 | [S] [J] [C] [F] [C] [D] [G] 29 | [M] [M] [H] [L] [P] [N] [P] [V] 30 | [P] [L] [D] [C] [T] [Q] [R] [S] [J] 31 | [H] [R] [Q] [S] [V] [R] [V] [Z] [S] 32 | [J] [S] [N] [R] [M] [T] [G] [C] [D] 33 | 1 2 3 4 5 6 7 8 9 34 | """ 35 | 36 | state = [ 37 | 'VNFSMPHJ', 38 | 'QDJMLRS', 39 | 'BWSCHDQN', 40 | 'LCSR', 41 | 'BFPTVM', 42 | 'CNQRT', 43 | 'RVG', 44 | 'RLDPSZC', 45 | 'FBPGVJSD', 46 | ] 47 | 48 | state = [list(x)[::-1] for x in state] 49 | 50 | for line in moves: 51 | _, n, _, f, _, t = line.split() 52 | n = int(n) 53 | f = int(f) 54 | t = int(t) 55 | 56 | f -= 1 57 | t -= 1 58 | popped = [] 59 | for i in range(n): 60 | x = state[f].pop() 61 | popped.append(x) 62 | popped = popped[::-1] 63 | state[t] += popped 64 | 65 | res = [] 66 | for row in state: 67 | res.append(row[-1]) 68 | print(''.join(res)) 69 | -------------------------------------------------------------------------------- /2022/day6.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day6.py < sample.in 14 | echo "=== real ===" ; py day6.py < day6.in 15 | echo "=== sample ===" ; py day6.py < sample.in ; echo "=== real ===" ; py day6.py < day6.in 16 | """ 17 | 18 | A = sys.stdin.read().strip() 19 | 20 | 21 | for i in range(0, len(A) - 13): 22 | if len(set(A[i:i+14])) == 14: 23 | print(i + 14) 24 | break 25 | -------------------------------------------------------------------------------- /2022/day7.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day7.py < sample.in 14 | echo "=== real ===" ; py day7.py < day7.in 15 | echo "=== sample ===" ; py day7.py < sample.in ; echo "=== real ===" ; py day7.py < day7.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | 20 | tree = {} 21 | 22 | i = 0 23 | cd = [] 24 | while i < len(A): 25 | line = A[i] 26 | assert line.startswith('$') 27 | args = line.split() 28 | if args[1] == 'cd': 29 | if args[2] == '/': 30 | cd = [] 31 | elif args[2] == '..': 32 | cd.pop() 33 | else: 34 | cd.append(args[2]) 35 | i += 1 36 | elif args[1] == 'ls': 37 | while True: 38 | i += 1 39 | if i >= len(A): 40 | break 41 | entry = A[i].split() 42 | if entry[0] == 'dir': 43 | pass 44 | elif entry[0] == '$': 45 | break 46 | else: 47 | size, fname = entry 48 | size = int(size) 49 | cur = tree 50 | for d in cd: 51 | if d not in cur: 52 | cur[d] = {} 53 | cur = cur[d] 54 | cur[fname] = size 55 | 56 | ans = 0 57 | 58 | def get_size(dir): 59 | global ans 60 | me = 0 61 | for k, v in dir.items(): 62 | if isinstance(v, int): 63 | me += v 64 | else: 65 | me += get_size(v) 66 | if me <= 100000: 67 | ans += me 68 | return me 69 | used = get_size(tree) 70 | 71 | print(ans) 72 | 73 | total = 70000000 74 | tgt_unused = 30000000 75 | cur_unused = total - used 76 | ans = 99e99 77 | 78 | def get_size2(dir): 79 | global ans 80 | me = 0 81 | for k, v in dir.items(): 82 | if isinstance(v, int): 83 | me += v 84 | else: 85 | me += get_size2(v) 86 | if me >= tgt_unused - cur_unused: 87 | ans = min(ans, me) 88 | return me 89 | get_size2(tree) 90 | 91 | print(ans) -------------------------------------------------------------------------------- /2022/day8.py: -------------------------------------------------------------------------------- 1 | from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day8.py < sample.in 14 | echo "=== real ===" ; py day8.py < day8.in 15 | echo "=== sample ===" ; py day8.py < sample.in ; echo "=== real ===" ; py day8.py < day8.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | 20 | res = 0 21 | 22 | R = len(A) 23 | C = len(A[0]) 24 | 25 | def visible(x, y): 26 | if x == 0 or x == R - 1 or y == 0 or y == C - 1: 27 | return True 28 | val = int(A[x][y]) 29 | # to the up 30 | up = max([int(A[i][y]) for i in range(x)]) 31 | if up < val: 32 | return True 33 | down = max([int(A[i][y]) for i in range(x + 1, R)]) 34 | if down < val: 35 | return True 36 | left = max([int(A[x][i]) for i in range(y)]) 37 | if left < val: 38 | return True 39 | right = max([int(A[x][i]) for i in range(y + 1, C)]) 40 | if right < val: 41 | return True 42 | return False 43 | 44 | def dist(x, y): 45 | up = [int(A[i][y]) for i in range(x - 1, -1, -1)] 46 | down = [int(A[i][y]) for i in range(x + 1, R)] 47 | left = [int(A[x][i]) for i in range(y - 1, -1, -1)] 48 | right = [int(A[x][i]) for i in range(y + 1, C)] 49 | val = int(A[x][y]) 50 | def helper(arr): 51 | if not arr: 52 | return 0 53 | for i in range(len(arr)): 54 | if arr[i] >= val: 55 | return i + 1 56 | return len(arr) 57 | return helper(up) * helper(down) * helper(left) * helper(right) 58 | 59 | res2 = 0 60 | for i in range(R): 61 | for j in range(C): 62 | if visible(i, j): 63 | res += 1 64 | res2 = max(res2, dist(i, j)) 65 | 66 | print(res) 67 | print(res2) 68 | -------------------------------------------------------------------------------- /2022/day9.py: -------------------------------------------------------------------------------- 1 | # from sortedcontainers import * 2 | 3 | from bisect import * 4 | from collections import * 5 | from functools import * 6 | from heapq import * 7 | import itertools 8 | from string import whitespace, ascii_lowercase, ascii_uppercase, ascii_letters, digits, hexdigits, octdigits, punctuation, printable 9 | 10 | from util import * 11 | 12 | """ 13 | echo "=== sample ===" ; py day9.py < sample.in 14 | echo "=== real ===" ; py day9.py < day9.in 15 | echo "=== sample ===" ; py day9.py < sample.in ; echo "=== real ===" ; py day9.py < day9.in 16 | """ 17 | 18 | A = sys.stdin.read().strip().splitlines() 19 | 20 | visited = set() 21 | 22 | hx, hy = 0, 0 23 | tx, ty = 0, 0 24 | 25 | visited.add((tx, ty)) 26 | 27 | knots = [[0, 0] for _ in range(10)] 28 | 29 | for line in A: 30 | d, c = line.split() 31 | d = DIRS_M[d] 32 | c = int(c) 33 | dxx, dyy = DIRS[d] 34 | for i in range(c): 35 | knots[0][0] += dxx 36 | knots[0][1] += dyy 37 | for ti in range(1, len(knots)): 38 | hx = knots[ti - 1][0] 39 | hy = knots[ti - 1][1] 40 | tx = knots[ti][0] 41 | ty = knots[ti][1] 42 | if hx == tx: 43 | if abs(hy - ty) >= 2: 44 | if ty > hy: 45 | ty -= 1 46 | else: 47 | ty += 1 48 | elif hy == ty: 49 | if abs(hx - tx) >= 2: 50 | if tx > hx: 51 | tx -= 1 52 | else: 53 | tx += 1 54 | else: 55 | if abs(hx - tx) >= 2 or abs(hy - ty) >= 2: 56 | dx = sgn(hx - tx) 57 | dy = sgn(hy - ty) 58 | assert dx and dy 59 | tx += dx 60 | ty += dy 61 | knots[ti][0] = tx 62 | knots[ti][1] = ty 63 | visited.add((knots[-1][0], knots[-1][1])) 64 | 65 | 66 | print(len(visited)) 67 | 68 | -------------------------------------------------------------------------------- /2022/util.py: -------------------------------------------------------------------------------- 1 | import re 2 | from operator import add 3 | from collections import defaultdict, Counter 4 | import itertools 5 | import math 6 | 7 | import sys 8 | sys.setrecursionlimit(int(1e7)) # segfault == you done messed up 9 | 10 | # convention that positive y is down 11 | # increment to clockwise/turn right, decrement to counterclockwise/turn left 12 | # index with grid[y][x] 13 | 14 | # this is x, y meta 15 | DIRS = { 16 | 0: (0, -1), 17 | 1: (1, 0), 18 | 2: (0, 1), 19 | 3: (-1, 0), 20 | } 21 | 22 | DIRS_M = { 23 | 'U': 0, 24 | 'R': 1, 25 | 'D': 2, 26 | 'L': 3, 27 | 'N': 0, 28 | 'E': 1, 29 | 'S': 2, 30 | 'W': 3, 31 | } 32 | 33 | inf = INF = float('inf') 34 | 35 | def sgn(x): 36 | if x == 0: 37 | return 0 38 | return x // abs(x) 39 | 40 | 41 | def read_input(fname, t=lambda x: x, strip_lines=True, force_multi=False): 42 | with open(fname, 'r') as f: 43 | contents = f.read() 44 | if strip_lines: 45 | lines = contents.strip().split('\n') 46 | else: 47 | lines = contents.split('\n') 48 | if len(lines) == 1 and not force_multi: 49 | return t(lines[0]) 50 | return list(map(t, lines)) 51 | 52 | def maybe_int(s): 53 | try: 54 | return int(s) 55 | except ValueError: 56 | return s 57 | 58 | def keep_by_index(indices, arr): 59 | result = [] 60 | for i in sorted(indices): 61 | if i < len(arr): 62 | result.append(arr[i]) 63 | return result 64 | 65 | def remove_by_index(indices, arr): 66 | result = [] 67 | to_remove = set(indices) 68 | for i in range(len(arr)): 69 | if i not in to_remove: 70 | result.append(arr[i]) 71 | return result 72 | 73 | def min_by(f, arr): 74 | return min([(f(x), x) for x in arr])[1] 75 | 76 | def max_by(f, arr): 77 | return max([(f(x), x) for x in arr])[1] 78 | 79 | def parse_coord(line): 80 | return tuple(map(int, line.split(','))) 81 | 82 | def metric_taxi(a, b): 83 | return sum(abs(a[i] - b[i]) for i in range(len(a))) 84 | 85 | def move_by(d, p): 86 | if isinstance(d, int): 87 | d = DIRS[d] 88 | return tuple(map(add, d, p)) 89 | 90 | def parse_list(s): 91 | s = s.strip() 92 | return [int(x.strip('()[]<>')) for x in s.split(',')] 93 | 94 | def fatal(*args, **kwargs): 95 | print(*args, **kwargs) 96 | exit() 97 | 98 | def automata(grid, rule, iterations): 99 | R = len(grid) 100 | C = len(grid[0]) 101 | def get_neighbors(i, j): 102 | # for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1)): 103 | for ii, jj in ((i + 1, j), (i - 1, j), (i, j + 1), (i, j - 1), (i - 1, j - 1), (i - 1, j + 1), (i + 1, j - 1), (i + 1, j + 1)): 104 | if 0 <= ii < R and 0 <= jj < C: 105 | yield ii, jj 106 | 107 | for _ in range(iterations): 108 | new_grid = [[None] * C for _ in range(R)] 109 | for i in range(R): 110 | for j in range(C): 111 | neighbors = map(lambda x: grid[x[0]][x[1]], get_neighbors(i, j)) 112 | new_grid[i][j] = rule(grid[i][j], Counter(neighbors)) 113 | grid = new_grid 114 | return grid 115 | 116 | def print_grid(grid, t=lambda x: x): 117 | for row in grid: 118 | print(''.join(map(t, row))) 119 | 120 | def rule_gol(me, neighbors): 121 | if me == '*': 122 | return '*' if 2 <= neighbors['*'] <= 3 else '.' 123 | else: 124 | return '*' if neighbors['*'] == 3 else '.' 125 | 126 | def prod(L): 127 | result = 1 128 | for x in L: 129 | result *= x 130 | return result 131 | 132 | def reverse_dict(d): 133 | result = defaultdict(list) 134 | for k, v in d.items(): 135 | for x in v: 136 | result[x].append(k) 137 | return result 138 | 139 | builtin_map = map 140 | 141 | def map(*args, **kwargs): 142 | return list(builtin_map(*args, **kwargs)) 143 | 144 | def do_ps(lst): 145 | prefix = [0] 146 | for x in lst: 147 | prefix.append(prefix[-1] + x) 148 | return prefix 149 | 150 | def transpose(A): 151 | N = len(A) 152 | M = len(A[0]) 153 | res = [] 154 | for j in range(M): 155 | row = [A[i][j] for i in range(N)] 156 | res.append(row) 157 | return res 158 | 159 | def crt(n, a): 160 | from functools import reduce 161 | sum = 0 162 | prod = reduce(lambda a, b: a * b, n) 163 | for n_i, a_i in zip(n, a): 164 | p = prod // n_i 165 | sum += a_i * pow(p, -1, n_i) * p 166 | return sum % prod 167 | 168 | def dump_dict_grid(d, t=lambda x: x): 169 | min_x = min(x for x, y in d.keys()) 170 | max_x = max(x for x, y in d.keys()) 171 | min_y = min(y for x, y in d.keys()) 172 | max_y = max(y for x, y in d.keys()) 173 | for y in range(min_y, max_y + 1): 174 | for x in range(min_x, max_x + 1): 175 | print(t(d[(x, y)]), end='') 176 | print() 177 | 178 | def ordch(ch: str) -> int: 179 | assert len(ch) == 1 180 | x = ord(ch) 181 | if x >= ord('a') and x <= ord('z'): return x - ord('a') 182 | if x >= ord('A') and x <= ord('Z'): return x - ord('A') 183 | raise Exception(f"{ch} is not alphabetic") 184 | 185 | def add_interval(ss, L, R): 186 | # [L, R) 187 | assert L <= R 188 | if L == R: 189 | return None 190 | idx = ss.bisect_left((L, R)) 191 | while idx < len(ss): 192 | ival = ss[idx] 193 | if ival[0] > R: 194 | break 195 | R = max(R, ival[1]) 196 | ss.pop(idx) 197 | if idx > 0: 198 | idx -= 1 199 | ival = ss[idx] 200 | if ival[1] >= L: 201 | L = min(L, ival[0]) 202 | R = max(R, ival[1]) 203 | ss.pop(idx) 204 | res = (L, R) 205 | ss.add(res) 206 | return res 207 | 208 | def remove_interval(ss, L, R): 209 | # [L, R) 210 | assert L <= R 211 | if L == R: 212 | return 213 | added = add_interval(ss, L, R) 214 | r2 = added[1] 215 | ss.remove(added) 216 | if added[0] != L: 217 | ss.add((added[0], L)) 218 | if R != r2: 219 | ss.add((R, r2)) 220 | 221 | def pad_grid(grid, ch=' '): 222 | C = max(len(row) for row in grid) 223 | for i in range(len(grid)): 224 | if len(grid[i]) < C: 225 | grid[i] += ch * (C - len(grid[i])) 226 | return grid 227 | 228 | def n4(i, j, mi=None, mj=None): 229 | """(x, y, C, R) or (i, j, R, C)""" 230 | for di, dj in ((-1, 0), (0, -1), (1, 0), (0, 1)): 231 | ii = i + di 232 | jj = j + dj 233 | if mi is not None: 234 | if ii < 0 or ii >= mi: 235 | continue 236 | if mj is not None: 237 | if jj < 0 or jj >= mj: 238 | continue 239 | yield ii, jj 240 | 241 | def n8(i, j, mi=None, mj=None): 242 | """(x, y, C, R) or (i, j, R, C)""" 243 | for di in range(-1, 2): 244 | for dj in range(-1, 2): 245 | ii = i + di 246 | jj = j + dj 247 | if di == 0 and dj == 0: 248 | continue 249 | if mi is not None: 250 | if ii < 0 or ii >= mi: 251 | continue 252 | if mj is not None: 253 | if jj < 0 or dj >= mj: 254 | continue 255 | yield ii, jj 256 | 257 | def n5(i, j, mi=None, mj=None): 258 | """(x, y, C, R) or (i, j, R, C)""" 259 | yield i, j 260 | yield from n4(i, j, mi, mj) 261 | 262 | def n9(i, j, mi=None, mj=None): 263 | """(x, y, C, R) or (i, j, R, C)""" 264 | yield i, j 265 | yield from n8(i, j, mi, mj) 266 | 267 | def n4d(pt): 268 | d = len(pt) 269 | for i in range(d): 270 | for sgn in (-1, 1): 271 | pt2 = list(pt) 272 | pt2[i] += sgn 273 | yield tuple(pt2) 274 | 275 | def n8d(pt): 276 | d = len(pt) 277 | for choice in itertools.product((-1, 0, 1), repeat=d): 278 | if not(any(choice)): 279 | continue 280 | pt2 = list(pt) 281 | for i, sgn in enumerate(choice): 282 | pt2[i] += sgn 283 | yield tuple(pt2) 284 | 285 | def ints(s, expected=None): 286 | res = map(int, re.findall(r'-?\d+', s)) 287 | if expected is not None: 288 | assert len(res) == expected, f"for string {s}, expected {expected} ints, got {len(res)} ({res})" 289 | return res 290 | 291 | 292 | def rline(x1, y1, x2, y2, unhinged=False): 293 | d = math.gcd(x2 - x1, y2 - y1) or 1 294 | assert d >= 1 295 | dy = (y2 - y1) // d 296 | dx = (x2 - x1) // d 297 | if not unhinged: 298 | assert dx in (-1, 0, 1) and dy in (-1, 0, 1), f"line ({x1}, {y1}) -> ({x2}, {y2}) is not horizontal, vertical, or 45 degree diagonal" 299 | while True: 300 | yield x1, y1 301 | if (x1, y1) == (x2, y2): break 302 | x1 += dx 303 | y1 += dy 304 | 305 | def _util_test(): 306 | assert list(rline(1, 1, 1, 1)) == [(1, 1)] 307 | assert list(rline(1, 1, 3, 1)) == [(1, 1), (2, 1), (3, 1)] 308 | assert list(rline(1, 3, 1, 1)) == [(1, 3), (1, 2), (1, 1)] 309 | assert list(rline(5, -3, 7, -5)) == [(5, -3), (6, -4), (7, -5)] 310 | assert list(rline(-6, 10, -10, 6)) == [(-6, 10), (-7, 9), (-8, 8), (-9, 7), (-10, 6)] 311 | assert list(rline(0, 0, 3, 4, True)) == [(0, 0), (3, 4)] 312 | assert list(rline(-106, -108, -100, -100, True)) == [(-106, -108), (-103, -104), (-100, -100)] 313 | assert list(rline(-106, 108, -100, 100, True)) == [(-106, 108), (-103, 104), (-100, 100)] 314 | assert list(rline(106, -108, 100, -100, True)) == [(106, -108), (103, -104), (100, -100)] 315 | assert list(rline(106, 108, 100, 100, True)) == [(106, 108), (103, 104), (100, 100)] 316 | 317 | assert ints('1 2 3 45 + 67 = -89,(90,91,92) asdf93asdf') == [1, 2, 3, 45, 67, -89, 90, 91, 92, 93] 318 | 319 | 320 | if __name__ == '__main__': 321 | _util_test() 322 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Solutions for Advent of Code, optimized purely for implementation speed, with little/no edits afterwards. 2 | 3 | Most solutions take input on stdin and output at least the answer to part 2 on stdout (and sometimes part 1). 4 | --------------------------------------------------------------------------------