├── 2015 ├── day04 │ ├── input.txt │ └── sol.py ├── day11 │ ├── input.txt │ └── sol.py ├── day20 │ ├── input.txt │ └── sol.py ├── day10 │ ├── input.txt │ └── sol.py ├── day22 │ └── input.txt ├── day21 │ └── input.txt ├── day25 │ ├── input.txt │ └── sol.py ├── day17 │ ├── input.txt │ └── sol.py ├── day24 │ ├── input.txt │ └── sol.py ├── day15 │ ├── input.txt │ └── sol.py ├── day08 │ └── sol.py ├── day01 │ └── sol.py ├── day23 │ ├── input.txt │ └── sol.py ├── day02 │ └── sol.py ├── day09 │ ├── sol.py │ └── input.txt ├── day03 │ └── sol.py ├── day12 │ └── sol.py ├── day14 │ ├── input.txt │ └── sol.py ├── day05 │ └── sol.py ├── day06 │ └── sol.py ├── day07 │ └── sol.py ├── day19 │ └── input.txt └── day18 │ └── sol.py ├── 2016 ├── day05 │ └── input.txt ├── day13 │ └── input.txt ├── day14 │ ├── input.txt │ ├── fast │ │ └── Cargo.toml │ └── sol.py ├── day17 │ ├── input.txt │ └── sol.py ├── day19 │ ├── input.txt │ └── sol.py ├── day16 │ ├── input.txt │ └── sol.py ├── day18 │ ├── input.txt │ └── sol.py ├── day12 │ ├── input.txt │ └── sol.py ├── day23 │ ├── input_opt.txt │ ├── input.txt │ └── input_dec.txt ├── day06 │ └── sol.py ├── day15 │ ├── input.txt │ └── sol.py ├── day25 │ ├── input.txt │ ├── sol.py │ └── input_dec.txt ├── day11 │ └── input.txt ├── day01 │ ├── input.txt │ └── sol.py ├── day20 │ └── sol.py ├── day03 │ └── sol.py ├── day09 │ └── sol.py ├── day04 │ └── sol.py └── day02 │ └── sol.py ├── 2017 ├── day06 │ ├── input.txt │ └── sol.py ├── day10 │ └── input.txt ├── day23 │ ├── decompiled.txt │ ├── input.txt │ ├── input_opt.txt │ └── optimized.py ├── day08 │ └── sol.py ├── day17 │ └── sol.py ├── day13 │ ├── input.txt │ └── sol.py ├── day18 │ └── input.txt ├── day05 │ └── sol.py ├── day24 │ └── input.txt ├── day04 │ └── sol.py ├── day02 │ ├── sol.py │ └── input.txt ├── day01 │ └── sol.py ├── day15 │ └── sol.py ├── day22 │ ├── input.txt │ └── sol.py ├── day12 │ └── sol.py ├── day09 │ └── soll.py ├── day11 │ └── sol.py ├── day25 │ └── sol.py ├── day19 │ └── sol.py └── day07 │ └── sol.py ├── 2018 ├── day11 │ └── input.txt ├── day14 │ └── input.txt ├── day22 │ └── input.txt ├── day09 │ ├── input.txt │ └── sol.py ├── day21 │ └── input.txt ├── day01 │ └── sol.py ├── day19 │ └── input.txt ├── day12 │ └── input.txt ├── day06 │ └── input.txt ├── day05 │ └── sol.py ├── day02 │ └── sol.py ├── day23 │ └── sol.py ├── day25 │ └── sol.py ├── day15 │ └── input.txt ├── day03 │ └── sol.py └── day08 │ └── sol.py ├── 2019 ├── day04 │ ├── input.txt │ └── sol.py ├── day24 │ └── input.txt ├── day12 │ ├── input.txt │ ├── Cargo.lock │ └── Cargo.toml ├── intcode │ └── README.md ├── day02 │ ├── input.txt │ └── sol.py ├── day01 │ ├── sol.py │ └── input.txt ├── day16 │ ├── input.txt │ └── sol.py ├── day10 │ └── input.txt ├── day06 │ └── sol.py ├── day08 │ └── sol.py ├── day22 │ └── sol.py ├── day07 │ └── input.txt ├── day03 │ └── sol.py └── day19 │ └── input.txt ├── 2020 ├── day15 │ ├── input.txt │ └── sol.py ├── day23 │ ├── input.txt │ └── sol.py ├── day25 │ ├── input.txt │ └── sol.py ├── optimized │ ├── .cargo │ │ └── config │ ├── _build.rs │ ├── Cargo.toml │ ├── expected.txt │ └── src │ │ └── utils.rs ├── day05 │ ├── golf.py │ └── sol.py ├── day17 │ └── input.txt ├── day13 │ ├── input.txt │ └── sol.py ├── day22 │ └── input.txt ├── day01 │ └── sol.py ├── day06 │ └── sol.py ├── day02 │ └── sol.py ├── day10 │ ├── input.txt │ └── sol.py ├── day03 │ └── sol.py ├── day09 │ └── sol.py ├── day07 │ └── sol.py ├── day08 │ └── sol.py ├── day21 │ └── sol.py ├── day12 │ └── sol.py ├── day14 │ └── sol.py ├── day04 │ └── sol.py ├── day19 │ └── sol.py └── day24 │ └── sol.py ├── 2021 ├── day17 │ └── input.txt ├── optimized │ ├── .cargo │ │ └── config │ ├── bare │ │ ├── Makefile │ │ ├── test.c │ │ ├── Cargo.toml │ │ ├── Cargo.lock │ │ └── day01.asm │ ├── expected.txt │ ├── Cargo.toml │ └── src │ │ ├── days.rs │ │ └── utils.rs ├── day21 │ ├── input.txt │ └── sol.py ├── day23 │ └── input.txt ├── day11 │ ├── input.txt │ └── sol.py ├── day12 │ ├── input.txt │ └── sol.py ├── day01 │ └── sol.py ├── day06 │ ├── input.txt │ └── sol.py ├── day02 │ └── sol.py ├── day10 │ └── sol.py ├── day24 │ ├── sol.py │ └── input_decoded_2.txt ├── day03 │ └── sol.py ├── day14 │ ├── sol.py │ └── input.txt ├── day07 │ └── sol.py ├── day05 │ └── sol.py ├── day13 │ └── sol.py ├── day25 │ └── sol.py ├── day15 │ └── sol.py ├── day04 │ └── sol.py ├── day09 │ └── sol.py └── day16 │ └── input.txt ├── 2022 ├── day19 │ └── rust │ │ └── Cargo.toml ├── day01 │ └── sol.py ├── day06 │ └── sol.py ├── day04 │ └── sol.py ├── day02 │ └── sol.py ├── day03 │ └── sol.py ├── day20 │ └── sol.py ├── day25 │ └── sol.py ├── day10 │ └── sol.py ├── day07 │ └── sol.py ├── day05 │ └── sol.py ├── day15 │ └── sol.py ├── day13 │ └── sol.py ├── day08 │ └── sol.py ├── day21 │ └── sol.py ├── day09 │ └── sol.py ├── day18 │ └── sol.py └── day14 │ └── sol.py ├── 2023 ├── day06 │ ├── input.txt │ └── sol.py ├── day04 │ └── sol.py ├── day09 │ └── sol.py ├── day02 │ └── sol.py ├── day08 │ └── sol.py ├── day20 │ └── input.txt ├── day18 │ └── sol.py ├── day11 │ └── sol.py ├── day15 │ └── sol.py ├── day25 │ └── sol.py ├── day12 │ └── sol.py └── day01 │ └── sol.py ├── 2024 ├── day11 │ ├── input.txt │ └── sol.py ├── day21 │ └── input.txt ├── day17 │ └── input.txt ├── day23 │ ├── sol.py │ └── sol_without_networkx.py ├── day24 │ └── notes.md ├── day01 │ └── sol.py ├── day02 │ └── sol.py ├── day03 │ └── sol.py ├── day19 │ └── sol.py ├── day05 │ └── sol.py ├── day13 │ ├── sol.py │ └── sol_no_z3.py ├── day10 │ └── sol.py ├── day14 │ └── sol.py ├── day07 │ └── sol.py ├── day22 │ └── sol.py ├── day18 │ └── sol.py ├── day25 │ └── sol.py ├── day08 │ └── sol.py └── day04 │ └── sol.py ├── 2025 ├── day02 │ ├── input.txt │ └── sol.py ├── day03 │ └── sol.py ├── day07 │ └── sol.py ├── day05 │ └── sol.py ├── day01 │ └── sol.py ├── day08 │ └── sol.py ├── day04 │ └── sol.py └── day06 │ └── sol.py ├── .gitignore ├── README.md └── LICENSE-MIT /2015/day04/input.txt: -------------------------------------------------------------------------------- 1 | iwrupvqb 2 | -------------------------------------------------------------------------------- /2015/day11/input.txt: -------------------------------------------------------------------------------- 1 | hepxcrrq 2 | -------------------------------------------------------------------------------- /2015/day20/input.txt: -------------------------------------------------------------------------------- 1 | 36000000 2 | -------------------------------------------------------------------------------- /2016/day05/input.txt: -------------------------------------------------------------------------------- 1 | ojvtpuvg 2 | -------------------------------------------------------------------------------- /2016/day13/input.txt: -------------------------------------------------------------------------------- 1 | 1352 2 | -------------------------------------------------------------------------------- /2016/day14/input.txt: -------------------------------------------------------------------------------- 1 | ihaygndm 2 | -------------------------------------------------------------------------------- /2016/day17/input.txt: -------------------------------------------------------------------------------- 1 | vwbaicqe 2 | -------------------------------------------------------------------------------- /2016/day19/input.txt: -------------------------------------------------------------------------------- 1 | 3017957 2 | -------------------------------------------------------------------------------- /2018/day11/input.txt: -------------------------------------------------------------------------------- 1 | 5468 2 | -------------------------------------------------------------------------------- /2018/day14/input.txt: -------------------------------------------------------------------------------- 1 | 880751 2 | -------------------------------------------------------------------------------- /2015/day10/input.txt: -------------------------------------------------------------------------------- 1 | 1321131112 2 | -------------------------------------------------------------------------------- /2019/day04/input.txt: -------------------------------------------------------------------------------- 1 | 158126-624574 2 | -------------------------------------------------------------------------------- /2020/day15/input.txt: -------------------------------------------------------------------------------- 1 | 0,13,1,8,6,15 2 | -------------------------------------------------------------------------------- /2020/day23/input.txt: -------------------------------------------------------------------------------- 1 | 614752839 2 | -------------------------------------------------------------------------------- /2016/day16/input.txt: -------------------------------------------------------------------------------- 1 | 01111010110010011 2 | -------------------------------------------------------------------------------- /2020/day25/input.txt: -------------------------------------------------------------------------------- 1 | 14205034 2 | 18047856 3 | -------------------------------------------------------------------------------- /2015/day22/input.txt: -------------------------------------------------------------------------------- 1 | Hit Points: 55 2 | Damage: 8 3 | -------------------------------------------------------------------------------- /2018/day22/input.txt: -------------------------------------------------------------------------------- 1 | depth: 6084 2 | target: 14,709 3 | -------------------------------------------------------------------------------- /2017/day06/input.txt: -------------------------------------------------------------------------------- 1 | 14 0 15 12 11 11 3 5 1 6 8 4 9 1 8 4 2 | -------------------------------------------------------------------------------- /2021/day17/input.txt: -------------------------------------------------------------------------------- 1 | target area: x=32..65, y=-225..-177 2 | -------------------------------------------------------------------------------- /2024/day11/input.txt: -------------------------------------------------------------------------------- 1 | 1 24596 0 740994 60 803 8918 9405859 2 | -------------------------------------------------------------------------------- /2015/day21/input.txt: -------------------------------------------------------------------------------- 1 | Hit Points: 109 2 | Damage: 8 3 | Armor: 2 4 | -------------------------------------------------------------------------------- /2024/day21/input.txt: -------------------------------------------------------------------------------- 1 | 083A 2 | 935A 3 | 964A 4 | 149A 5 | 789A 6 | -------------------------------------------------------------------------------- /2018/day09/input.txt: -------------------------------------------------------------------------------- 1 | 486 players; last marble is worth 70833 points 2 | -------------------------------------------------------------------------------- /2019/day24/input.txt: -------------------------------------------------------------------------------- 1 | #.#.. 2 | .#.#. 3 | #...# 4 | .#..# 5 | ##.#. 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | code.txt 2 | cookie 3 | .vscode 4 | target 5 | venv 6 | .venv 7 | -------------------------------------------------------------------------------- /2017/day10/input.txt: -------------------------------------------------------------------------------- 1 | 106,118,236,1,130,0,235,254,59,205,2,87,129,25,255,118 2 | -------------------------------------------------------------------------------- /2020/optimized/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = "-C target-cpu=native" 3 | -------------------------------------------------------------------------------- /2021/optimized/.cargo/config: -------------------------------------------------------------------------------- 1 | [build] 2 | rustflags = "-C target-cpu=native" 3 | -------------------------------------------------------------------------------- /2021/day21/input.txt: -------------------------------------------------------------------------------- 1 | Player 1 starting position: 8 2 | Player 2 starting position: 4 3 | -------------------------------------------------------------------------------- /2023/day06/input.txt: -------------------------------------------------------------------------------- 1 | Time: 46 80 78 66 2 | Distance: 214 1177 1402 1024 3 | -------------------------------------------------------------------------------- /2021/day23/input.txt: -------------------------------------------------------------------------------- 1 | ############# 2 | #...........# 3 | ###D#A#C#D### 4 | #C#A#B#B# 5 | ######### 6 | -------------------------------------------------------------------------------- /2019/day12/input.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /2015/day25/input.txt: -------------------------------------------------------------------------------- 1 | To continue, please consult the code grid in the manual. Enter the code at row 2981, column 3075. 2 | -------------------------------------------------------------------------------- /2020/day05/golf.py: -------------------------------------------------------------------------------- 1 | print(*{*range(min(f:={int(bytes(c>>2&1^49 for c in t),2)//2 for t in open("t","rb")}),max(f))}-f) 2 | -------------------------------------------------------------------------------- /2016/day18/input.txt: -------------------------------------------------------------------------------- 1 | ^.....^.^^^^^.^..^^.^.......^^..^^^..^^^^..^.^^.^.^....^^...^^.^^.^...^^.^^^^..^^.....^.^...^.^.^^.^ 2 | -------------------------------------------------------------------------------- /2020/day17/input.txt: -------------------------------------------------------------------------------- 1 | ..##.##. 2 | #.#..### 3 | ##.#.#.# 4 | #.#.##.# 5 | ###..#.. 6 | .#.#..## 7 | #.##.### 8 | #.#..##. 9 | -------------------------------------------------------------------------------- /2024/day17/input.txt: -------------------------------------------------------------------------------- 1 | Register A: 47006051 2 | Register B: 0 3 | Register C: 0 4 | 5 | Program: 2,4,1,3,7,5,1,5,0,3,4,3,5,5,3,0 6 | -------------------------------------------------------------------------------- /2020/optimized/_build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | cc::Build::new() 3 | .file("src/days/day01.S") 4 | .compile("day1"); 5 | } 6 | -------------------------------------------------------------------------------- /2019/intcode/README.md: -------------------------------------------------------------------------------- 1 | The intcode compiler and assembler were moved to [benediktwerner/intcode](https://github.com/benediktwerner/intcode). 2 | -------------------------------------------------------------------------------- /2016/day14/fast/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fast" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rayon = "*" 8 | md5 = "*" 9 | -------------------------------------------------------------------------------- /2022/day19/rust/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aoc" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | rayon = "1.6" 8 | memuse = "0.2" 9 | -------------------------------------------------------------------------------- /2021/optimized/bare/Makefile: -------------------------------------------------------------------------------- 1 | 2 | build: 3 | @nasm -f elf64 day01.asm 4 | @ld day01.o -o day01 5 | @rm day01.o 6 | 7 | run: build 8 | @./day01 ../day01/input.txt 9 | -------------------------------------------------------------------------------- /2015/day17/input.txt: -------------------------------------------------------------------------------- 1 | 11 2 | 30 3 | 47 4 | 31 5 | 32 6 | 36 7 | 3 8 | 1 9 | 5 10 | 3 11 | 32 12 | 36 13 | 15 14 | 11 15 | 46 16 | 26 17 | 28 18 | 1 19 | 19 20 | 3 21 | -------------------------------------------------------------------------------- /2019/day12/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | [[package]] 4 | name = "day12" 5 | version = "0.1.0" 6 | 7 | -------------------------------------------------------------------------------- /2019/day12/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "day12" 3 | version = "0.1.0" 4 | authors = ["Benedikt Werner <1benediktwerner@gmail.com>"] 5 | edition = "2018" 6 | 7 | [dependencies] 8 | -------------------------------------------------------------------------------- /2020/day13/input.txt: -------------------------------------------------------------------------------- 1 | 1007268 2 | 17,x,x,x,x,x,x,41,x,x,x,x,x,x,x,x,x,937,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,x,13,x,x,x,x,23,x,x,x,x,x,29,x,397,x,x,x,x,x,37,x,x,x,x,x,x,x,x,x,x,x,x,19 3 | -------------------------------------------------------------------------------- /2021/day11/input.txt: -------------------------------------------------------------------------------- 1 | 5212166716 2 | 1567322581 3 | 2268461548 4 | 3481561744 5 | 6248342248 6 | 6526667368 7 | 5627335775 8 | 8124511754 9 | 4614137683 10 | 4724561156 11 | -------------------------------------------------------------------------------- /2021/optimized/bare/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | void main() { 5 | struct stat sb; 6 | printf("%d\n", sizeof(struct stat)); 7 | printf("%d\n", (long)&sb.st_size - (long)&sb); 8 | } 9 | -------------------------------------------------------------------------------- /2015/day24/input.txt: -------------------------------------------------------------------------------- 1 | 1 2 | 2 3 | 3 4 | 7 5 | 11 6 | 13 7 | 17 8 | 19 9 | 23 10 | 31 11 | 37 12 | 41 13 | 43 14 | 47 15 | 53 16 | 59 17 | 61 18 | 67 19 | 71 20 | 73 21 | 79 22 | 83 23 | 89 24 | 97 25 | 101 26 | 103 27 | 107 28 | 109 29 | 113 30 | -------------------------------------------------------------------------------- /2021/optimized/expected.txt: -------------------------------------------------------------------------------- 1 | 1713 1734 2 | 1840243 1727785422 3 | 2954600 1662846 4 | 38594 21184 5 | 7380 21373 6 | 366057 1653559299811 7 | 341534 93397632 8 | 554 990964 9 | 512 1600104 10 | -------------------------------------------------------------------------------- /2021/day12/input.txt: -------------------------------------------------------------------------------- 1 | start-YA 2 | ps-yq 3 | zt-mu 4 | JS-yi 5 | yq-VJ 6 | QT-ps 7 | start-yq 8 | YA-yi 9 | start-nf 10 | nf-YA 11 | nf-JS 12 | JS-ez 13 | yq-JS 14 | ps-JS 15 | ps-yi 16 | yq-nf 17 | QT-yi 18 | end-QT 19 | nf-yi 20 | zt-QT 21 | end-ez 22 | yq-YA 23 | end-JS 24 | -------------------------------------------------------------------------------- /2016/day12/input.txt: -------------------------------------------------------------------------------- 1 | cpy 1 a 2 | cpy 1 b 3 | cpy 26 d 4 | jnz c 2 5 | jnz 1 5 6 | cpy 7 c 7 | inc d 8 | dec c 9 | jnz c -2 10 | cpy a c 11 | inc a 12 | dec b 13 | jnz b -2 14 | cpy c b 15 | dec d 16 | jnz d -6 17 | cpy 13 c 18 | cpy 14 d 19 | inc a 20 | dec d 21 | jnz d -2 22 | dec c 23 | jnz c -5 24 | -------------------------------------------------------------------------------- /2015/day15/input.txt: -------------------------------------------------------------------------------- 1 | Sprinkles: capacity 2, durability 0, flavor -2, texture 0, calories 3 2 | Butterscotch: capacity 0, durability 5, flavor -3, texture 0, calories 3 3 | Chocolate: capacity 0, durability 0, flavor 5, texture -1, calories 8 4 | Candy: capacity 0, durability -1, flavor 0, texture 5, calories 8 5 | -------------------------------------------------------------------------------- /2022/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | elves = sorted(sum(map(int, elf.splitlines())) for elf in f.read().split("\n\n")) 8 | print("Part 1:", elves[-1]) 9 | print("Part 2:", sum(elves[-3:])) 10 | -------------------------------------------------------------------------------- /2016/day23/input_opt.txt: -------------------------------------------------------------------------------- 1 | cpy a b 2 | dec b 3 | cpy a d 4 | cpy 0 a 5 | cpy b c 6 | mul c d a 7 | nop 8 | nop 9 | nop 10 | nop 11 | dec b 12 | cpy b c 13 | cpy c d 14 | dec d 15 | inc c 16 | jnz d -2 17 | tgl c 18 | cpy -16 c 19 | jnz 1 c 20 | cpy 89 c 21 | jnz 77 d 22 | inc a 23 | inc d 24 | jnz d -2 25 | inc c 26 | jnz c -5 27 | -------------------------------------------------------------------------------- /2016/day23/input.txt: -------------------------------------------------------------------------------- 1 | cpy a b 2 | dec b 3 | cpy a d 4 | cpy 0 a 5 | cpy b c 6 | inc a 7 | dec c 8 | jnz c -2 9 | dec d 10 | jnz d -5 11 | dec b 12 | cpy b c 13 | cpy c d 14 | dec d 15 | inc c 16 | jnz d -2 17 | tgl c 18 | cpy -16 c 19 | jnz 1 c 20 | cpy 89 c 21 | jnz 77 d 22 | inc a 23 | inc d 24 | jnz d -2 25 | inc c 26 | jnz c -5 27 | -------------------------------------------------------------------------------- /2021/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | depths = [int(x) for x in f.read().splitlines()] 8 | print("Part 1:", sum(b > a for a, b in zip(depths, depths[1:]))) 9 | print("Part 2:", sum(b > a for a, b in zip(depths, depths[3:]))) 10 | -------------------------------------------------------------------------------- /2021/optimized/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aoc-2021-optimized" 3 | version = "0.1.0" 4 | authors = ["Benedikt Werner <1benediktwerner@gmail.com>"] 5 | edition = "2021" 6 | 7 | [profile.release] 8 | lto = true 9 | codegen-units = 1 10 | panic = "abort" 11 | 12 | [dependencies] 13 | anyhow = "1" 14 | clap = "4" 15 | rustc-hash = "2" 16 | itertools = "0.13" 17 | -------------------------------------------------------------------------------- /2016/day06/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | def main(): 5 | with open("input.txt") as f: 6 | x = list(zip(*(line.strip() for line in f))) 7 | 8 | print("Part 1:", "".join(max(c, key=c.count) for c in x)) 9 | print("Part 2:", "".join(min(c, key=c.count) for c in x)) 10 | 11 | 12 | if __name__ == "__main__": 13 | main() 14 | -------------------------------------------------------------------------------- /2016/day15/input.txt: -------------------------------------------------------------------------------- 1 | Disc #1 has 5 positions; at time=0, it is at position 2. 2 | Disc #2 has 13 positions; at time=0, it is at position 7. 3 | Disc #3 has 17 positions; at time=0, it is at position 10. 4 | Disc #4 has 3 positions; at time=0, it is at position 2. 5 | Disc #5 has 19 positions; at time=0, it is at position 9. 6 | Disc #6 has 7 positions; at time=0, it is at position 0. 7 | -------------------------------------------------------------------------------- /2016/day25/input.txt: -------------------------------------------------------------------------------- 1 | cpy a d 2 | cpy 9 c 3 | cpy 282 b 4 | inc d 5 | dec b 6 | jnz b -2 7 | dec c 8 | jnz c -5 9 | cpy d a 10 | jnz 0 0 11 | cpy a b 12 | cpy 0 a 13 | cpy 2 c 14 | jnz b 2 15 | jnz 1 6 16 | dec b 17 | dec c 18 | jnz c -4 19 | inc a 20 | jnz 1 -7 21 | cpy 2 b 22 | jnz c 2 23 | jnz 1 4 24 | dec b 25 | dec c 26 | jnz 1 -4 27 | jnz 0 0 28 | out b 29 | jnz a -19 30 | jnz 1 -21 31 | -------------------------------------------------------------------------------- /2021/optimized/bare/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aoc-2021-optimized-bare" 3 | version = "0.1.0" 4 | authors = ["Benedikt Werner <1benediktwerner@gmail.com>"] 5 | edition = "2021" 6 | 7 | [profile.dev] 8 | panic = "abort" 9 | 10 | [profile.release] 11 | lto = true 12 | codegen-units = 1 13 | panic = "abort" 14 | 15 | [dependencies] 16 | libc = { version = "0.2", default-features = false } 17 | -------------------------------------------------------------------------------- /2016/day25/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | instrs = f.read().splitlines() 8 | a = int(instrs[1].split()[1]) 9 | b = int(instrs[2].split()[1]) 10 | curr = a * b 11 | curr_b = f"{curr:b}" 12 | target = int("10" * (len(curr_b) // 2), 2) 13 | print("Part 1:", target - curr) 14 | -------------------------------------------------------------------------------- /2017/day23/decompiled.txt: -------------------------------------------------------------------------------- 1 | b = 105700 2 | c = 122700 3 | g = 1 4 | 5 | while g != 0: 6 | f = 1 7 | d = 2 8 | 9 | while g != 0: 10 | e = 2 11 | while g != 0: 12 | g = d 13 | g *= e 14 | g -= b 15 | if g == 0: 16 | f = 0 17 | e++ 18 | g = e 19 | g -= b 20 | d++ 21 | g = d 22 | g -= b 23 | 24 | if f == 0: 25 | h++ 26 | g = b - c 27 | b += 17 28 | -------------------------------------------------------------------------------- /2022/day06/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def solve(inp, count): 7 | for i in range(count, len(inp)): 8 | if len(set(inp[i - count : i])) == count: 9 | return i 10 | 11 | 12 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 13 | inp = f.read() 14 | print("Part 1:", solve(inp, 4)) 15 | print("Part 2:", solve(inp, 14)) 16 | -------------------------------------------------------------------------------- /2020/day22/input.txt: -------------------------------------------------------------------------------- 1 | Player 1: 2 | 5 3 | 20 4 | 28 5 | 30 6 | 48 7 | 7 8 | 41 9 | 24 10 | 29 11 | 8 12 | 37 13 | 32 14 | 16 15 | 17 16 | 34 17 | 27 18 | 46 19 | 43 20 | 14 21 | 49 22 | 35 23 | 11 24 | 6 25 | 38 26 | 1 27 | 28 | Player 2: 29 | 22 30 | 18 31 | 50 32 | 31 33 | 12 34 | 13 35 | 33 36 | 39 37 | 45 38 | 21 39 | 19 40 | 26 41 | 44 42 | 10 43 | 42 44 | 3 45 | 4 46 | 15 47 | 36 48 | 2 49 | 40 50 | 47 51 | 9 52 | 23 53 | 25 54 | -------------------------------------------------------------------------------- /2020/optimized/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aoc-2020-optimized" 3 | version = "0.1.0" 4 | authors = ["Benedikt Werner <1benediktwerner@gmail.com>"] 5 | edition = "2018" 6 | 7 | [profile.release] 8 | lto = true 9 | codegen-units = 1 10 | panic = "abort" 11 | 12 | [dependencies] 13 | anyhow = "1" 14 | clap = "4" 15 | rustc-hash = "2" 16 | # regex = "*" 17 | # libc = "0.2" 18 | 19 | # [build-dependencies] 20 | # cc = "1.0" 21 | -------------------------------------------------------------------------------- /2016/day11/input.txt: -------------------------------------------------------------------------------- 1 | The first floor contains a thulium generator, a thulium-compatible microchip, a plutonium generator, and a strontium generator. 2 | The second floor contains a plutonium-compatible microchip and a strontium-compatible microchip. 3 | The third floor contains a promethium generator, a promethium-compatible microchip, a ruthenium generator, and a ruthenium-compatible microchip. 4 | The fourth floor contains nothing relevant. 5 | -------------------------------------------------------------------------------- /2017/day23/input.txt: -------------------------------------------------------------------------------- 1 | set b 57 2 | set c b 3 | jnz a 2 4 | jnz 1 5 5 | mul b 100 6 | sub b -100000 7 | set c b 8 | sub c -17000 9 | set f 1 10 | set d 2 11 | set e 2 12 | set g d 13 | mul g e 14 | sub g b 15 | jnz g 2 16 | set f 0 17 | sub e -1 18 | set g e 19 | sub g b 20 | jnz g -8 21 | sub d -1 22 | set g d 23 | sub g b 24 | jnz g -13 25 | jnz f 2 26 | sub h -1 27 | set g b 28 | sub g c 29 | jnz g 2 30 | jnz 1 3 31 | sub b -17 32 | jnz 1 -23 33 | -------------------------------------------------------------------------------- /2019/day02/input.txt: -------------------------------------------------------------------------------- 1 | 1,0,0,3,1,1,2,3,1,3,4,3,1,5,0,3,2,10,1,19,1,5,19,23,1,23,5,27,2,27,10,31,1,5,31,35,2,35,6,39,1,6,39,43,2,13,43,47,2,9,47,51,1,6,51,55,1,55,9,59,2,6,59,63,1,5,63,67,2,67,13,71,1,9,71,75,1,75,9,79,2,79,10,83,1,6,83,87,1,5,87,91,1,6,91,95,1,95,13,99,1,10,99,103,2,6,103,107,1,107,5,111,1,111,13,115,1,115,13,119,1,13,119,123,2,123,13,127,1,127,6,131,1,131,9,135,1,5,135,139,2,139,6,143,2,6,143,147,1,5,147,151,1,151,2,155,1,9,155,0,99,2,14,0,0 2 | -------------------------------------------------------------------------------- /2015/day08/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import ast 5 | import json 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | code, decoded, encoded = 0, 0, 0 10 | for line in f.read().splitlines(): 11 | code += len(line) 12 | decoded += len(ast.literal_eval(line)) 13 | encoded += len(json.dumps(line)) 14 | print("Part 1:", code - decoded) 15 | print("Part 2:", encoded - code) 16 | -------------------------------------------------------------------------------- /2017/day08/sol.py: -------------------------------------------------------------------------------- 1 | from collections import * 2 | 3 | M = {"inc": 1, "dec": -1} 4 | 5 | with open("input.txt", "r") as f: 6 | max_val = 0 7 | memory = defaultdict(lambda: 0) 8 | for line in f: 9 | s = line.strip().split() 10 | if eval(str(memory[s[4]]) + s[5] + s[6]): 11 | memory[s[0]] += M[s[1]] * int(s[2]) 12 | max_val = max(memory[s[0]], max_val) 13 | print(max(memory.values())) 14 | print(max_val) 15 | -------------------------------------------------------------------------------- /2017/day23/input_opt.txt: -------------------------------------------------------------------------------- 1 | set b 105700 2 | set c 122700 3 | set f 1 <-- label6 4 | set d 2 5 | set e 2 <-- label3 6 | set g d <-- label2 7 | mul g e 8 | sub g b 9 | jnz g 2 label1 10 | set f 0 11 | sub e -1 <-- label1 12 | set g e 13 | sub g b 14 | jnz g -8 label2 15 | sub d -1 16 | set g d 17 | sub g b 18 | jnz g -13 label3 19 | jnz f 2 label4 20 | sub h -1 21 | set g b <-- label4 22 | sub g c 23 | jnz g 2 label5 24 | halt 25 | sub b -17 <-- label5 26 | jnz 1 -23 label6 27 | -------------------------------------------------------------------------------- /2020/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import itertools 5 | import math 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | numbers = [int(x) for x in f] 10 | 11 | def solve(count): 12 | for ns in itertools.combinations(numbers, count): 13 | if sum(ns) == 2020: 14 | return math.prod(ns) 15 | 16 | print("Part 1:", solve(2)) 17 | print("Part 2:", solve(3)) 18 | -------------------------------------------------------------------------------- /2021/optimized/bare/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aoc-2021-optimized-bare" 7 | version = "0.1.0" 8 | dependencies = [ 9 | "libc", 10 | ] 11 | 12 | [[package]] 13 | name = "libc" 14 | version = "0.2.108" 15 | source = "registry+https://github.com/rust-lang/crates.io-index" 16 | checksum = "8521a1b57e76b1ec69af7599e75e38e7b7fad6610f037db8c79b127201b5d119" 17 | -------------------------------------------------------------------------------- /2017/day17/sol.py: -------------------------------------------------------------------------------- 1 | INPUT = 370 2 | 3 | curr_pos = 0 4 | pos_zero = 0 5 | length = 1 6 | val_after_zero = None 7 | 8 | for i in range(1, 50000001): 9 | curr_pos = ((curr_pos + INPUT) % length) + 1 10 | length += 1 11 | if curr_pos < pos_zero: 12 | pos_zero += 1 13 | if curr_pos == (pos_zero + 1) % length: 14 | val_after_zero = i 15 | 16 | ##print(output[curr_pos-5:curr_pos+5]) 17 | ##print(output[curr_pos+2]) 18 | print(val_after_zero) 19 | -------------------------------------------------------------------------------- /2025/day02/input.txt: -------------------------------------------------------------------------------- 1 | 67562556-67743658,62064792-62301480,4394592-4512674,3308-4582,69552998-69828126,9123-12332,1095-1358,23-48,294-400,3511416-3689352,1007333-1150296,2929221721-2929361280,309711-443410,2131524-2335082,81867-97148,9574291560-9574498524,648635477-648670391,1-18,5735-8423,58-72,538-812,698652479-698760276,727833-843820,15609927-15646018,1491-1766,53435-76187,196475-300384,852101-903928,73-97,1894-2622,58406664-58466933,6767640219-6767697605,523453-569572,7979723815-7979848548,149-216 2 | -------------------------------------------------------------------------------- /2019/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | total1 = 0 8 | total2 = 0 9 | 10 | for line in f: 11 | line = line.strip() 12 | 13 | fuel = int(line) // 3 - 2 14 | total1 += fuel 15 | 16 | while fuel > 0: 17 | total2 += fuel 18 | fuel = fuel // 3 - 2 19 | 20 | print("Part 1:", total1) 21 | print("Part 2:", total2) 22 | -------------------------------------------------------------------------------- /2017/day13/input.txt: -------------------------------------------------------------------------------- 1 | 0: 4 2 | 1: 2 3 | 2: 3 4 | 4: 5 5 | 6: 6 6 | 8: 4 7 | 10: 8 8 | 12: 6 9 | 14: 6 10 | 16: 8 11 | 18: 8 12 | 20: 6 13 | 22: 8 14 | 24: 9 15 | 26: 8 16 | 28: 8 17 | 30: 12 18 | 32: 12 19 | 34: 10 20 | 36: 12 21 | 38: 12 22 | 40: 10 23 | 42: 12 24 | 44: 12 25 | 46: 12 26 | 48: 12 27 | 50: 12 28 | 52: 14 29 | 54: 14 30 | 56: 12 31 | 58: 14 32 | 60: 14 33 | 62: 14 34 | 64: 17 35 | 66: 14 36 | 70: 14 37 | 72: 14 38 | 74: 14 39 | 76: 14 40 | 78: 18 41 | 82: 14 42 | 88: 18 43 | 90: 14 44 | -------------------------------------------------------------------------------- /2016/day19/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from math import log2 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | elves = int(f.read()) 9 | pow2 = 2 ** int(log2(elves)) 10 | print("Part 1:", (elves - pow2) * 2 + 1) 11 | 12 | prev = 0 13 | for i in range(2, elves + 1): 14 | remove = i // 2 15 | prev += 1 16 | if prev >= remove: 17 | prev += 1 18 | prev %= i 19 | 20 | print("Part 2:", prev + 1) 21 | -------------------------------------------------------------------------------- /2017/day13/sol.py: -------------------------------------------------------------------------------- 1 | def severity(r, start=0): 2 | severity = 0 3 | for i in r: 4 | if (start + i) % (r[i]*2 - 2) == 0: 5 | severity += (start + i)*r[i] 6 | return severity 7 | 8 | with open("input.txt", "r") as f: 9 | r = {} 10 | for line in f: 11 | i, ra = line.strip().split(": ") 12 | r[int(i)] = int(ra) 13 | print(severity(r)) 14 | for i in range(10000000): 15 | if severity(r, i) == 0: 16 | print(i) 17 | break 18 | -------------------------------------------------------------------------------- /2017/day23/optimized.py: -------------------------------------------------------------------------------- 1 | b = 105700 2 | c = 122700 3 | g = 1 4 | h = 0 5 | 6 | while g != 0: 7 | f = False 8 | d = 2 9 | 10 | while g != 0: 11 | # e = 2 12 | if b % d == 0: 13 | f = True 14 | break 15 | # while g != 0: 16 | # if d * e == b: 17 | # f = True 18 | # e += 1 19 | # g = e - b 20 | d += 1 21 | g = d - b 22 | 23 | if f: 24 | h += 1 25 | g = b - c 26 | b += 17 27 | print(h) 28 | -------------------------------------------------------------------------------- /2015/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | floor = 0 8 | first_basement = None 9 | 10 | for i, c in enumerate(f.readline().strip()): 11 | if c == "(": 12 | floor += 1 13 | else: 14 | floor -= 1 15 | if floor < 0 and first_basement is None: 16 | first_basement = i + 1 17 | 18 | print("Part 1:", floor) 19 | print("Part 2:", first_basement) 20 | -------------------------------------------------------------------------------- /2015/day23/input.txt: -------------------------------------------------------------------------------- 1 | jio a, +18 2 | inc a 3 | tpl a 4 | inc a 5 | tpl a 6 | tpl a 7 | tpl a 8 | inc a 9 | tpl a 10 | inc a 11 | tpl a 12 | inc a 13 | inc a 14 | tpl a 15 | tpl a 16 | tpl a 17 | inc a 18 | jmp +22 19 | tpl a 20 | inc a 21 | tpl a 22 | inc a 23 | inc a 24 | tpl a 25 | inc a 26 | tpl a 27 | inc a 28 | inc a 29 | tpl a 30 | tpl a 31 | inc a 32 | inc a 33 | tpl a 34 | inc a 35 | inc a 36 | tpl a 37 | inc a 38 | inc a 39 | tpl a 40 | jio a, +8 41 | inc b 42 | jie a, +4 43 | tpl a 44 | inc a 45 | jmp +2 46 | hlf a 47 | jmp -7 48 | -------------------------------------------------------------------------------- /2015/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | 6 | 7 | def ints(string): 8 | return map(int, re.findall(r"-?[0-9]+", string)) 9 | 10 | 11 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 12 | p1, p2 = 0, 0 13 | for l, w, h in map(ints, f.read().splitlines()): 14 | sides = (l * w, w * h, h * l) 15 | p1 += 2 * sum(sides) + min(sides) 16 | p2 += 2 * min(l + w, w + h, h + l) + l * w * h 17 | 18 | print("Part 1:", p1) 19 | print("Part 2:", p2) 20 | -------------------------------------------------------------------------------- /2022/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | part1, part2 = 0, 0 8 | for line in f.read().splitlines(): 9 | (a1, a2), (b1, b2) = map(lambda e: map(int, e.split("-")), line.split(",")) 10 | if a1 <= b1 and b2 <= a2 or b1 <= a1 and a2 <= b2: 11 | part1 += 1 12 | if a1 <= b1 <= a2 or b1 <= a1 <= b2: 13 | part2 += 1 14 | 15 | print("Part 1:", part1) 16 | print("Part 2:", part2) 17 | -------------------------------------------------------------------------------- /2018/day21/input.txt: -------------------------------------------------------------------------------- 1 | #ip 5 2 | seti 123 0 3 3 | bani 3 456 3 4 | eqri 3 72 3 5 | addr 3 5 5 6 | seti 0 0 5 7 | seti 0 9 3 8 | bori 3 65536 1 9 | seti 14906355 8 3 10 | bani 1 255 4 11 | addr 3 4 3 12 | bani 3 16777215 3 13 | muli 3 65899 3 14 | bani 3 16777215 3 15 | gtir 256 1 4 16 | addr 4 5 5 17 | addi 5 1 5 18 | seti 27 8 5 19 | seti 0 4 4 20 | addi 4 1 2 21 | muli 2 256 2 22 | gtrr 2 1 2 23 | addr 2 5 5 24 | addi 5 1 5 25 | seti 25 1 5 26 | addi 4 1 4 27 | seti 17 2 5 28 | setr 4 9 1 29 | seti 7 0 5 30 | eqrr 3 0 4 31 | addr 4 5 5 32 | seti 5 3 5 33 | -------------------------------------------------------------------------------- /2017/day18/input.txt: -------------------------------------------------------------------------------- 1 | set i 31 2 | set a 1 3 | mul p 17 4 | jgz p p 5 | mul a 2 6 | add i -1 7 | jgz i -2 8 | add a -1 9 | set i 127 10 | set p 735 11 | mul p 8505 12 | mod p a 13 | mul p 129749 14 | add p 12345 15 | mod p a 16 | set b p 17 | mod b 10000 18 | snd b 19 | add i -1 20 | jgz i -9 21 | jgz a 3 22 | rcv b 23 | jgz b -1 24 | set f 0 25 | set i 126 26 | rcv a 27 | rcv b 28 | set p a 29 | mul p -1 30 | add p b 31 | jgz p 4 32 | snd a 33 | set a b 34 | jgz 1 3 35 | snd b 36 | set f 1 37 | add i -1 38 | jgz i -11 39 | snd a 40 | jgz f -16 41 | jgz a -19 42 | -------------------------------------------------------------------------------- /2018/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | def main(): 5 | values = [] 6 | with open("input.txt") as f: 7 | for line in f: 8 | values.append(int(line)) 9 | 10 | print("Part 1:", sum(values)) 11 | 12 | seen = set([0]) 13 | value = 0 14 | while True: 15 | for val in values: 16 | value += val 17 | if value in seen: 18 | print("Part 2:", value) 19 | return 20 | seen.add(value) 21 | 22 | 23 | if __name__ == "__main__": 24 | main() 25 | -------------------------------------------------------------------------------- /2017/day05/sol.py: -------------------------------------------------------------------------------- 1 | def find_exit(maze): 2 | index = 0 3 | counter = 0 4 | length = len(maze) 5 | while True: 6 | counter += 1 7 | x = maze[index] 8 | if index + x < 0 or index + x >= length: 9 | return counter 10 | maze[index] += 1 if maze[index] < 3 else -1 11 | index += x 12 | 13 | print(find_exit([0, 3, 0, 1, -3])) 14 | 15 | with open("input.txt", "r") as f: 16 | maze = [] 17 | for line in f: 18 | maze.append(int(line.strip())) 19 | print(find_exit(maze)) 20 | -------------------------------------------------------------------------------- /2020/optimized/expected.txt: -------------------------------------------------------------------------------- 1 | 1015476 200878544 2 | 439 584 3 | 167 736527114 4 | 192 101 5 | 938 696 6 | 7027 3579 7 | 289 30055 8 | 1134 1205 9 | 556543474 76096372 10 | 1755 4049565169664 11 | 2273 2064 12 | 319 50157 13 | 6559 626670513163231 14 | 0 0 15 | 0 0 16 | 0 0 17 | 0 0 18 | 0 0 19 | 0 0 20 | 0 0 21 | 0 0 22 | 0 0 23 | 89372645 21273394210 24 | 0 0 25 | 0 0 26 | -------------------------------------------------------------------------------- /2015/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from hashlib import md5 5 | from itertools import count 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | result = f.read().strip().encode() 10 | for i in count(1): 11 | if md5(result + str(i).encode()).hexdigest()[:5] == "00000": 12 | print("Part 1:", i) 13 | break 14 | for i in count(i): 15 | if md5(result + str(i).encode()).hexdigest()[:6] == "000000": 16 | print("Part 2:", i) 17 | break 18 | -------------------------------------------------------------------------------- /2017/day24/input.txt: -------------------------------------------------------------------------------- 1 | 0/36 2 | 0/50 3 | 10/25 4 | 11/6 5 | 13/2 6 | 13/3 7 | 13/43 8 | 14/14 9 | 14/22 10 | 14/40 11 | 16/40 12 | 16/46 13 | 17/0 14 | 17/50 15 | 19/34 16 | 19/36 17 | 19/43 18 | 2/6 19 | 22/26 20 | 22/33 21 | 22/44 22 | 23/6 23 | 24/4 24 | 24/7 25 | 27/19 26 | 27/21 27 | 29/0 28 | 3/10 29 | 31/46 30 | 32/11 31 | 32/32 32 | 32/49 33 | 33/36 34 | 33/44 35 | 36/21 36 | 37/28 37 | 37/37 38 | 37/45 39 | 38/26 40 | 4/16 41 | 41/3 42 | 42/26 43 | 43/43 44 | 47/47 45 | 49/10 46 | 49/2 47 | 49/21 48 | 49/25 49 | 49/49 50 | 50/39 51 | 50/41 52 | 6/39 53 | 6/9 54 | 8/40 55 | 9/39 56 | -------------------------------------------------------------------------------- /2020/day05/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | ids = set() 8 | 9 | for line in f: 10 | row = int(line[:7].replace("F", "0").replace("B", "1"), 2) 11 | col = int(line[7:].replace("L", "0").replace("R", "1"), 2) 12 | ids.add(row * 8 + col) 13 | 14 | print("Part 1:", max(ids)) 15 | 16 | for i in range(1, 0b1_0000000_000): 17 | if i not in ids and i - 1 in ids and i + 1 in ids: 18 | print("Part 2:", i) 19 | break 20 | -------------------------------------------------------------------------------- /2016/day18/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | rows = [f.read().strip()] 8 | 9 | for i in range(399_999): 10 | last = rows[-1] 11 | new = last[1] 12 | for i in range(1, len(last) - 1): 13 | new += "^" if last[i-1] != last[i+1] else "." 14 | new += last[-2] 15 | rows.append(new) 16 | 17 | print("Part 1:", sum(c == "." for row in rows[:40] for c in row)) 18 | print("Part 2:", sum(c == "." for row in rows for c in row)) 19 | -------------------------------------------------------------------------------- /2018/day19/input.txt: -------------------------------------------------------------------------------- 1 | #ip 3 2 | addi 3 16 3 3 | seti 1 9 5 4 | seti 1 1 4 5 | mulr 5 4 2 6 | eqrr 2 1 2 7 | addr 2 3 3 8 | addi 3 1 3 9 | addr 5 0 0 10 | addi 4 1 4 11 | gtrr 4 1 2 12 | addr 3 2 3 13 | seti 2 3 3 14 | addi 5 1 5 15 | gtrr 5 1 2 16 | addr 2 3 3 17 | seti 1 4 3 18 | mulr 3 3 3 19 | addi 1 2 1 20 | mulr 1 1 1 21 | mulr 3 1 1 22 | muli 1 11 1 23 | addi 2 2 2 24 | mulr 2 3 2 25 | addi 2 20 2 26 | addr 1 2 1 27 | addr 3 0 3 28 | seti 0 4 3 29 | setr 3 9 2 30 | mulr 2 3 2 31 | addr 3 2 2 32 | mulr 3 2 2 33 | muli 2 14 2 34 | mulr 2 3 2 35 | addr 1 2 1 36 | seti 0 6 0 37 | seti 0 0 3 38 | -------------------------------------------------------------------------------- /2020/day06/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import string 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | part1 = part2 = 0 8 | 9 | for group in f.read().split("\n\n"): 10 | answers1 = set() 11 | answers2 = set(string.ascii_lowercase) 12 | 13 | for person in group.split(): 14 | answers1.update(person) 15 | answers2 &= set(person) 16 | 17 | part1 += len(answers1) 18 | part2 += len(answers2) 19 | 20 | print("Part 1:", part1) 21 | print("Part 2:", part2) 22 | -------------------------------------------------------------------------------- /2020/day15/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | age = {} 8 | nums = list(map(int, f.read().strip().split(","))) 9 | for i, n in enumerate(nums[:-1]): 10 | age[n] = i 11 | 12 | last = nums[-1] 13 | for t in range(len(nums)-1, 2019): 14 | age[last], last = t, t - age.get(last, t) 15 | 16 | print("Part 1:", last) 17 | 18 | for t in range(2019, 30000000-1): 19 | age[last], last = t, t - age.get(last, t) 20 | 21 | print("Part 2:", last) 22 | -------------------------------------------------------------------------------- /2025/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | from os import path 5 | 6 | 7 | def find_max(bank: str, n: int) -> str: 8 | if n == 1: 9 | return max(bank) 10 | a = max(bank[: -n + 1]) 11 | return a + find_max(bank[bank.find(a) + 1 :], n - 1) 12 | 13 | 14 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 15 | part1 = part2 = 0 16 | 17 | for bank in file.read().splitlines(): 18 | part1 += int(find_max(bank, 2)) 19 | part2 += int(find_max(bank, 12)) 20 | 21 | print("Part 1:", part1) 22 | print("Part 2:", part2) 23 | -------------------------------------------------------------------------------- /2021/day06/input.txt: -------------------------------------------------------------------------------- 1 | 1,2,1,3,2,1,1,5,1,4,1,2,1,4,3,3,5,1,1,3,5,3,4,5,5,4,3,1,1,4,3,1,5,2,5,2,4,1,1,1,1,1,1,1,4,1,4,4,4,1,4,4,1,4,2,1,1,1,1,3,5,4,3,3,5,4,1,3,1,1,2,1,1,1,4,1,2,5,2,3,1,1,1,2,1,5,1,1,1,4,4,4,1,5,1,2,3,2,2,2,1,1,4,3,1,4,4,2,1,1,5,1,1,1,3,1,2,1,1,1,1,4,5,5,2,3,4,2,1,1,1,2,1,1,5,5,3,5,4,3,1,3,1,1,5,1,1,4,2,1,3,1,1,4,3,1,5,1,1,3,4,2,2,1,1,2,1,1,2,1,3,2,3,1,4,5,1,1,4,3,3,1,1,2,2,1,5,2,1,3,4,5,4,5,5,4,3,1,5,1,1,1,4,4,3,2,5,2,1,4,3,5,1,3,5,1,3,3,1,1,1,2,5,3,1,1,3,1,1,1,2,1,5,1,5,1,3,1,1,5,4,3,3,2,2,1,1,3,4,1,1,1,1,4,1,3,1,5,1,1,3,1,1,1,1,2,2,4,4,4,1,2,5,5,2,2,4,1,1,4,2,1,1,5,1,5,3,5,4,5,3,1,1,1,2,3,1,2,1,1 2 | -------------------------------------------------------------------------------- /2016/day01/input.txt: -------------------------------------------------------------------------------- 1 | L2, L3, L3, L4, R1, R2, L3, R3, R3, L1, L3, R2, R3, L3, R4, R3, R3, L1, L4, R4, L2, R5, R1, L5, R1, R3, L5, R2, L2, R2, R1, L1, L3, L3, R4, R5, R4, L1, L189, L2, R2, L5, R5, R45, L3, R4, R77, L1, R1, R194, R2, L5, L3, L2, L1, R5, L3, L3, L5, L5, L5, R2, L1, L2, L3, R2, R5, R4, L2, R3, R5, L2, L2, R3, L3, L2, L1, L3, R5, R4, R3, R2, L1, R2, L5, R4, L5, L4, R4, L2, R5, L3, L2, R4, L1, L2, R2, R3, L2, L5, R1, R1, R3, R4, R1, R2, R4, R5, L3, L5, L3, L3, R5, R4, R1, L3, R1, L3, R3, R3, R3, L1, R3, R4, L5, L3, L1, L5, L4, R4, R1, L4, R3, R3, R5, R4, R3, R3, L1, L2, R1, L4, L4, L3, L4, L3, L5, R2, R4, L2 2 | -------------------------------------------------------------------------------- /2017/day04/sol.py: -------------------------------------------------------------------------------- 1 | def isvalid1(pwd): 2 | s = set() 3 | for word in pwd.split(" "): 4 | if word in s: 5 | return False 6 | s.add(word) 7 | return True 8 | 9 | def isvalid2(pwd): 10 | s = set() 11 | for word in pwd.split(" "): 12 | w = "".join(sorted(word)) 13 | if w in s: 14 | return False 15 | s.add(w) 16 | return True 17 | 18 | 19 | with open("input.txt", "r") as f: 20 | result = 0 21 | for line in f: 22 | if isvalid2(line.strip()): 23 | result += 1 24 | print(result) 25 | -------------------------------------------------------------------------------- /2022/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def score(mine, theirs): 7 | return 3 * ((mine - theirs + 1) % 3) + mine + 1 8 | 9 | 10 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 11 | score1, score2 = 0, 0 12 | 13 | for line in f.read().splitlines(): 14 | theirs, mine = map(ord, line.split()) 15 | theirs -= ord("A") 16 | mine -= ord("X") 17 | 18 | score1 += score(mine, theirs) 19 | score2 += score((theirs + mine - 1) % 3, theirs) 20 | 21 | print("Part 1:", score1) 22 | print("Part 2:", score2) 23 | -------------------------------------------------------------------------------- /2015/day09/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | import itertools 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | G = defaultdict(dict) 9 | for line in f.read().splitlines(): 10 | start, _, end, _, cost = line.split() 11 | G[start][end] = int(cost) 12 | G[end][start] = int(cost) 13 | 14 | def dists(): 15 | for perm in itertools.permutations(G): 16 | yield sum(G[a][b] for a, b in zip(perm, perm[1:])) 17 | 18 | print("Part 1:", min(dists())) 19 | print("Part 2:", max(dists())) 20 | -------------------------------------------------------------------------------- /2017/day02/sol.py: -------------------------------------------------------------------------------- 1 | with open("input.txt") as f: 2 | result1 = 0 3 | result2 = 0 4 | for line in f: 5 | numbers = list(map(int, line.strip().split("\t"))) 6 | result1 += max(numbers) - min(numbers) 7 | 8 | found = False 9 | for i in numbers: 10 | for j in numbers: 11 | if i == j: 12 | continue 13 | if i % j == 0: 14 | found = True 15 | result2 += i // j 16 | break 17 | if found: 18 | break 19 | print(result1, result2) 20 | -------------------------------------------------------------------------------- /2020/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import Counter 5 | import re 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | part1 = part2 = 0 10 | 11 | for line in f: 12 | low, high, c, word = re.findall(r"(\d+)-(\d+) (\w): (\w+)", line.strip())[0] 13 | low, high = int(low), int(high) 14 | 15 | if low <= Counter(word)[c] <= high: 16 | part1 += 1 17 | 18 | if (word[low - 1] == c) ^ (word[high - 1] == c): 19 | part2 += 1 20 | 21 | print("Part 1:", part1) 22 | print("Part 2:", part2) 23 | -------------------------------------------------------------------------------- /2024/day23/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | import networkx as nx 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 9 | G = nx.Graph() 10 | for line in file.read().splitlines(): 11 | G.add_edge(*line.split("-")) 12 | 13 | print( 14 | "Part 1:", 15 | sum( 16 | len(clique) == 3 and any(n.startswith("t") for n in clique) 17 | for clique in nx.enumerate_all_cliques(G) 18 | ), 19 | ) 20 | 21 | max_clique = max(nx.find_cliques(G), key=len) 22 | print("Part 2:", ",".join(sorted(max_clique))) 23 | -------------------------------------------------------------------------------- /2018/day12/input.txt: -------------------------------------------------------------------------------- 1 | initial state: ##.##.#.#...#......#..#.###..##...##.#####..#..###.########.##.....#...#...##....##.#...#.###...#.## 2 | 3 | .###. => # 4 | ###.# => # 5 | #..#. => # 6 | .#..# => # 7 | ...## => # 8 | .#### => . 9 | .#.## => # 10 | #.... => . 11 | #..## => . 12 | ..#.. => . 13 | #.##. => # 14 | ##.#. => . 15 | ....# => . 16 | #.#.. => # 17 | .#... => # 18 | .##.# => # 19 | ..### => . 20 | .##.. => . 21 | ##... => # 22 | ###.. => # 23 | ##..# => # 24 | ...#. => . 25 | ..#.# => # 26 | ..##. => . 27 | #...# => . 28 | .#.#. => # 29 | ##### => . 30 | #.#.# => . 31 | ####. => # 32 | #.### => . 33 | ..... => . 34 | ##.## => . 35 | -------------------------------------------------------------------------------- /2015/day25/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import itertools 5 | import re 6 | 7 | 8 | def ints(string): 9 | return list(map(int, re.findall(r"-?[0-9]+", string))) 10 | 11 | 12 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 13 | row, column = ints(f.read()) 14 | val = 20151125 15 | for diag in itertools.count(1): 16 | r, c = diag, 1 17 | for _ in range(diag): 18 | val = (val * 252533) % 33554393 19 | r -= 1 20 | c += 1 21 | if r == row and c == column: 22 | print("Part 1:", val) 23 | exit() 24 | -------------------------------------------------------------------------------- /2019/day16/input.txt: -------------------------------------------------------------------------------- 1 | 59731816011884092945351508129673371014862103878684944826017645844741545300230138932831133873839512146713127268759974246245502075014905070039532876129205215417851534077861438833829150700128859789264910166202535524896960863759734991379392200570075995540154404564759515739872348617947354357737896622983395480822393561314056840468397927687908512181180566958267371679145705350771757054349846320639601111983284494477902984330803048219450650034662420834263425046219982608792077128250835515865313986075722145069152768623913680721193045475863879571787112159970381407518157406924221437152946039000886837781446203456224983154446561285113664381711600293030463013 2 | -------------------------------------------------------------------------------- /2015/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | x, y = 0, 0 8 | xx, yy = [0, 0], [0, 0] 9 | visited1 = set([(x, y)]) 10 | visited2 = set([(x, y)]) 11 | for i, dir in enumerate(f.read().strip()): 12 | dx, dy = {"^": (0, -1), "v": (0, 1), ">": (1, 0), "<": (-1, 0)}[dir] 13 | x += dx 14 | y += dy 15 | xx[i % 2] += dx 16 | yy[i % 2] += dy 17 | visited1.add((x, y)) 18 | visited2.add((xx[i % 2], yy[i % 2])) 19 | 20 | print("Part 1:", len(visited1)) 21 | print("Part 2:", len(visited2)) 22 | -------------------------------------------------------------------------------- /2015/day12/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import json 5 | 6 | 7 | def nums(j): 8 | if isinstance(j, int): 9 | return j 10 | if isinstance(j, list): 11 | return sum(map(nums, j)) 12 | if isinstance(j, str): 13 | return 0 14 | assert isinstance(j, dict), type(j) 15 | if p2 and any(v == "red" for v in j.values()): 16 | return 0 17 | return sum(map(nums, j.values())) 18 | 19 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 20 | inp = json.load(f) 21 | p2 = False 22 | print("Part 1:", nums(inp)) 23 | p2 = True 24 | print("Part 2:", nums(inp)) 25 | -------------------------------------------------------------------------------- /2015/day10/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | curr = f.read().strip() 8 | for i in range(50): 9 | new = "" 10 | count = 0 11 | last = None 12 | for c in curr: 13 | if c != last: 14 | if last is not None: 15 | new += f"{count}{last}" 16 | last = c 17 | count = 0 18 | count += 1 19 | curr = new + f"{count}{last}" 20 | 21 | if i == 39: 22 | print("Part 1:", len(curr)) 23 | 24 | print("Part 2:", len(curr)) 25 | -------------------------------------------------------------------------------- /2021/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | depth, x = 0, 0 8 | depth2, x2, aim = 0, 0, 0 9 | 10 | for line in f.read().splitlines(): 11 | d, a = line.split() 12 | a = int(a) 13 | if d == "forward": 14 | x += a 15 | x2 += a 16 | depth2 += aim * a 17 | elif d == "up": 18 | depth -= a 19 | aim -= a 20 | elif d == "down": 21 | depth += a 22 | aim += a 23 | 24 | print("Part 1:", depth * x) 25 | print("Part 2:", depth2 * x2) 26 | -------------------------------------------------------------------------------- /2024/day24/notes.md: -------------------------------------------------------------------------------- 1 | # Mistake 1 2 | 3 | vmv = carry_06 XOR direct_bit_07 4 | 5 | vmv <-> z07 6 | 7 | # Mistake 2 8 | 9 | kfm = direct_bit_20 XOR carry_19 10 | 11 | kfm <-> z20 12 | 13 | # Mistake 3 14 | 15 | hnv = carry_27 XOR direct_bit_28 16 | 17 | hnv <-> z28 18 | 19 | # Mistake 4 20 | 21 | chh = direct_carry_35 AND carry_34 22 | z35 = direct_carry_35 XOR carry_34 23 | 24 | direct_carry_35 should be direct_bit_35 25 | 26 | direct_carry_35 == tqr 27 | direct_bit_35 == hth 28 | 29 | tqr <-> hth 30 | 31 | # Answer 32 | 33 | ```python 34 | >>> ",".join(sorted(["vmv", "z07","kfm", "z20","hnv", "z28","tqr", "hth"])) 35 | 'hnv,hth,kfm,tqr,vmv,z07,z20,z28' 36 | ``` 37 | -------------------------------------------------------------------------------- /2018/day06/input.txt: -------------------------------------------------------------------------------- 1 | 135, 127 2 | 251, 77 3 | 136, 244 4 | 123, 169 5 | 253, 257 6 | 359, 309 7 | 100, 247 8 | 191, 323 9 | 129, 323 10 | 76, 284 11 | 69, 56 12 | 229, 266 13 | 74, 216 14 | 236, 130 15 | 152, 126 16 | 174, 319 17 | 315, 105 18 | 329, 146 19 | 288, 51 20 | 184, 344 21 | 173, 69 22 | 293, 80 23 | 230, 270 24 | 279, 84 25 | 107, 163 26 | 130, 176 27 | 347, 114 28 | 133, 331 29 | 237, 300 30 | 291, 283 31 | 246, 297 32 | 60, 359 33 | 312, 278 34 | 242, 76 35 | 81, 356 36 | 204, 291 37 | 187, 335 38 | 176, 98 39 | 103, 274 40 | 357, 144 41 | 314, 118 42 | 67, 196 43 | 156, 265 44 | 254, 357 45 | 218, 271 46 | 118, 94 47 | 300, 189 48 | 290, 356 49 | 354, 91 50 | 209, 334 51 | -------------------------------------------------------------------------------- /2022/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import string 5 | 6 | prio = string.ascii_lowercase + string.ascii_uppercase 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | lines = f.read().splitlines() 10 | 11 | part1 = 0 12 | for line in lines: 13 | half = len(line) // 2 14 | a, b = line[:half], line[half:] 15 | part1 += sum(prio.index(c) + 1 for c in set(a) & set(b)) 16 | print("Part 1:", part1) 17 | 18 | part2 = 0 19 | for a, b, c in zip(lines[::3], lines[1::3], lines[2::3]): 20 | part2 += sum(prio.index(c) + 1 for c in set(a) & set(b) & set(c)) 21 | print("Part 2:", part2) 22 | -------------------------------------------------------------------------------- /2015/day14/input.txt: -------------------------------------------------------------------------------- 1 | Vixen can fly 8 km/s for 8 seconds, but then must rest for 53 seconds. 2 | Blitzen can fly 13 km/s for 4 seconds, but then must rest for 49 seconds. 3 | Rudolph can fly 20 km/s for 7 seconds, but then must rest for 132 seconds. 4 | Cupid can fly 12 km/s for 4 seconds, but then must rest for 43 seconds. 5 | Donner can fly 9 km/s for 5 seconds, but then must rest for 38 seconds. 6 | Dasher can fly 10 km/s for 4 seconds, but then must rest for 37 seconds. 7 | Comet can fly 3 km/s for 37 seconds, but then must rest for 76 seconds. 8 | Prancer can fly 9 km/s for 12 seconds, but then must rest for 97 seconds. 9 | Dancer can fly 37 km/s for 1 seconds, but then must rest for 36 seconds. 10 | -------------------------------------------------------------------------------- /2017/day01/sol.py: -------------------------------------------------------------------------------- 1 | def solve(line): 2 | result1 = 0 3 | result2 = 0 4 | length = len(line) 5 | half = length // 2 6 | for i in range(length): 7 | if line[i] == line[(i+1)%length]): 8 | result1 += int(line[i]) 9 | if line[i] == line[(i+half)%length]: 10 | result2 += int(line[i]) 11 | return result1, result2 12 | 13 | with open("input.txt", "r") as f: 14 | input_text = "" 15 | for line in f: 16 | input_text += line.strip() 17 | print(solve(input_text)) 18 | 19 | print("Test:") 20 | print(solve("1212")) 21 | print(solve("1221")) 22 | print(solve("123425")) 23 | print(solve("123123")) 24 | print(solve("12131415")) 25 | -------------------------------------------------------------------------------- /2021/day06/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import Counter 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | count = Counter(map(int, f.read().strip().split(","))) 9 | 10 | for i in range(256): 11 | if i == 80: 12 | print("Part 1:", sum(count.values())) 13 | 14 | new_count = Counter() 15 | 16 | for fish, c in count.items(): 17 | if fish == 0: 18 | new_count[6] += c 19 | new_count[8] += c 20 | else: 21 | new_count[fish-1] += c 22 | 23 | count = new_count 24 | 25 | print("Part 2:", sum(count.values())) 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Advent of Code 2 | 3 | My solutions for [Advent of Code](http://adventofcode.com) written mostly in Python 3. 4 | 5 | There also are some very optimized solutions written in Rust for [2020](2020/optimized) and [2021](2021/optimized). 6 | 7 | And I've written some [fun stuff](https://github.com/benediktwerner/intcode) for intcode from 2019, including a compiler from a simple high-level language to intcode. 8 | 9 | ## License 10 | 11 | All the code in this repository is in the public domain. Or if you prefer, you may also use it under the [MIT license](LICENSE-MIT) or [CC0 license](LICENSE-CC0). 12 | 13 | Note though that some parts of this code use external libraries which have their own licenses. 14 | -------------------------------------------------------------------------------- /2019/day10/input.txt: -------------------------------------------------------------------------------- 1 | #..#.#.###.#...##.##.... 2 | .#.#####.#.#.##.....##.# 3 | ##..#.###..###..#####..# 4 | ####.#.#..#....#..##.##. 5 | .#######.#####...#.###.. 6 | .##...#.#.###..###.#.#.# 7 | .######.....#.###..#.... 8 | .##..##.#..#####...###.# 9 | #######.#..#####..#.#.#. 10 | .###.###...##.##....##.# 11 | ##.###.##.#.#..####..... 12 | #.#..##..#..#.#..#####.# 13 | #####.##.#.#.#.#.#.#..## 14 | #...##.##.###.##.#.###.. 15 | ####.##.#.#.####.#####.# 16 | .#..##...##..##..#.#.##. 17 | ###...####.###.#.###.#.# 18 | ..####.#####..#####.#.## 19 | ..###..###..#..##...#.#. 20 | ##.####...##....####.##. 21 | ####..#..##.#.#....#..#. 22 | .#..........#..#.#.####. 23 | ###..###.###.#.#.#....## 24 | ########.#######.#.##.## 25 | -------------------------------------------------------------------------------- /2015/day05/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | 6 | 7 | def nice1(s): 8 | for p in ("ab", "cd", "pq", "xy"): 9 | if p in s: 10 | return False 11 | if not re.findall(r"(.)\1", s): 12 | return False 13 | return len(re.findall("[aeiou]", s)) >= 3 14 | 15 | 16 | def nice2(s): 17 | if not re.findall(r"(.).\1", s): 18 | return False 19 | if not re.findall(r"(.)(.).*\1\2", s): 20 | return False 21 | return True 22 | 23 | 24 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 25 | lines = f.read().splitlines() 26 | print("Part 1:", sum(map(nice1, lines))) 27 | print("Part 2:", sum(map(nice2, lines))) 28 | -------------------------------------------------------------------------------- /2024/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import Counter 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | lefts = [] 9 | rights = [] 10 | for line in file.read().splitlines(): 11 | left, right = map(int, line.split()) 12 | lefts.append(left) 13 | rights.append(right) 14 | lefts.sort() 15 | rights.sort() 16 | 17 | part1 = 0 18 | for left, right in zip(lefts, rights): 19 | part1 += abs(left - right) 20 | print("Part 1:", part1) 21 | 22 | part2 = 0 23 | right_counts = Counter(rights) 24 | for n in lefts: 25 | part2 += n * right_counts[n] 26 | print("Part 2:", part2) 27 | -------------------------------------------------------------------------------- /2024/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def is_safe(levels: list[int]) -> bool: 7 | increasing = levels[0] < levels[1] 8 | for a, b in zip(levels, levels[1:]): 9 | if a == b or abs(a - b) > 3 or (a < b) != increasing: 10 | return False 11 | return True 12 | 13 | 14 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 15 | part1 = part2 = 0 16 | for line in file.read().splitlines(): 17 | levels = list(map(int, line.split())) 18 | part1 += is_safe(levels) 19 | part2 += any(is_safe(levels[:i] + levels[i + 1 :]) for i in range(len(levels))) 20 | 21 | print("Part 1:", part1) 22 | print("Part 2:", part2) 23 | -------------------------------------------------------------------------------- /2022/day20/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def solve(inp, repeat): 7 | nums = list(enumerate(inp)) 8 | new = nums[:] 9 | for _ in range(repeat): 10 | for p in nums: 11 | i = new.index(p) 12 | new.remove(p) 13 | target = (i + p[1]) % len(new) 14 | new.insert(target, p) 15 | zero = [n for _, n in new].index(0) 16 | return sum(new[(zero + i) % len(new)][1] for i in (1000, 2000, 3000)) 17 | 18 | 19 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 20 | inp = list(map(int, f.read().splitlines())) 21 | print("Part 1:", solve(inp, 1)) 22 | print("Part 2:", solve(map(lambda x: x * 811589153, inp), 10)) 23 | -------------------------------------------------------------------------------- /2025/day07/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | part1 = 0 9 | 10 | start, *grid = file.read().splitlines() 11 | beams = {start.index("S"): 1} 12 | 13 | for line in grid: 14 | new_beams = defaultdict(int) 15 | for b, c in beams.items(): 16 | if line[b] == "^": 17 | new_beams[b - 1] += c 18 | new_beams[b + 1] += c 19 | part1 += 1 20 | else: 21 | new_beams[b] += c 22 | beams = new_beams 23 | 24 | print("Part 1:", part1) 25 | print("Part 2:", sum(beams.values())) 26 | -------------------------------------------------------------------------------- /2018/day05/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from string import ascii_lowercase 4 | 5 | 6 | def retract(poly, exclude=None): 7 | poly = (ord(c) for c in poly if c != exclude and c.lower() != exclude) 8 | stack = [] 9 | 10 | for c in poly: 11 | if stack and abs(stack[-1] - c) == 32: 12 | stack.pop() 13 | else: 14 | stack.append(c) 15 | 16 | return len(stack) 17 | 18 | 19 | def main(): 20 | with open("input.txt") as f: 21 | poly = f.readline().strip() 22 | 23 | print("Part 1:", retract(poly)) 24 | 25 | reduced = [retract(poly, c) for c in ascii_lowercase] 26 | 27 | print("Part 2:", min(reduced)) 28 | 29 | 30 | if __name__ == "__main__": 31 | main() 32 | -------------------------------------------------------------------------------- /2023/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | 6 | 7 | def ints(string) -> list[int]: 8 | return list(map(int, re.findall(r"-?[0-9]+", string))) 9 | 10 | 11 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 12 | lines = f.read().splitlines() 13 | part1 = 0 14 | count = [1] * len(lines) 15 | for i, line in enumerate(lines): 16 | win, my = map(ints, line.split(": ")[1].split(" | ")) 17 | win = set(win) 18 | c = sum(x in win for x in my) 19 | if c > 0: 20 | part1 += 2 ** (c - 1) 21 | for j in range(c): 22 | count[i + j + 1] += count[i] 23 | print("Part 1:", part1) 24 | print("Part 2:", sum(count)) 25 | -------------------------------------------------------------------------------- /2020/day10/input.txt: -------------------------------------------------------------------------------- 1 | 95 2 | 43 3 | 114 4 | 118 5 | 2 6 | 124 7 | 120 8 | 127 9 | 140 10 | 21 11 | 66 12 | 103 13 | 102 14 | 132 15 | 136 16 | 93 17 | 59 18 | 131 19 | 32 20 | 9 21 | 20 22 | 141 23 | 94 24 | 109 25 | 143 26 | 142 27 | 65 28 | 73 29 | 27 30 | 83 31 | 133 32 | 104 33 | 60 34 | 110 35 | 89 36 | 29 37 | 78 38 | 49 39 | 76 40 | 16 41 | 34 42 | 17 43 | 105 44 | 98 45 | 15 46 | 106 47 | 4 48 | 57 49 | 1 50 | 67 51 | 71 52 | 14 53 | 92 54 | 39 55 | 68 56 | 125 57 | 113 58 | 115 59 | 26 60 | 33 61 | 61 62 | 45 63 | 46 64 | 11 65 | 99 66 | 7 67 | 25 68 | 130 69 | 42 70 | 3 71 | 10 72 | 54 73 | 44 74 | 139 75 | 50 76 | 8 77 | 58 78 | 86 79 | 64 80 | 77 81 | 35 82 | 79 83 | 72 84 | 36 85 | 80 86 | 126 87 | 28 88 | 123 89 | 119 90 | 51 91 | 22 92 | -------------------------------------------------------------------------------- /2016/day20/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | intervals = defaultdict(int) 9 | 10 | for line in f.read().splitlines(): 11 | start, end = map(int, line.split("-")) 12 | intervals[start] += 1 13 | intervals[end + 1] -= 1 14 | 15 | c = 0 16 | count = 0 17 | first = None 18 | last = 0 19 | 20 | for k in sorted(intervals): 21 | v = intervals[k] 22 | if c == 0 and v != 0: 23 | count += k - last 24 | c += v 25 | if c == 0: 26 | if first is None: 27 | first = k 28 | last = k 29 | 30 | print("Part 1:", first) 31 | print("Part 2:", count) 32 | -------------------------------------------------------------------------------- /2017/day15/sol.py: -------------------------------------------------------------------------------- 1 | AFAC = 16807 2 | BFAC = 48271 3 | 4 | def aGen(p1=False): 5 | a = 873 6 | while True: 7 | a = (a*AFAC) % 2147483647 8 | if p1 or a % 4 == 0: 9 | yield a 10 | 11 | def bGen(p1=False): 12 | b = 583 13 | while True: 14 | b = (b*BFAC) % 2147483647 15 | if p1 or b % 8 == 0: 16 | yield b 17 | 18 | part1 = 0 19 | A = aGen(True) 20 | B = bGen(True) 21 | for i in range(40000000): 22 | if next(A) & 0xFFFF == next(B) & 0xFFFF: 23 | part1 += 1 24 | print(part1) 25 | 26 | part2 = 0 27 | A = aGen() 28 | B = bGen() 29 | for i in range(5000000): 30 | if next(A) & 0xFFFF == next(B) & 0xFFFF: 31 | part2 += 1 32 | print(part2) 33 | -------------------------------------------------------------------------------- /2020/day25/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from itertools import count 5 | 6 | 7 | def transform(subj, loop_size): 8 | val = 1 9 | for _ in range(loop_size): 10 | val *= subj 11 | val %= 20201227 12 | return val 13 | 14 | 15 | def find_loop_size(subj, res): 16 | val = 1 17 | for loop_size in count(0): 18 | if val == res: 19 | return loop_size 20 | val *= subj 21 | val %= 20201227 22 | 23 | 24 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 25 | pub1, pub2, = (int(x) for x in f.read().splitlines()) 26 | loop1 = find_loop_size(7, pub1) 27 | loop2 = find_loop_size(7, pub2) 28 | print("Part 1:", transform(pub2, loop1)) 29 | -------------------------------------------------------------------------------- /2025/day05/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 7 | part1 = part2 = 0 8 | 9 | ranges, ingredients = file.read().split("\n\n") 10 | ranges = [list(map(int, r.split("-"))) for r in ranges.splitlines()] 11 | 12 | for i in map(int, ingredients.splitlines()): 13 | for a, b in ranges: 14 | if a <= i <= b: 15 | part1 += 1 16 | break 17 | 18 | print("Part 1:", part1) 19 | 20 | ranges.sort() 21 | end = ranges[0][0] - 1 22 | 23 | for a, b in sorted(ranges): 24 | if b > end: 25 | part2 += b - max(a - 1, end) 26 | end = b 27 | 28 | print("Part 2:", part2) 29 | -------------------------------------------------------------------------------- /2017/day22/input.txt: -------------------------------------------------------------------------------- 1 | ..#..##...##.######.##... 2 | ..#...#####..#.#####..#.. 3 | ...##.#..##.#.##....#...# 4 | #.#.#.#..###...#....##..# 5 | ..#..#####.....##..#.#..# 6 | .##.#####.#.....###.#..#. 7 | ##..####...#.##.#...##... 8 | ###.#.#####...##.#.##..#. 9 | #.##..##.#....#.#..#.##.. 10 | ###.######......####..#.# 11 | ###.....#.##.##.######..# 12 | ...####.###.#....#..##.## 13 | #..####.#.....#....###.#. 14 | #..##..#.####.#.##..#.#.. 15 | #..#.#.##...#...#####.##. 16 | #.###..#.##.#..##.####### 17 | ...###..#..####.####.#.#. 18 | .#..###..###.#....####### 19 | .####..##.#####.#.#..#.#. 20 | #.#....##.....##.##.....# 21 | ....####.....#..#.##..##. 22 | ######..##..#.###...###.. 23 | ..##...##.....#..###.###. 24 | ##.#.#..##.#.#.##....##.# 25 | .#.###..##..#....#...##.# 26 | -------------------------------------------------------------------------------- /2021/optimized/src/days.rs: -------------------------------------------------------------------------------- 1 | macro_rules! assert_solver_day { 2 | ($solver:expr) => { 3 | assert_eq!( 4 | format!("day{:02}.rs", crate::Solver::day(&$solver)), 5 | { 6 | let fname = file!(); 7 | &fname[fname.len() - 8..] 8 | }, 9 | "Solver in '{}' has incorrect Solver::day()", 10 | file!() 11 | ); 12 | }; 13 | } 14 | 15 | pub mod day01; 16 | pub mod day02; 17 | pub mod day05; 18 | pub mod day09; 19 | 20 | pub fn get_solvers() -> Vec> { 21 | vec![ 22 | Box::new(day01::Solver::new()), 23 | Box::new(day02::Solver::new()), 24 | Box::new(day05::Solver::new()), 25 | Box::new(day09::Solver::new()), 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /2020/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | PART1_SLOPE = (3, 1) 6 | SLOPES = [ 7 | (1, 1), 8 | (3, 1), 9 | (5, 1), 10 | (7, 1), 11 | (1, 2), 12 | ] 13 | 14 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 15 | trees = [line.strip() for line in f] 16 | width = len(trees[0]) 17 | 18 | part2 = 1 19 | for dx, dy in SLOPES: 20 | x, y = 0, 0 21 | count = 0 22 | 23 | while y < len(trees): 24 | if trees[y][x] == "#": 25 | count += 1 26 | x = (x + dx) % width 27 | y += dy 28 | 29 | if (dx, dy) == PART1_SLOPE: 30 | print("Part 1:", count) 31 | 32 | part2 *= count 33 | 34 | print("Part 2:", part2) 35 | -------------------------------------------------------------------------------- /2020/day09/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import itertools 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | nums = [int(line) for line in f] 9 | 10 | for i, num in enumerate(nums[25:], 25): 11 | for pair in itertools.combinations(nums[i-25:i], 2): 12 | if sum(pair) == num: 13 | break 14 | else: 15 | print("Part 1:", num) 16 | break 17 | 18 | for n in itertools.count(2): 19 | for i in range(len(nums) - n): 20 | group = nums[i : i + n] 21 | if sum(group) == num: 22 | print("Part 2:", min(group) + max(group)) 23 | break 24 | else: 25 | continue 26 | break 27 | -------------------------------------------------------------------------------- /2024/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import math 5 | import re 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 9 | part1 = part2 = 0 10 | enabled = True 11 | for line in file.read().splitlines(): 12 | for op in re.findall(r"mul\(\d+,\d+\)|do\(\)|don't\(\)", line): 13 | if op == "do()": 14 | enabled = True 15 | elif op == "don't()": 16 | enabled = False 17 | else: 18 | nums = re.findall(r"\d+", op) 19 | result = math.prod(map(int, nums)) 20 | part1 += result 21 | if enabled: 22 | part2 += result 23 | 24 | print("Part 1:", part1) 25 | print("Part 2:", part2) 26 | -------------------------------------------------------------------------------- /2017/day12/sol.py: -------------------------------------------------------------------------------- 1 | with open("input.txt", "r") as f: 2 | to = [] 3 | for line in f: 4 | to.append([int(x.strip(",")) for x in line.strip().split(" ")[2:]]) 5 | groups = [] 6 | todo = set(range(len(to))) 7 | while todo: 8 | stack = [todo.pop()] 9 | group = set() 10 | while stack: 11 | element = stack.pop(0) 12 | for n in to[element]: 13 | if n not in group: 14 | group.add(n) 15 | stack.append(n) 16 | if n in todo: 17 | todo.remove(n) 18 | groups.append(group) 19 | for g in groups: 20 | if 0 in g: 21 | print("Group 0:", len(g)) 22 | print("Total groups:", len(groups)) 23 | 24 | -------------------------------------------------------------------------------- /2025/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 7 | part1 = part2 = 0 8 | dial = 50 9 | 10 | for line in file.read().splitlines(): 11 | direction = line[0] 12 | amount = int(line[1:]) 13 | old_dial = dial 14 | 15 | if amount >= 100: 16 | part2 += amount // 100 17 | amount %= 100 18 | 19 | if direction == "R": 20 | dial += amount 21 | else: 22 | dial -= amount 23 | 24 | if old_dial != 0 and (dial <= 0 or dial >= 100): 25 | part2 += 1 26 | 27 | dial %= 100 28 | 29 | if dial == 0: 30 | part1 += 1 31 | 32 | print("Part 1:", part1) 33 | print("Part 2:", part2) 34 | -------------------------------------------------------------------------------- /2019/day06/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from networkx import DiGraph, dfs_postorder_nodes, shortest_path_length 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | g = DiGraph() 9 | 10 | for line in f: 11 | line = line.strip() 12 | 13 | a, b = line.split(")") 14 | g.add_edge(a, b) 15 | 16 | total_orbits = 0 17 | 18 | for n in dfs_postorder_nodes(g, "COM"): 19 | curr_orbits = 0 20 | 21 | for s in g[n]: 22 | curr_orbits += g.nodes[s]["orbits"] + 1 23 | 24 | g.nodes[n]["orbits"] = curr_orbits 25 | total_orbits += curr_orbits 26 | 27 | print("Part 1:", total_orbits) 28 | 29 | transfers = shortest_path_length(g.to_undirected(), "YOU", "SAN") - 2 30 | print("Part 2:", transfers) 31 | -------------------------------------------------------------------------------- /2015/day09/input.txt: -------------------------------------------------------------------------------- 1 | Faerun to Tristram = 65 2 | Faerun to Tambi = 129 3 | Faerun to Norrath = 144 4 | Faerun to Snowdin = 71 5 | Faerun to Straylight = 137 6 | Faerun to AlphaCentauri = 3 7 | Faerun to Arbre = 149 8 | Tristram to Tambi = 63 9 | Tristram to Norrath = 4 10 | Tristram to Snowdin = 105 11 | Tristram to Straylight = 125 12 | Tristram to AlphaCentauri = 55 13 | Tristram to Arbre = 14 14 | Tambi to Norrath = 68 15 | Tambi to Snowdin = 52 16 | Tambi to Straylight = 65 17 | Tambi to AlphaCentauri = 22 18 | Tambi to Arbre = 143 19 | Norrath to Snowdin = 8 20 | Norrath to Straylight = 23 21 | Norrath to AlphaCentauri = 136 22 | Norrath to Arbre = 115 23 | Snowdin to Straylight = 101 24 | Snowdin to AlphaCentauri = 84 25 | Snowdin to Arbre = 96 26 | Straylight to AlphaCentauri = 107 27 | Straylight to Arbre = 14 28 | AlphaCentauri to Arbre = 46 29 | -------------------------------------------------------------------------------- /2024/day19/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from functools import cache 4 | from os import path 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | towels, designs = file.read().split("\n\n") 9 | towels = towels.split(", ") 10 | 11 | @cache 12 | def ways_to_make(d: str) -> int: 13 | count = 0 14 | for t in towels: 15 | if d.startswith(t): 16 | if len(d) == len(t): 17 | count += 1 18 | else: 19 | count += ways_to_make(d[len(t) :]) 20 | return count 21 | 22 | part1 = part2 = 0 23 | 24 | for d in designs.splitlines(): 25 | ways = ways_to_make(d) 26 | part1 += ways > 0 27 | part2 += ways 28 | 29 | print("Part 1:", part1) 30 | print("Part 2:", part2) 31 | -------------------------------------------------------------------------------- /2023/day09/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def extrapolate(line: list[int], part2: bool) -> int: 7 | diffs = [line] 8 | while not all(x == 0 for x in diffs[-1]): 9 | line = diffs[-1] 10 | diffs.append([]) 11 | for a, b in zip(line, line[1::]): 12 | diffs[-1].append(b - a) 13 | last = 0 14 | for line in reversed(diffs[:-1]): 15 | if part2: 16 | last = line[0] - last 17 | else: 18 | last = line[-1] + last 19 | return last 20 | 21 | 22 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 23 | lines = [list(map(int, line.split())) for line in f.read().splitlines()] 24 | print("Part 1:", sum(extrapolate(line, False) for line in lines)) 25 | print("Part 2:", sum(extrapolate(line, True) for line in lines)) 26 | -------------------------------------------------------------------------------- /2015/day17/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from functools import lru_cache 5 | 6 | 7 | @lru_cache(maxsize=None) 8 | def solve(buckets, target, num=100): 9 | if num == 0: 10 | return 0 11 | total = 0 12 | for i, b in enumerate(buckets): 13 | if b > target: 14 | continue 15 | elif b == target: 16 | total += 1 17 | else: 18 | total += solve(buckets[i+1:], target - b, num-1) 19 | return total 20 | 21 | 22 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 23 | buckets = tuple(sorted(map(int, f.read().splitlines()), reverse=True)) 24 | 25 | print("Part 1:", solve(buckets, 150)) 26 | 27 | for i in range(100): 28 | res = solve(buckets, 150, i) 29 | if res != 0: 30 | print("Part 2:", res) 31 | break 32 | -------------------------------------------------------------------------------- /2015/day20/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | target = int(f.read().strip()) 8 | 9 | 10 | N = 1_000_000 11 | houses = [0] * N 12 | for elf in range(1, N): 13 | num = elf 14 | presents = elf * 10 15 | while num < N: 16 | houses[num] += presents 17 | num += elf 18 | 19 | for h, p in enumerate(houses): 20 | if p >= target: 21 | print("Part 1:", h) 22 | break 23 | 24 | 25 | houses = [0] * N 26 | for elf in range(1, N): 27 | num = elf 28 | presents = elf * 11 29 | for _ in range(50): 30 | houses[num] += presents 31 | num += elf 32 | if num >= N: 33 | break 34 | 35 | for h, p in enumerate(houses): 36 | if p >= target: 37 | print("Part 2:", h) 38 | break 39 | -------------------------------------------------------------------------------- /2016/day16/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def gen(line, length): 7 | while len(line) < length: 8 | line += "0" + "".join("1" if c == "0" else "0" for c in reversed(line)) 9 | return line[:length] 10 | 11 | 12 | def checksum(line): 13 | if len(line) % 2 == 1: 14 | return line 15 | 16 | out = "" 17 | 18 | for a, b in zip(line[::2], line[1::2]): 19 | if a == b: 20 | out += "1" 21 | else: 22 | out += "0" 23 | 24 | return checksum(out) 25 | 26 | 27 | def main(): 28 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 29 | start = f.readline().strip() 30 | 31 | print("Part 1:", checksum(gen(start, 272))) 32 | print("Part 2:", checksum(gen(start, 35651584))) 33 | 34 | 35 | if __name__ == "__main__": 36 | main() 37 | -------------------------------------------------------------------------------- /2017/day09/soll.py: -------------------------------------------------------------------------------- 1 | def score(line): 2 | total = 0 3 | curr = 0 4 | garbage = False 5 | ignore = False 6 | garbage_count = 0 7 | for c in line: 8 | if ignore: 9 | ignore = False 10 | elif garbage: 11 | if c == ">": 12 | garbage = False 13 | elif c == "!": 14 | ignore = True 15 | else: 16 | garbage_count += 1 17 | elif c == "<": 18 | garbage = True 19 | elif c == "{": 20 | curr += 1 21 | total += curr 22 | elif c == "}": 23 | curr -= 1 24 | return total, garbage_count 25 | 26 | print(score("{{},{},{},{}}")) 27 | 28 | with open("input.txt", "r") as f: 29 | for line in f: 30 | print(score(line)) 31 | break 32 | -------------------------------------------------------------------------------- /2023/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | import math 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | part1, part2 = 0, 0 10 | for i, line in enumerate(f.read().splitlines()): 11 | rounds = line.split(": ")[1].split(";") 12 | mins = {"red": 0, "green": 0, "blue": 0} 13 | maxs = {"red": 12, "green": 13, "blue": 14} 14 | wrong = False 15 | for r in rounds: 16 | for cubes in r.split(", "): 17 | n, c = cubes.split() 18 | n = int(n) 19 | mins[c] = max(mins[c], n) 20 | if n > maxs[c]: 21 | wrong = True 22 | if not wrong: 23 | part1 += i + 1 24 | part2 += math.prod(mins.values()) 25 | 26 | print("Part 1:", part1) 27 | print("Part 2:", part2) 28 | -------------------------------------------------------------------------------- /2016/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | 5 | 6 | def main(): 7 | numbers = [] 8 | 9 | with open("input.txt") as f: 10 | for line in f: 11 | numbers.append(tuple(map(int, re.findall(r"(\d+) +(\d+) +(\d+)", line.strip())[0]))) 12 | 13 | count = 0 14 | for vals in numbers: 15 | a, b, c = sorted(vals) 16 | if a + b > c: 17 | count += 1 18 | 19 | print("Part 1:", count) 20 | 21 | count = 0 22 | for i in range(len(numbers)): 23 | vals = ( 24 | (numbers[(i // 3) * 3][i % 3]), 25 | (numbers[(i // 3) * 3 + 1][i % 3]), 26 | (numbers[(i // 3) * 3 + 2][i % 3]) 27 | ) 28 | 29 | a, b, c = sorted(vals) 30 | if a + b > c: 31 | count += 1 32 | 33 | print("Part 2:", count) 34 | 35 | 36 | if __name__ == "__main__": 37 | main() 38 | -------------------------------------------------------------------------------- /2025/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def is_repeated(n: str, times: int) -> bool: 7 | if len(n) % times != 0: 8 | return False 9 | size = len(n) // times 10 | pattern = n[:size] 11 | for i in range(1, times): 12 | if pattern != n[i * size : (i + 1) * size]: 13 | return False 14 | return True 15 | 16 | 17 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 18 | part1 = part2 = 0 19 | 20 | for r in file.read().strip().split(","): 21 | a, b = map(int, r.split("-")) 22 | for n in map(str, range(a, b + 1)): 23 | if is_repeated(n, 2): 24 | part1 += int(n) 25 | if any(is_repeated(n, times) for times in range(2, len(n) + 1)): 26 | part2 += int(n) 27 | 28 | print("Part 1:", part1) 29 | print("Part 2:", part2) 30 | -------------------------------------------------------------------------------- /2024/day11/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import defaultdict 4 | from os import path 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | counts = {int(stone): 1 for stone in file.read().strip().split()} 9 | 10 | for i in range(75): 11 | if i == 25: 12 | print("Part 1:", sum(counts.values())) 13 | 14 | new_counts = defaultdict(int) 15 | for s, c in counts.items(): 16 | if s == 0: 17 | new_counts[1] += c 18 | continue 19 | ss = str(s) 20 | if len(ss) % 2 == 0: 21 | new_counts[int(ss[: len(ss) // 2])] += c 22 | new_counts[int(ss[len(ss) // 2 :])] += c 23 | else: 24 | new_counts[s * 2024] += c 25 | counts = new_counts 26 | 27 | print("Part 2:", sum(counts.values())) 28 | -------------------------------------------------------------------------------- /2024/day05/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from functools import cmp_to_key 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | orderings, updates = map(str.splitlines, file.read().split("\n\n")) 9 | orderings = {tuple(map(int, line.split("|"))) for line in orderings} 10 | 11 | def compare(a: int, b: int) -> int: 12 | if (a, b) in orderings: 13 | return -1 14 | return 1 15 | 16 | part1 = part2 = 0 17 | 18 | for update in updates: 19 | update = list(map(int, update.split(","))) 20 | update_sorted = sorted(update, key=cmp_to_key(compare)) 21 | middle = update_sorted[len(update_sorted) // 2] 22 | if update == update_sorted: 23 | part1 += middle 24 | else: 25 | part2 += middle 26 | 27 | print("Part 1:", part1) 28 | print("Part 2:", part2) 29 | -------------------------------------------------------------------------------- /2019/day16/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import * 5 | import itertools 6 | import math 7 | 8 | 9 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 10 | inp = f.readline().strip() 11 | 12 | digits = list(map(int, inp)) 13 | for _ in range(100): 14 | for i in range(len(digits)): 15 | val = 0 16 | for j, d in enumerate(digits[i:], i): 17 | val += d * (0,1,0,-1)[(j+1) // (i+1) % 4] 18 | digits[i] = int(str(val)[-1]) 19 | 20 | print("Part 1: ", *digits[:8], sep="") 21 | 22 | digits = list(map(int, inp)) 23 | digits *= 10000 24 | offset = int(inp[:7]) 25 | 26 | for _ in range(100): 27 | for i in reversed(range(offset, len(digits)-1)): 28 | digits[i] = (digits[i] + digits[i+1]) % 10 29 | 30 | print("Part 2: ", *digits[offset:offset+8], sep="") 31 | -------------------------------------------------------------------------------- /2020/day13/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import math 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | start, buses = f.read().splitlines() 9 | start = int(start) 10 | buses = [int(b) if b != "x" else None for b in buses.split(",")] 11 | 12 | min_arrival = float("inf") 13 | min_bus = None 14 | for b in filter(bool, buses): 15 | next_arrival = b - start % b 16 | if next_arrival < min_arrival: 17 | min_arrival, min_bus = next_arrival, b 18 | 19 | print("Part 1:", min_bus * min_arrival) 20 | 21 | M = math.prod(b for b in buses if b is not None) 22 | result = 0 23 | 24 | for i, b in enumerate(buses): 25 | if b is None: 26 | continue 27 | 28 | Mi = M // b 29 | mi = pow(Mi, -1, b) 30 | result += (-i) * Mi * mi 31 | 32 | print("Part 2:", result % M) 33 | -------------------------------------------------------------------------------- /2021/day10/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | OPPOSITE = { 6 | "(": ")", 7 | "[": "]", 8 | "{": "}", 9 | "<": ">", 10 | } 11 | 12 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 13 | part1 = 0 14 | scores = [] 15 | for line in f.read().splitlines(): 16 | stack = [] 17 | for c in line: 18 | if c in OPPOSITE: 19 | stack.append(OPPOSITE[c]) 20 | elif c != stack.pop(): 21 | part1 += {")": 3, "]": 57, "}": 1197, ">": 25137}[c] 22 | break 23 | else: 24 | score = 0 25 | for c in reversed(stack): 26 | score *= 5 27 | score += {")": 1, "]": 2, "}": 3, ">": 4}[c] 28 | scores.append(score) 29 | 30 | scores.sort() 31 | 32 | print("Part 1:", part1) 33 | print("Part 2:", scores[len(scores) // 2]) 34 | -------------------------------------------------------------------------------- /2021/day12/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from networkx import Graph 5 | 6 | 7 | def dfs(G, node, can_twice): 8 | if node == "end": 9 | return 1 10 | 11 | G.nodes[node]["visited"] += 1 12 | total = 0 13 | 14 | for n in G.adj[node]: 15 | if n.isupper() or G.nodes[n]["visited"] == 0: 16 | total += dfs(G, n, can_twice) 17 | elif can_twice and n != "start" and G.nodes[n]["visited"] == 1: 18 | total += dfs(G, n, False) 19 | 20 | G.nodes[node]["visited"] -= 1 21 | return total 22 | 23 | 24 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 25 | G = Graph() 26 | 27 | for line in f.read().splitlines(): 28 | G.add_edge(*line.split("-")) 29 | 30 | for n in G: 31 | G.nodes[n]["visited"] = 0 32 | 33 | print("Part 1:", dfs(G, "start", False)) 34 | print("Part 2:", dfs(G, "start", True)) 35 | -------------------------------------------------------------------------------- /2017/day06/sol.py: -------------------------------------------------------------------------------- 1 | def check(memory): 2 | counter = 0 3 | known_states = {} 4 | length = len(memory) 5 | while True: 6 | s = ";".join(map(str, memory)) 7 | if s in known_states.keys(): 8 | return counter, counter - known_states[s] 9 | known_states[s] = counter 10 | counter += 1 11 | index = 0 12 | blocks = memory[0] 13 | for i in range(1, length): 14 | if memory[i] > blocks: 15 | blocks = memory[i] 16 | index = i 17 | memory[index] = 0 18 | index += 1 19 | while blocks > 0: 20 | memory[index % length] += 1 21 | blocks -= 1 22 | index += 1 23 | 24 | print(check([0, 2, 7, 0])) 25 | 26 | with open("input.txt", "r") as f: 27 | for line in f: 28 | print(check([int(x) for x in line.strip().split("\t")])) 29 | break 30 | -------------------------------------------------------------------------------- /2023/day08/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import itertools 5 | import math 6 | 7 | 8 | def steps_until(start, end_fn): 9 | pos = start 10 | for i, move in enumerate(itertools.cycle(instructions)): 11 | if end_fn(pos): 12 | return i 13 | pos = network[pos][move == "R"] 14 | assert False 15 | 16 | 17 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 18 | lines = f.read().splitlines() 19 | instructions = lines[0] 20 | network = {} 21 | for line in lines[2:]: 22 | start, ends = line.split(" = ") 23 | network[start] = ends[1:-1].split(", ") 24 | 25 | print("Part 1:", steps_until("AAA", lambda pos: pos == "ZZZ")) 26 | 27 | steps = [ 28 | steps_until(start, lambda pos: pos.endswith("Z")) 29 | for start in network 30 | if start.endswith("A") 31 | ] 32 | print("Part 2:", math.lcm(*steps)) 33 | -------------------------------------------------------------------------------- /2015/day24/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import math 5 | from z3 import Optimize, Int, If, sat 6 | 7 | 8 | def solve(parts): 9 | part = sum(nums) // parts 10 | opt = Optimize() 11 | vars = [(Int(str(x)), x) for x in nums] 12 | 13 | for v, _ in vars: 14 | opt.add(v >= 0) 15 | opt.add(v < parts) 16 | 17 | for i in range(parts - 1): 18 | opt.add(sum(If(v == i, n, 0) for v, n in vars) == part) 19 | 20 | count = sum(If(v == 0, 1, 0) for v, _ in vars) 21 | quantum = math.prod(If(v == 0, n, 1) for v, n in vars) 22 | 23 | opt.minimize(count) 24 | opt.minimize(quantum) 25 | 26 | assert opt.check() == sat 27 | return opt.model().eval(quantum).as_long() 28 | 29 | 30 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 31 | nums = list(map(int, f.read().splitlines())) 32 | 33 | 34 | print("Part 1:", solve(3)) 35 | print("Part 2:", solve(4)) 36 | -------------------------------------------------------------------------------- /2017/day11/sol.py: -------------------------------------------------------------------------------- 1 | import turtle 2 | # Visualization 3 | turtle.shape("circle") 4 | turtle.shapesize(0.1, 0.1) 5 | turtle.speed(0) 6 | 7 | MOVES = { 8 | "n": ( 2, 0), 9 | "ne": ( 1, 1), 10 | "se": (-1, 1), 11 | "s": (-2, 0), 12 | "sw": (-1, -1), 13 | "nw": ( 1, -1) 14 | } 15 | 16 | def dist(x, y): 17 | x = abs(x) 18 | y = abs(y) 19 | ma = max(x,y) 20 | mi = min(x,y) 21 | return mi+((ma-mi)//2) 22 | 23 | def find_pos(line): 24 | moves = line.strip().lower().split(",") 25 | x = 0 26 | y = 0 27 | max_dist = 0 28 | for m in moves: 29 | x += MOVES[m][0] 30 | y += MOVES[m][1] 31 | #turtle.goto(x, y) # Visualize path 32 | max_dist = max(max_dist, dist(x,y)) 33 | return x, y, dist(x,y), max_dist 34 | 35 | with open("input.txt", "r") as f: 36 | for line in f: 37 | print(find_pos(line)) 38 | break 39 | -------------------------------------------------------------------------------- /2021/day24/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | prog = [line.split() for line in f.read().splitlines()] 8 | stack = [] 9 | part1 = [None] * 14 10 | part2 = [None] * 14 11 | for i in range(14): 12 | if prog[18 * i + 4][-1] == "1": 13 | stack.append((i, int(prog[18 * i + 15][-1]))) 14 | else: 15 | j, n = stack.pop() 16 | n += int(prog[18 * i + 5][-1]) 17 | if n > 0: 18 | part1[i] = 9 19 | part1[j] = 9 - n 20 | part2[i] = 1 + n 21 | part2[j] = 1 22 | else: 23 | part1[i] = 9 + n 24 | part1[j] = 9 25 | part2[i] = 1 26 | part2[j] = 1 - n 27 | print("Part 1:", "".join(map(str, part1))) 28 | print("Part 2:", "".join(map(str, part2))) 29 | -------------------------------------------------------------------------------- /2017/day25/sol.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | PROGRAM = { 4 | "A": ( 5 | (1, 1, "B"), 6 | (0, -1, "E") 7 | ), 8 | "B": ( 9 | (1, -1, "C"), 10 | (0, 1, "A") 11 | ), 12 | "C": ( 13 | (1, -1, "D"), 14 | (0, 1, "C") 15 | ), 16 | "D": ( 17 | (1, -1, "E"), 18 | (0, -1, "F") 19 | ), 20 | "E": ( 21 | (1, -1, "A"), 22 | (1, -1, "C") 23 | ), 24 | "F": ( 25 | (1, -1, "E"), 26 | (1, 1, "A") 27 | ) 28 | } 29 | 30 | STEPS = 12386363 31 | 32 | def main(): 33 | state = "A" 34 | pos = 0 35 | memory = defaultdict(lambda: 0) 36 | for i in range(STEPS): 37 | write, move, new_state = PROGRAM[state][memory[pos]] 38 | memory[pos] = write 39 | pos += move 40 | state = new_state 41 | print(sum(memory.values())) 42 | 43 | 44 | if __name__ == '__main__': 45 | main() 46 | -------------------------------------------------------------------------------- /2020/day07/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from networkx import DiGraph, dfs_postorder_nodes 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | G = DiGraph() 8 | 9 | for line in f: 10 | bag, contains = line.strip().rstrip(".").split(" bags contain ") 11 | 12 | if contains == "no other bags": 13 | continue 14 | 15 | for other in contains.split(", "): 16 | count = int(other[0]) 17 | other = other[2:].rstrip("bags").strip() 18 | G.add_edge(bag, other, count=count) 19 | 20 | print("Part 1:", len(list(dfs_postorder_nodes(G.reverse(), "shiny gold"))) - 1) 21 | 22 | for node in dfs_postorder_nodes(G, "shiny gold"): 23 | G.nodes[node]["count"] = sum( 24 | (G.nodes[n]["count"] + 1) * v["count"] for (n, v) in G[node].items() 25 | ) 26 | 27 | print("Part 2:", G.nodes["shiny gold"]["count"]) 28 | -------------------------------------------------------------------------------- /2016/day25/input_dec.txt: -------------------------------------------------------------------------------- 1 | cpy a d d = a + 9 * 282 2 | cpy 9 c 3 | cpy 282 b 4 | inc d 5 | dec b 6 | jnz b -2 7 | dec c 8 | jnz c -5 9 | loop 10 | cpy d a a = d 11 | jnz 0 0 do 12 | cpy a b b = a 13 | cpy 0 a a = 0 14 | loop 15 | cpy 2 c c = 2 16 | do 17 | jnz b 2 if b == 0 18 | jnz 1 6 break 2 19 | dec b b-- 20 | dec c c-- 21 | jnz c -4 while c 22 | inc a a++ 23 | jnz 1 -7 24 | cpy 2 b b = 2 25 | jnz c 2 while c 26 | jnz 1 4 27 | dec b b-- 28 | dec c c-- 29 | jnz 1 -4 30 | jnz 0 0 31 | out b out b 32 | jnz a -19 while a 33 | jnz 1 -21 34 | 35 | loop 36 | a = init + 9 * 282 37 | do 38 | out 2 - a % 2 39 | a //= 2 40 | while a 41 | -------------------------------------------------------------------------------- /2017/day22/sol.py: -------------------------------------------------------------------------------- 1 | from collections import defaultdict 2 | 3 | DIRS = ((0, -1), (1, 0), (0, 1), (-1, 0)) 4 | DIR_CHANGE = (3, 0, 1, 2) 5 | 6 | def main(): 7 | cells = defaultdict(lambda: 0) 8 | width = None 9 | height = None 10 | with open("input.txt", "r") as f: 11 | for y, line in enumerate(f): 12 | if width is None: 13 | width = len(line.strip()) 14 | height = y+1 15 | for x, c in enumerate(line.strip()): 16 | cells[(x, y)] = 2 if c == "#" else 0 17 | x = width // 2 18 | y = height // 2 19 | d = 0 20 | infections = 0 21 | for _ in range(10000000): 22 | d = (d + DIR_CHANGE[cells[(x, y)]]) % 4 23 | if cells[(x, y)] == 1: 24 | infections += 1 25 | cells[(x, y)] = (cells[(x, y)] + 1) % 4 26 | x += DIRS[d][0] 27 | y += DIRS[d][1] 28 | print(infections) 29 | 30 | if __name__ == '__main__': 31 | main() 32 | -------------------------------------------------------------------------------- /2018/day09/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import deque 4 | 5 | 6 | def simulate(players, max_marble): 7 | circle = deque([0, 1]) 8 | points = [0]*players 9 | player = 1 10 | 11 | for marble in range(2, max_marble + 1): 12 | if marble % 23 == 0: 13 | circle.rotate(7) 14 | points[player] += marble + circle.pop() 15 | circle.rotate(-1) 16 | else: 17 | circle.rotate(-1) 18 | circle.append(marble) 19 | 20 | player = (player + 1) % players 21 | 22 | return max(points) 23 | 24 | 25 | def main(): 26 | with open("input.txt") as f: 27 | parts = f.readline().strip().split(" ") 28 | players = int(parts[0]) 29 | max_marble = int(parts[6]) 30 | 31 | print("Part 1:", simulate(players, max_marble)) 32 | print("Part 2:", simulate(players, max_marble*100)) 33 | 34 | 35 | if __name__ == "__main__": 36 | main() 37 | -------------------------------------------------------------------------------- /2022/day25/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import math 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | total = 0 9 | for line in f.read().splitlines(): 10 | for i, c in enumerate(reversed(line)): 11 | total += ("=-012".index(c) - 2) * 5 ** i 12 | 13 | largest_power = int(math.log(total, 5)) + 2 14 | coeffs = [] 15 | for i in reversed(range(largest_power)): 16 | for j in reversed(range(5)): 17 | if j * 5 ** i <= total: 18 | coeffs.append(j) 19 | total -= j * 5 ** i 20 | break 21 | 22 | while any(c > 2 for c in coeffs): 23 | for i, c in enumerate(coeffs): 24 | if c > 2: 25 | coeffs[i - 1] += 1 26 | coeffs[i] = c - 5 27 | 28 | while coeffs[0] == 0: 29 | coeffs = coeffs[1:] 30 | 31 | print("".join("012=-"[c] for c in coeffs)) 32 | -------------------------------------------------------------------------------- /2015/day06/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | lights1 = defaultdict(bool) 9 | lights2 = defaultdict(int) 10 | 11 | for instr in map(str.split, f.read().splitlines()): 12 | toggle = instr[0] == "toggle" 13 | x1, y1 = map(int, instr[2 - toggle].split(",")) 14 | x2, y2 = map(int, instr[4 - toggle].split(",")) 15 | for x in range(x1, x2 + 1): 16 | for y in range(y1, y2 + 1): 17 | if toggle: 18 | lights1[x, y] = not lights1[x, y] 19 | else: 20 | lights1[x, y] = instr[1] == "on" 21 | d = 2 if toggle else (-1, 1)[instr[1] == "on"] 22 | lights2[x, y] = max(0, lights2[x, y] + d) 23 | 24 | print("Part 1:", sum(lights1.values())) 25 | print("Part 2:", sum(lights2.values())) 26 | -------------------------------------------------------------------------------- /2021/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | 6 | 7 | def filter(nums: list, op, i=0): 8 | if len(nums) == 1: 9 | return int(nums[0], 2) 10 | 11 | c = sum((x[i] == "1") * 2 - 1 for x in nums) 12 | nums = [x for x in nums if op(c, 0) == int(x[i])] 13 | return filter(nums, op, i + 1) 14 | 15 | 16 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 17 | bit_count = defaultdict(int) 18 | nums = [] 19 | 20 | for line in f.read().splitlines(): 21 | for i, bit in enumerate(reversed(line)): 22 | bit_count[i] += (bit == "1") * 2 - 1 23 | nums.append(line) 24 | 25 | gamma, epsilon = 0, 0 26 | 27 | for i, x in bit_count.items(): 28 | gamma |= (x > 0) << i 29 | epsilon |= (x < 0) << i 30 | 31 | print("Part 1:", gamma * epsilon) 32 | print("Part 2:", filter(nums, int.__ge__) * filter(nums, int.__lt__)) 33 | -------------------------------------------------------------------------------- /2021/day14/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import Counter 5 | from functools import lru_cache 6 | 7 | 8 | @lru_cache(maxsize=None) 9 | def count(poly, n): 10 | if n == 0: 11 | return Counter(poly) 12 | 13 | if len(poly) > 2: 14 | return sum( 15 | (count(poly[i : i + 2], n) for i in range(len(poly) - 1)), Counter() 16 | ) - Counter(poly[1:-1]) 17 | 18 | new = rules[poly] 19 | return count(poly[0] + new, n - 1) + count(new + poly[1], n - 1) - Counter(new) 20 | 21 | 22 | def solve(n): 23 | c = count(poly, n) 24 | most_common = c.most_common() 25 | return most_common[0][1] - most_common[-1][1] 26 | 27 | 28 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 29 | lines = f.read().splitlines() 30 | poly = lines[0] 31 | rules = dict(rule.split(" -> ") for rule in lines[2:]) 32 | 33 | print("Part 1:", solve(10)) 34 | print("Part 2:", solve(40)) 35 | -------------------------------------------------------------------------------- /2018/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import Counter 4 | 5 | 6 | def main(): 7 | two = 0 8 | three = 0 9 | ids = [] 10 | 11 | with open("input.txt") as f: 12 | for line in f: 13 | line = line.strip() 14 | ids.append(line) 15 | 16 | c = Counter(line) 17 | if 2 in c.values(): 18 | two += 1 19 | if 3 in c.values(): 20 | three += 1 21 | 22 | print("Part 1:", two * three) 23 | 24 | for i, val in enumerate(ids): 25 | for other in ids[i+1:]: 26 | diff = 0 27 | for k in range(len(val)): 28 | if val[k] != other[k]: 29 | if diff != 0: 30 | break 31 | diff = k + 1 32 | else: 33 | print("Part 2:", val[:diff-1] + val[diff:]) 34 | return 35 | 36 | 37 | if __name__ == "__main__": 38 | main() 39 | -------------------------------------------------------------------------------- /2020/day10/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from functools import lru_cache 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | jolts = [int(line) for line in f] 9 | jolts.sort() 10 | jolts = [0] + jolts + [jolts[-1] + 3] 11 | 12 | ones = threes = 0 13 | 14 | for a, b in zip(jolts, jolts[1:]): 15 | if b - a == 1: 16 | ones += 1 17 | if b - a == 3: 18 | threes += 1 19 | 20 | print("Part 1:", ones * threes) 21 | 22 | 23 | @lru_cache 24 | def arrangements(i): 25 | if i >= len(jolts): 26 | return 1 27 | 28 | x = jolts[i] 29 | result = arrangements(i + 1) 30 | 31 | if i + 2 < len(jolts) and jolts[i + 2] - x <= 3: 32 | result += arrangements(i + 2) 33 | 34 | if i + 3 < len(jolts) and jolts[i + 3] - x <= 3: 35 | result += arrangements(i + 3) 36 | 37 | return result 38 | 39 | 40 | print("Part 2:", arrangements(0)) 41 | -------------------------------------------------------------------------------- /2022/day10/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | part1, part2 = 0, "" 8 | cycle = 1 9 | value = 1 10 | 11 | for line in f.read().splitlines(): 12 | if (cycle - 1) % 40 == 0: 13 | part2 += "\n" 14 | part2 += "#" if (cycle - 1) % 40 in (value - 1, value, value + 1) else " " 15 | if cycle in (20, 60, 100, 140, 180, 220): 16 | part1 += cycle * value 17 | 18 | if line.startswith("noop"): 19 | cycle += 1 20 | else: 21 | if cycle % 40 == 0: 22 | part2 += "\n" 23 | part2 += "#" if cycle % 40 in (value - 1, value, value + 1) else " " 24 | if cycle + 1 in (20, 60, 100, 140, 180, 220): 25 | part1 += (cycle + 1) * value 26 | cycle += 2 27 | value += int(line.split()[1]) 28 | 29 | print("Part 1:", part1) 30 | print("Part 2:", part2) 31 | -------------------------------------------------------------------------------- /2015/day15/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import kill, path 4 | import itertools 5 | import math 6 | import re 7 | 8 | 9 | def ints(string): 10 | return list(map(int, re.findall(r"-?[0-9]+", string))) 11 | 12 | 13 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 14 | ingredients = [ints(line) for line in f.read().splitlines()] 15 | best1, best2 = 0, 0 16 | for a in range(101): 17 | for b in range(101 - a): 18 | for c in range(101 - a - b): 19 | d = 100 - a - b - c 20 | attrs = [0] * 5 21 | for ing, amnt in zip(ingredients, (a, b, c, d)): 22 | for i, x in enumerate(ing): 23 | attrs[i] += x * amnt 24 | score = math.prod(max(0, x) for x in attrs[:-1]) 25 | best1 = max(best1, score) 26 | if attrs[-1] == 500: 27 | best2 = max(best2, score) 28 | print("Part 1:", best1) 29 | print("Part 2:", best2) 30 | -------------------------------------------------------------------------------- /2016/day23/input_dec.txt: -------------------------------------------------------------------------------- 1 | cpy a b b = a 2 | dec b b-- b = a - 1 3 | cpy a d d = a d = a 4 | cpy 0 a a = 0 a = d*b, c = 0, d = 0 5 | do 6 | cpy b c c = b 7 | do 8 | inc a a++ 9 | dec c c-- 10 | jnz c -2 while c 11 | dec d d-- 12 | jnz d -5 while d 13 | dec b b-- b-- 14 | cpy b c c = b c = 2*b 15 | cpy c d d = c 16 | do 17 | dec d d-- 18 | inc c c++ 19 | jnz d -2 while d 20 | tgl c tgl c tgl c 21 | cpy -16 c c = -16 c = -16 22 | jnz 1 c jmp c 23 | cpy 89 c c = 89 24 | do 25 | jnz 77 d jmp d 26 | do 27 | inc a a++ 28 | inc d d++ 29 | jnz d -2 while d 30 | inc c c++ 31 | jnz c -5 while c 32 | -------------------------------------------------------------------------------- /2022/day07/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | dirs = defaultdict(int) 9 | cwd = [] 10 | for line in f.read().splitlines(): 11 | if line.startswith("$ cd"): 12 | d = line[5:] 13 | if d == "..": 14 | cwd.pop() 15 | else: 16 | cwd.append(d) 17 | elif line.startswith("$ ls"): 18 | continue 19 | else: 20 | try: 21 | dirs["/".join(cwd)] += int(line.split()[0]) 22 | except ValueError: 23 | pass 24 | 25 | for d in sorted(dirs.keys(), key=lambda x: x.count("/"), reverse=True): 26 | dirs["/".join(d.split("/")[:-1])] += dirs[d] 27 | 28 | print("Part 1:", sum(s for s in dirs.values() if s <= 100_000)) 29 | 30 | free = 70_000_000 - dirs["/"] 31 | needed = 30_000_000 - free 32 | print("Part 2:", min(v for v in dirs.values() if v > needed)) 33 | -------------------------------------------------------------------------------- /2024/day13/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | from os import path 5 | from z3 import Optimize, Int, sat 6 | 7 | 8 | def ints(string) -> list[int]: 9 | return list(map(int, re.findall(r"-?[0-9]+", string))) 10 | 11 | 12 | def solve(a: list[int], b: list[int], p: list[int]) -> int | None: 13 | a_count, b_count = Int("a"), Int("b") 14 | opt = Optimize() 15 | for aa, bb, pp in zip(a, b, p): 16 | opt.add(pp == aa * a_count + bb * b_count) 17 | opt.minimize(a_count * 3 + b_count) 18 | if opt.check() == sat: 19 | m = opt.model() 20 | return m[a_count].as_long() * 3 + m[b_count].as_long() 21 | 22 | 23 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 24 | part1 = part2 = 0 25 | 26 | for game in file.read().split("\n\n"): 27 | a, b, p = map(ints, game.splitlines()) 28 | part1 += solve(a, b, p) or 0 29 | part2 += solve(a, b, [x + 10_000_000_000_000 for x in p]) or 0 30 | 31 | print("Part 1:", part1) 32 | print("Part 2:", part2) 33 | -------------------------------------------------------------------------------- /2019/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def run_program(memory, noun, verb): 7 | ip = 0 8 | memory = list(memory) 9 | memory[1] = noun 10 | memory[2] = verb 11 | 12 | while memory[ip] != 99: 13 | opcode, a, b, c = memory[ip : ip + 4] 14 | ip += 4 15 | 16 | if opcode == 1: 17 | memory[c] = memory[a] + memory[b] 18 | elif opcode == 2: 19 | memory[c] = memory[a] * memory[b] 20 | 21 | return memory[0] 22 | 23 | 24 | def main(): 25 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 26 | memory = list(map(int, f.readline().strip().split(","))) 27 | 28 | print("Part 1:", run_program(memory, 12, 2)) 29 | 30 | for noun in range(100): 31 | for verb in range(100): 32 | if run_program(memory, noun, verb) == 19690720: 33 | print("Part 2:", noun * 100 + verb) 34 | return 35 | 36 | 37 | if __name__ == "__main__": 38 | main() 39 | -------------------------------------------------------------------------------- /2021/day07/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def euler_sum(x): 7 | return (x + 1) * x // 2 8 | 9 | 10 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 11 | crabs = list(map(int, f.read().strip().split(","))) 12 | left, right = min(crabs), max(crabs) 13 | part1, part2 = float("inf"), float("inf") 14 | for target in range(left, right + 1): 15 | part1 = min(part1, sum(abs(target - c) for c in crabs)) 16 | part2 = min(part2, sum(euler_sum(abs(target - c)) for c in crabs)) 17 | print("Part 1:", part1) 18 | print("Part 2:", part2) 19 | 20 | from math import ceil 21 | 22 | mean = sum(crabs) / len(crabs) 23 | target = ceil((sum(crabs) - sum(c < mean for c in crabs)) / len(crabs)) 24 | print("Part 2:", sum(euler_sum(abs(target - c)) for c in crabs)) 25 | 26 | # part1 target = median i.e. crabs.sort(); crans[len(crabs)//2] 27 | # part2 target = mean = sum(crabs)/len(crabs); ceil((sum(crabs) - sum(c < mean for c in crabs))/len(crabs)) 28 | -------------------------------------------------------------------------------- /2024/day10/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | grid = [list(map(int, line)) for line in file.read().splitlines()] 9 | width, height = len(grid[0]), len(grid) 10 | starts = [(x, y) for y, row in enumerate(grid) for x, c in enumerate(row) if c == 0] 11 | 12 | part1 = part2 = 0 13 | 14 | for x, y in starts: 15 | pos = {(x, y): 1} 16 | for h in range(9): 17 | new_pos = defaultdict(int) 18 | for (x, y), c in pos.items(): 19 | for dx, dy in ((-1, 0), (1, 0), (0, 1), (0, -1)): 20 | nx, ny = x + dx, y + dy 21 | if 0 <= nx < width and 0 <= ny < height and grid[ny][nx] == h + 1: 22 | new_pos[(nx, ny)] += c 23 | pos = new_pos 24 | 25 | part1 += len(pos) 26 | part2 += sum(pos.values()) 27 | 28 | print("Part 1:", part1) 29 | print("Part 2:", part2) 30 | -------------------------------------------------------------------------------- /2020/optimized/src/utils.rs: -------------------------------------------------------------------------------- 1 | fn thousand_separated(n: u128) -> String { 2 | let string = n.to_string(); 3 | let mut result = String::new(); 4 | let mut place = string.len(); 5 | let mut later_loop = false; 6 | 7 | for ch in string.chars() { 8 | if later_loop && place % 3 == 0 { 9 | result.push(','); 10 | } 11 | 12 | result.push(ch); 13 | later_loop = true; 14 | place -= 1; 15 | } 16 | 17 | result 18 | } 19 | 20 | #[allow(clippy::cast_precision_loss)] 21 | pub fn format_duration(nanos: u128) -> (String, String) { 22 | let nanos_sep = format!("{}ns", thousand_separated(nanos)); 23 | 24 | let other = if nanos > 1_000_000_000 { 25 | format!("{:.2}s", nanos as f64 / 1_000_000_000_f64) 26 | } else if nanos > 1_000_000 { 27 | format!("{:.2}ms", nanos as f64 / 1_000_000_f64) 28 | } else if nanos > 1_000 { 29 | format!("{:.2}us", nanos as f64 / 1_000_f64) 30 | } else { 31 | nanos_sep.clone() 32 | }; 33 | 34 | (nanos_sep, other) 35 | } 36 | -------------------------------------------------------------------------------- /2021/optimized/src/utils.rs: -------------------------------------------------------------------------------- 1 | fn thousand_separated(n: u128) -> String { 2 | let string = n.to_string(); 3 | let mut result = String::new(); 4 | let mut place = string.len(); 5 | let mut later_loop = false; 6 | 7 | for ch in string.chars() { 8 | if later_loop && place % 3 == 0 { 9 | result.push(','); 10 | } 11 | 12 | result.push(ch); 13 | later_loop = true; 14 | place -= 1; 15 | } 16 | 17 | result 18 | } 19 | 20 | #[allow(clippy::cast_precision_loss)] 21 | pub fn format_duration(nanos: u128) -> (String, String) { 22 | let nanos_sep = format!("{}ns", thousand_separated(nanos)); 23 | 24 | let other = if nanos > 1_000_000_000 { 25 | format!("{:.2}s", nanos as f64 / 1_000_000_000_f64) 26 | } else if nanos > 1_000_000 { 27 | format!("{:.2}ms", nanos as f64 / 1_000_000_f64) 28 | } else if nanos > 1_000 { 29 | format!("{:.2}us", nanos as f64 / 1_000_f64) 30 | } else { 31 | nanos_sep.clone() 32 | }; 33 | 34 | (nanos_sep, other) 35 | } 36 | -------------------------------------------------------------------------------- /2025/day08/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from networkx import Graph, connected_components, is_connected 4 | from os import path 5 | import math 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 9 | G = Graph() 10 | 11 | distances = [] 12 | 13 | for line in file.read().splitlines(): 14 | x, y, z = map(int, line.split(",")) 15 | 16 | for xx, yy, zz in G.nodes: 17 | distances.append( 18 | ((x - xx) ** 2 + (y - yy) ** 2 + (z - zz) ** 2, (x, y, z), (xx, yy, zz)) 19 | ) 20 | 21 | G.add_node((x, y, z)) 22 | 23 | distances.sort() 24 | for _, a, b in distances[:1000]: 25 | G.add_edge(a, b) 26 | 27 | components = list(connected_components(G)) 28 | components.sort(key=len, reverse=True) 29 | 30 | print("Part 1:", math.prod(map(len, components[:3]))) 31 | 32 | for _, a, b in distances[1000:]: 33 | G.add_edge(a, b) 34 | if is_connected(G): 35 | print("Part 2:", a[0] * b[0]) 36 | break 37 | -------------------------------------------------------------------------------- /2022/day05/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | 6 | 7 | def ints(string): 8 | return list(map(int, re.findall(r"-?[0-9]+", string))) 9 | 10 | 11 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 12 | lines = iter(f.read().splitlines()) 13 | stacks = [[] for _ in range(10)] 14 | 15 | for line in lines: 16 | if not line: 17 | break 18 | if line.startswith(" 1"): 19 | continue 20 | for i, c in enumerate(line[1::4]): 21 | if not c.isspace(): 22 | stacks[i + 1].insert(0, c) 23 | 24 | stacks2 = [s[:] for s in stacks] 25 | 26 | for line in lines: 27 | count, fro, to = ints(line) 28 | 29 | for _ in range(count): 30 | stacks[to].append(stacks[fro].pop()) 31 | 32 | stacks2[to].extend(stacks2[fro][-count:]) 33 | stacks2[fro] = stacks2[fro][:-count] 34 | 35 | print("Part 1:", "".join(s[-1] for s in stacks[1:])) 36 | print("Part 2:", "".join(s[-1] for s in stacks2[1:])) 37 | -------------------------------------------------------------------------------- /2023/day20/input.txt: -------------------------------------------------------------------------------- 1 | %hs -> sl 2 | &dg -> rx 3 | %vp -> fd, dv 4 | %kz -> jc, mc 5 | %nv -> dv 6 | %hx -> gf 7 | %mm -> vh 8 | %fd -> td 9 | &dv -> hx, bl, rc, fd, xt 10 | %hg -> xq 11 | %td -> dv, hx 12 | %bl -> jt 13 | %br -> jq 14 | %qh -> ln 15 | &xq -> zl, cx, qh, hs, nt, sp 16 | %sg -> vv, tr 17 | %dm -> bl, dv 18 | %gt -> xq, hg 19 | %ln -> mq, xq 20 | %mc -> xv, jc 21 | %tx -> rv, jc 22 | &lk -> dg 23 | %mg -> hl, jc 24 | &vv -> zv, br, kx, mm, tr 25 | %nt -> xq, cx 26 | &zv -> dg 27 | %cd -> jc, ps 28 | %rc -> rm, dv 29 | %nj -> pt, xq 30 | broadcaster -> nt, kx, rc, mg 31 | %gf -> dc, dv 32 | %rm -> dm, dv 33 | %xx -> vv, cz 34 | %jt -> dv, vp 35 | %zl -> nj 36 | &sp -> dg 37 | %xc -> jc, kz 38 | &xt -> dg 39 | %tp -> jc 40 | %lc -> vv, vn 41 | %vh -> xx, vv 42 | %mq -> hs, xq 43 | %cc -> vv 44 | %vn -> vv, cc 45 | %tr -> br 46 | %hl -> qb, jc 47 | %dc -> dv, nv 48 | %jq -> mm, vv 49 | %kx -> vv, sg 50 | %cx -> qh 51 | %sl -> zl, xq 52 | %cz -> lc, vv 53 | %qb -> jc, cd 54 | &jc -> ps, xv, lk, mg 55 | %xv -> tx 56 | %pt -> xq, gt 57 | %rv -> jc, tp 58 | %ps -> xc 59 | -------------------------------------------------------------------------------- /2015/day11/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | 6 | 7 | def valid(pwd): 8 | for a, b, c in zip(pwd, pwd[1:], pwd[2:]): 9 | if ord(a) + 1 == ord(b) == ord(c) - 1: 10 | break 11 | else: 12 | return False 13 | return ( 14 | all(c not in ("i", "o", "l") for c in pwd) 15 | and len(re.findall(r"(.)\1", pwd)) >= 2 16 | ) 17 | 18 | 19 | def nxt(pwd): 20 | while True: 21 | new = "" 22 | inc = True 23 | for c in reversed(pwd): 24 | if inc: 25 | if c == "z": 26 | new = "a" + new 27 | else: 28 | inc = False 29 | new = chr(ord(c) + 1) + new 30 | else: 31 | new = c + new 32 | pwd = new 33 | if valid(pwd): 34 | return pwd 35 | 36 | 37 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 38 | pwd = nxt(f.read().strip()) 39 | print("Part 1:", pwd) 40 | print("Part 2:", nxt(pwd)) 41 | -------------------------------------------------------------------------------- /2021/day05/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | from collections import defaultdict 6 | 7 | 8 | def ints(string): 9 | return map(int, re.findall(r"-?[0-9]+", string)) 10 | 11 | 12 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 13 | grid1 = defaultdict(int) 14 | grid2 = defaultdict(int) 15 | for line in f.read().splitlines(): 16 | x1, y1, x2, y2 = ints(line) 17 | if x1 != x2 and y1 != y2: 18 | xx = range(x1, x2 + 1) if x2 > x1 else range(x1, x2 - 1, -1) 19 | yy = range(y1, y2 + 1) if y2 > y1 else range(y1, y2 - 1, -1) 20 | for x, y in zip(xx, yy): 21 | grid2[(x, y)] += 1 22 | else: 23 | for x in range(min(x1, x2), max(x1, x2) + 1): 24 | for y in range(min(y1, y2), max(y1, y2) + 1): 25 | grid1[(x, y)] += 1 26 | grid2[(x, y)] += 1 27 | 28 | print("Part 1:", sum(c > 1 for c in grid1.values())) 29 | print("Part 2:", sum(c > 1 for c in grid2.values())) 30 | -------------------------------------------------------------------------------- /2020/day08/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | NOP, JMP, ACC = range(3) 7 | 8 | 9 | def run(code): 10 | visited = set() 11 | ip = acc = 0 12 | 13 | while ip not in visited and ip < len(code): 14 | visited.add(ip) 15 | op, arg = code[ip] 16 | 17 | if op == JMP: 18 | ip += arg 19 | else: 20 | if op == ACC: 21 | acc += arg 22 | ip += 1 23 | 24 | return ip >= len(code), acc 25 | 26 | 27 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 28 | code = [] 29 | for line in f: 30 | op, arg = line.split() 31 | code.append([["nop", "jmp", "acc"].index(op), int(arg)]) 32 | 33 | print("Part 1:", run(code)[1]) 34 | 35 | for i, (op, arg) in enumerate(code): 36 | if op != ACC: 37 | code[i][0] = 1 - op 38 | 39 | terminates, acc = run(code) 40 | if terminates: 41 | print("Part 2:", acc) 42 | break 43 | 44 | code[i][0] = op 45 | -------------------------------------------------------------------------------- /2019/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | low, high = map(int, f.readline().strip().split("-")) 8 | 9 | count1 = 0 10 | count2 = 0 11 | 12 | for num in range(low, high + 1): 13 | last = 0 14 | last_count = 0 15 | at_least_double = False 16 | exactly_double = False 17 | 18 | for digit in str(num): 19 | digit = int(digit) 20 | if digit < last: 21 | break 22 | elif digit == last: 23 | at_least_double = True 24 | last_count += 1 25 | else: 26 | if last_count == 2: 27 | exactly_double = True 28 | last_count = 1 29 | last = digit 30 | else: 31 | if at_least_double: 32 | count1 += 1 33 | if exactly_double or last_count == 2: 34 | count2 += 1 35 | 36 | print("Part 1:", count1) 37 | print("Part 2:", count2) 38 | -------------------------------------------------------------------------------- /2019/day08/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | WIDTH = 25 6 | HEIGHT = 6 7 | SIZE = WIDTH * HEIGHT 8 | 9 | BLACK = 0 10 | WHITE = 1 11 | TRANSPARENT = 2 12 | 13 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 14 | line = f.readline().strip() 15 | pixels_iter = iter(line) 16 | 17 | min_zeros = float("inf") 18 | part1 = 0 19 | image = [[TRANSPARENT] * WIDTH for _ in range(HEIGHT)] 20 | 21 | for _ in range(len(line) // SIZE): 22 | count = [0] * 3 23 | 24 | for y in range(HEIGHT): 25 | for x in range(WIDTH): 26 | pixel = int(next(pixels_iter)) 27 | count[pixel] += 1 28 | 29 | if image[y][x] == TRANSPARENT: 30 | image[y][x] = pixel 31 | 32 | if count[0] < min_zeros: 33 | min_zeros = count[0] 34 | part1 = count[1] * count[2] 35 | 36 | print("Part 1:", part1) 37 | print("Part 2:") 38 | 39 | for row in image: 40 | print("".join("\u2588" if c == WHITE else " " for c in row)) 41 | -------------------------------------------------------------------------------- /2021/day13/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | points = set() 8 | lines = iter(f.read().splitlines()) 9 | 10 | for line in lines: 11 | if not line: 12 | break 13 | points.add(tuple(map(int, line.split(",")))) 14 | 15 | for i, line in enumerate(lines): 16 | (*_, dir), p = line.split("=") 17 | p = int(p) 18 | for x, y in tuple(points): 19 | if dir == "x": 20 | if x > p: 21 | points.remove((x, y)) 22 | points.add((2 * p - x, y)) 23 | elif y > p: 24 | points.remove((x, y)) 25 | points.add((x, 2 * p - y)) 26 | 27 | if i == 0: 28 | print("Part 1:", len(points)) 29 | 30 | xmax = max(x for x, _ in points) 31 | ymax = max(y for _, y in points) 32 | 33 | print("Part 2:") 34 | 35 | for y in range(ymax + 1): 36 | print("".join((" ", "█")[(x, y) in points] for x in range(xmax + 1))) 37 | -------------------------------------------------------------------------------- /2019/day22/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def find_ab(m): 7 | a, b = 1, 0 8 | 9 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 10 | for line in f: 11 | parts = line.strip().split() 12 | 13 | if parts[0] == "cut": 14 | index = int(parts[1]) 15 | na, nb = 1, -index 16 | elif parts[1] == "into": 17 | na, nb = -1, -1 18 | else: 19 | inc = int(parts[3]) 20 | na, nb = inc, 0 21 | 22 | a = (a * na) % m 23 | b = (b * na + nb) % m 24 | 25 | return a, b 26 | 27 | 28 | def modinv(x, m): 29 | # if m is prime 30 | return pow(x, m - 2, m) 31 | 32 | 33 | cards = 10007 34 | a, b = find_ab(cards) 35 | print("Part 1:", (a * 2019 + b) % cards) 36 | 37 | cards = 119315717514047 38 | repeat = 101741582076661 39 | a, b = find_ab(cards) 40 | ra = pow(a, repeat, cards) 41 | rb = (b * (ra - 1) * modinv(a - 1, cards)) % cards 42 | print("Part 2:", (2020 - rb) * modinv(ra, cards) % cards) 43 | -------------------------------------------------------------------------------- /2018/day23/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | from z3 import Int, If, Optimize 5 | 6 | 7 | def z3_abs(x): 8 | return If(x < 0, -x, x) 9 | 10 | 11 | def z3_dist(x, y): 12 | return z3_abs(x[0] - y[0]) + z3_abs(x[1] - y[1]) + z3_abs(x[2] - y[2]) 13 | 14 | 15 | def main(): 16 | bots = [] 17 | 18 | with open("input.txt") as f: 19 | for line in f: 20 | bots.append(tuple(map(int, re.findall(r"-?\d+", line)))) 21 | 22 | x, y, z, r = max(bots, key=lambda b: b[3]) 23 | in_range = sum((abs(x - b[0]) + abs(y - b[1]) + abs(z - b[2]) <= r) for b in bots) 24 | print("Part 1:", in_range) 25 | 26 | x, y, z = Int("x"), Int("y"), Int("z") 27 | point = (x, y, z) 28 | count = sum(If(z3_dist(b[:3], point) <= b[3], 1, 0) for b in bots) 29 | 30 | opt = Optimize() 31 | opt.maximize(count) 32 | opt.minimize(z3_dist(point, (0, 0, 0))) 33 | 34 | opt.check() 35 | model = opt.model() 36 | result = model[x].as_long() + model[y].as_long() + model[z].as_long() 37 | 38 | print("Part 2:", result) 39 | 40 | 41 | if __name__ == "__main__": 42 | main() 43 | -------------------------------------------------------------------------------- /2015/day07/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | OPS = { 7 | "AND": lambda a, b: a & b, 8 | "OR": lambda a, b: a | b, 9 | "LSHIFT": lambda a, b: (a << b) & 0xffff, 10 | "RSHIFT": lambda a, b: (a >> b) & 0xffff, 11 | } 12 | 13 | def solve(wire): 14 | if wire in cache: 15 | return cache[wire] 16 | 17 | try: 18 | return int(wire) 19 | except ValueError: 20 | pass 21 | 22 | inp = lines[wire] 23 | 24 | if len(inp) == 1: 25 | res = solve(inp[0]) 26 | elif len(inp) == 2: 27 | res = (~solve(inp[1])) & 0xffff 28 | else: 29 | left, op, right = inp 30 | res = OPS[op](solve(left), solve(right)) 31 | 32 | cache[wire] = res 33 | return res 34 | 35 | 36 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 37 | lines = {} 38 | 39 | for line in f: 40 | a, b = line.strip().split(" -> ") 41 | lines[b] = a.split() 42 | 43 | cache = {} 44 | part1 = solve("a") 45 | print("Part 1:", part1) 46 | 47 | cache = {"b": part1} 48 | print("Part 2:", solve("a")) 49 | -------------------------------------------------------------------------------- /2018/day25/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import defaultdict 4 | 5 | 6 | def dist(a, b): 7 | return sum(abs(a - b) for a, b in zip(a, b)) 8 | 9 | 10 | class Solver: 11 | def solve(self): 12 | points = [] 13 | 14 | with open("input.txt") as f: 15 | for line in f: 16 | points.append(tuple(map(int, line.strip().split(",")))) 17 | 18 | self.connections = {} 19 | 20 | for p in points: 21 | self.connections[p] = {q for q in points if q != p and dist(p, q) <= 3} 22 | 23 | count = 0 24 | self.visited = set() 25 | 26 | for p in points: 27 | if p in self.visited: 28 | continue 29 | 30 | self.visited.add(p) 31 | self.visit(p) 32 | count += 1 33 | 34 | print("Part 1:", count) 35 | 36 | def visit(self, p): 37 | for q in self.connections[p]: 38 | if q not in self.visited: 39 | self.visited.add(q) 40 | self.visit(q) 41 | 42 | 43 | if __name__ == "__main__": 44 | Solver().solve() 45 | -------------------------------------------------------------------------------- /2022/day15/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | from z3 import * 6 | 7 | 8 | def ints(string): 9 | return list(map(int, re.findall(r"-?[0-9]+", string))) 10 | 11 | 12 | def Abs(x): 13 | return If(x >= 0, x, -x) 14 | 15 | 16 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 17 | impossible_x = set() 18 | beacons_in_row = set() 19 | x, y = Ints("x y") 20 | s = Solver() 21 | s.add(x >= 0) 22 | s.add(x <= 4000000) 23 | s.add(y >= 0) 24 | s.add(y <= 4000000) 25 | for line in f.read().splitlines(): 26 | sx, sy, bx, by = ints(line) 27 | d = abs(sx - bx) + abs(sy - by) 28 | s.add(Abs(x - sx) + Abs(y - sy) > d) 29 | extent = d - abs(sy - 2000000) 30 | for xx in range(sx - extent, sx + extent + 1): 31 | impossible_x.add(xx) 32 | if by == 2000000: 33 | beacons_in_row.add(bx) 34 | 35 | print("Part 1:", len(impossible_x - beacons_in_row)) 36 | 37 | assert s.check() == sat, "unsat" 38 | m = s.model() 39 | print("Part 2:", m[x].as_long() * 4000000 + m[y].as_long()) 40 | -------------------------------------------------------------------------------- /2025/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | DIRS = ((0, 1), (0, -1), (1, 0), (-1, 0), (1, 1), (1, -1), (-1, 1), (-1, -1)) 6 | 7 | 8 | def removable(x: int, y: int) -> bool: 9 | if grid[y][x] != "@": 10 | return False 11 | 12 | count = 0 13 | 14 | for dx, dy in DIRS: 15 | nx, ny = x + dx, y + dy 16 | if 0 <= nx < width and 0 <= ny < height and grid[ny][nx] == "@": 17 | count += 1 18 | 19 | return count < 4 20 | 21 | 22 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 23 | grid = list(map(list, file.read().splitlines())) 24 | width, height = len(grid[0]), len(grid) 25 | 26 | print("Part 1:", sum(removable(x, y) for x in range(width) for y in range(height))) 27 | 28 | part2 = 0 29 | changed = True 30 | 31 | while changed: 32 | changed = False 33 | for x in range(height): 34 | for y in range(width): 35 | if removable(x, y): 36 | grid[y][x] = "." 37 | changed = True 38 | part2 += 1 39 | 40 | print("Part 2:", part2) 41 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Benedikt Werner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /2015/day19/input.txt: -------------------------------------------------------------------------------- 1 | Al => ThF 2 | Al => ThRnFAr 3 | B => BCa 4 | B => TiB 5 | B => TiRnFAr 6 | Ca => CaCa 7 | Ca => PB 8 | Ca => PRnFAr 9 | Ca => SiRnFYFAr 10 | Ca => SiRnMgAr 11 | Ca => SiTh 12 | F => CaF 13 | F => PMg 14 | F => SiAl 15 | H => CRnAlAr 16 | H => CRnFYFYFAr 17 | H => CRnFYMgAr 18 | H => CRnMgYFAr 19 | H => HCa 20 | H => NRnFYFAr 21 | H => NRnMgAr 22 | H => NTh 23 | H => OB 24 | H => ORnFAr 25 | Mg => BF 26 | Mg => TiMg 27 | N => CRnFAr 28 | N => HSi 29 | O => CRnFYFAr 30 | O => CRnMgAr 31 | O => HP 32 | O => NRnFAr 33 | O => OTi 34 | P => CaP 35 | P => PTi 36 | P => SiRnFAr 37 | Si => CaSi 38 | Th => ThCa 39 | Ti => BP 40 | Ti => TiTi 41 | e => HF 42 | e => NAl 43 | e => OMg 44 | 45 | CRnCaSiRnBSiRnFArTiBPTiTiBFArPBCaSiThSiRnTiBPBPMgArCaSiRnTiMgArCaSiThCaSiRnFArRnSiRnFArTiTiBFArCaCaSiRnSiThCaCaSiRnMgArFYSiRnFYCaFArSiThCaSiThPBPTiMgArCaPRnSiAlArPBCaCaSiRnFYSiThCaRnFArArCaCaSiRnPBSiRnFArMgYCaCaCaCaSiThCaCaSiAlArCaCaSiRnPBSiAlArBCaCaCaCaSiThCaPBSiThPBPBCaSiRnFYFArSiThCaSiRnFArBCaCaSiRnFYFArSiThCaPBSiThCaSiRnPMgArRnFArPTiBCaPRnFArCaCaCaCaSiRnCaCaSiRnFYFArFArBCaSiThFArThSiThSiRnTiRnPMgArFArCaSiThCaPBCaSiRnBFArCaCaPRnCaCaPMgArSiRnFYFArCaSiThRnPBPMgAr 46 | -------------------------------------------------------------------------------- /2022/day13/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from functools import cmp_to_key 5 | 6 | 7 | def compare(a, b): 8 | if type(a) == int: 9 | if type(b) == int: 10 | return (a > b) - (a < b) 11 | return compare([a], b) 12 | if type(b) == int: 13 | return compare(a, [b]) 14 | for aa, bb in zip(a, b): 15 | if r := compare(aa, bb): 16 | return r 17 | return compare(len(a), len(b)) 18 | 19 | 20 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 21 | inp = f.read() 22 | part1, part2 = 0, 1 23 | 24 | for i, pairs in enumerate(inp.split("\n\n")): 25 | a, b = map(eval, pairs.splitlines()) 26 | if compare(a, b) == -1: 27 | part1 += i + 1 28 | 29 | pairs = [eval(p) for p in inp.splitlines() if p] 30 | pairs.append([[2]]) 31 | pairs.append([[6]]) 32 | pairs.sort(key=cmp_to_key(compare)) 33 | for i, p in enumerate(pairs): 34 | if p == [[2]]: 35 | part2 *= i + 1 36 | if p == [[6]]: 37 | part2 *= i + 1 38 | 39 | print("Part 1:", part1) 40 | print("Part 2:", part2) 41 | -------------------------------------------------------------------------------- /2015/day14/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from dataclasses import dataclass 5 | import re 6 | 7 | 8 | @dataclass 9 | class Reindeer: 10 | fly_speed: int 11 | fly_time: int 12 | rest_time: int 13 | score: int = 0 14 | pos: int = 0 15 | flying: bool = False 16 | left: int = 0 17 | 18 | def step(self): 19 | if self.left == 0: 20 | self.flying = not self.flying 21 | self.left = self.fly_time if self.flying else self.rest_time 22 | if self.flying: 23 | self.pos += self.fly_speed 24 | self.left -= 1 25 | 26 | @staticmethod 27 | def from_line(line): 28 | return Reindeer(*map(int, re.findall(r"-?[0-9]+", line))) 29 | 30 | 31 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 32 | reindeer = [Reindeer.from_line(line) for line in f.read().splitlines()] 33 | 34 | for _ in range(2503): 35 | for r in reindeer: 36 | r.step() 37 | max(reindeer, key=lambda r: r.pos).score += 1 38 | 39 | print("Part 1:", max(r.pos for r in reindeer)) 40 | print("Part 2:", max(r.score for r in reindeer)) 41 | -------------------------------------------------------------------------------- /2022/day08/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 6 | grid = [list(map(int, line)) for line in f.read().splitlines()] 7 | max_score = float("-inf") 8 | visibles = 0 9 | 10 | for y in range(len(grid)): 11 | for x in range(len(grid[0])): 12 | score = 1 13 | visible = False 14 | c = grid[y][x] 15 | for dx, dy in ((0, 1), (0, -1), (1, 0), (-1, 0)): 16 | xx, yy = x, y 17 | dist = 0 18 | while True: 19 | xx += dx 20 | yy += dy 21 | if not (0 <= xx < len(grid[0]) and 0 <= yy < len(grid)): 22 | visible = True 23 | break 24 | dist += 1 25 | if grid[yy][xx] >= c: 26 | break 27 | score *= dist 28 | 29 | max_score = max(max_score, score) 30 | if visible: 31 | visibles += 1 32 | 33 | print("Part 1:", visibles) 34 | print("Part 2:", max_score) 35 | -------------------------------------------------------------------------------- /2024/day14/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import itertools 4 | import math 5 | import re 6 | from os import path 7 | 8 | WIDTH = 101 9 | HEIGHT = 103 10 | 11 | 12 | def ints(string) -> list[int]: 13 | return list(map(int, re.findall(r"-?[0-9]+", string))) 14 | 15 | 16 | def simulate(robots, steps): 17 | positions = [] 18 | for x, y, vx, vy in robots: 19 | x = (x + vx * steps) % WIDTH 20 | y = (y + vy * steps) % HEIGHT 21 | positions.append((x, y)) 22 | return positions 23 | 24 | 25 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 26 | robots = [ints(line) for line in file.read().splitlines()] 27 | 28 | quadrants = [0, 0, 0, 0] 29 | for x, y in simulate(robots, 100): 30 | if x != WIDTH // 2 and y != HEIGHT // 2: 31 | qx = x < WIDTH // 2 32 | qy = y < HEIGHT // 2 33 | quadrants[qx * 2 + qy] += 1 34 | 35 | print("Part 1:", math.prod(quadrants)) 36 | 37 | for i in itertools.count(1): 38 | positions = simulate(robots, i) 39 | if len(positions) == len(set(positions)): 40 | break 41 | 42 | print("Part 2:", i) 43 | -------------------------------------------------------------------------------- /2015/day23/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def solve(instrs, a): 7 | regs = {"a":a, "b":0} 8 | ip = 0 9 | while 0 <= ip < len(instrs): 10 | instr, args = instrs[ip].split(maxsplit=1) 11 | args = args.split(", ") 12 | ip += 1 13 | match (instr, *args): 14 | case ("hlf", r): 15 | regs[r] //= 2 16 | case ("tpl", r): 17 | regs[r] *= 3 18 | case ("inc", r): 19 | regs[r] += 1 20 | case ("jmp", off): 21 | ip += int(off) - 1 22 | case ("jie", r, off): 23 | if regs[r] % 2 == 0: 24 | ip += int(off) - 1 25 | case ("jio", r, off): 26 | if regs[r] == 1: 27 | ip += int(off) - 1 28 | case _: 29 | assert False, f"illegal instruction: {instrs[ip]}" 30 | return regs["b"] 31 | 32 | 33 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 34 | instrs = f.read().splitlines() 35 | print("Part 1:", solve(instrs, 0)) 36 | print("Part 2:", solve(instrs, 1)) 37 | -------------------------------------------------------------------------------- /2024/day07/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import operator 5 | import itertools 6 | from typing import Callable 7 | 8 | 9 | def concat(a: int, b: int) -> int: 10 | return int(str(a) + str(b)) 11 | 12 | 13 | def can_compute( 14 | target: int, nums: list[int], possible_ops: list[Callable[[int, int], int]] 15 | ) -> bool: 16 | for ops in itertools.product(possible_ops, repeat=len(nums) - 1): 17 | curr = nums[0] 18 | for i, op in enumerate(ops, start=1): 19 | curr = op(curr, nums[i]) 20 | if curr == target: 21 | return True 22 | return False 23 | 24 | 25 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 26 | part1 = part2 = 0 27 | for line in file.read().splitlines(): 28 | target, *nums = map(int, line.replace(":", "").split()) 29 | if can_compute(target, nums, [operator.add, operator.mul]): 30 | part1 += target 31 | part2 += target 32 | elif can_compute(target, nums, [operator.add, operator.mul, concat]): 33 | part2 += target 34 | 35 | print("Part 1:", part1) 36 | print("Part 2:", part2) 37 | -------------------------------------------------------------------------------- /2017/day19/sol.py: -------------------------------------------------------------------------------- 1 | #DIRS = ((0, 1), (-1, 0), (0, -1), (1, 0)) 2 | DIRS = ((1, 0), (0, -1), (-1, 0), (0, 1), ) 3 | 4 | with open("input.txt", "r") as f: 5 | maze = [] 6 | for line in f: 7 | maze.append([c for c in line]) 8 | x = 0 9 | y = 0 10 | direction = 0 11 | steps = 0 12 | letters = [] 13 | for i, c in enumerate(maze[0]): 14 | if c == "|": 15 | y = i 16 | break 17 | while True: 18 | curr = maze[x][y] 19 | #print(x, y, curr) 20 | if curr == "+": 21 | for i, d in enumerate(DIRS): 22 | if i == (direction+2)%4: 23 | continue 24 | if maze[x+d[0]][y+d[1]] != " ": 25 | direction = i 26 | break 27 | elif curr == " ": 28 | print("Landed on empty space", x, y) 29 | break 30 | elif curr != "|" and curr != "-": 31 | letters.append(curr) 32 | 33 | steps += 1 34 | x += DIRS[direction][0] 35 | y += DIRS[direction][1] 36 | print(*letters, sep="") 37 | print("Steps:", steps) 38 | -------------------------------------------------------------------------------- /2020/day23/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def play(inp, iters): 7 | # cups[x] is the cup following the cup with value x 8 | cups = [0] * (max(inp) + 1) 9 | for a, b in zip(inp, inp[1:] + inp[:1]): 10 | cups[a] = b 11 | 12 | mod = max(inp) 13 | curr = inp[0] 14 | 15 | for _ in range(iters): 16 | a = cups[curr] 17 | b = cups[a] 18 | c = cups[b] 19 | dest = (curr - 2) % mod + 1 20 | while dest in (a, b, c): 21 | dest = (dest - 2) % mod + 1 22 | last = cups[dest] 23 | cups[dest] = a 24 | cups[curr] = cups[c] 25 | curr = cups[curr] 26 | cups[c] = last 27 | 28 | return cups 29 | 30 | 31 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 32 | cups = list(map(int, f.read().strip())) 33 | 34 | p1 = play(cups, 100) 35 | curr = p1[1] 36 | out = "" 37 | while curr != 1: 38 | out += str(curr) 39 | curr = p1[curr] 40 | print("Part 1:", out) 41 | 42 | cups.extend(range(10, 1_000_001)) 43 | p2 = play(cups, 10_000_000) 44 | print("Part 2:", p2[1] * p2[p2[1]]) 45 | -------------------------------------------------------------------------------- /2025/day06/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import math 4 | from os import path 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | lines = file.read().splitlines() 9 | 10 | part1 = 0 11 | problems = [line.split() for line in lines] 12 | 13 | for i in range(len(problems[0])): 14 | numbers = [int(problems[j][i]) for j in range(len(problems) - 1)] 15 | if problems[-1][i] == "*": 16 | part1 += math.prod(numbers) 17 | else: 18 | part1 += sum(numbers) 19 | 20 | print("Part 1:", part1) 21 | 22 | part2 = 0 23 | curr_op = "" 24 | curr = 0 25 | for i in range(len(lines[0])): 26 | if lines[-1][i] != " ": 27 | part2 += curr 28 | curr_op = lines[-1][i] 29 | curr = 0 if curr_op == "+" else 1 30 | n = 0 31 | for j in range(len(lines) - 1): 32 | if lines[j][i] != " ": 33 | n = n * 10 + int(lines[j][i]) 34 | if n != 0: 35 | if curr_op == "+": 36 | curr += n 37 | else: 38 | curr *= n 39 | 40 | print("Part2:", part2 + curr) 41 | -------------------------------------------------------------------------------- /2019/day07/input.txt: -------------------------------------------------------------------------------- 1 | 3,8,1001,8,10,8,105,1,0,0,21,42,55,64,85,98,179,260,341,422,99999,3,9,101,2,9,9,102,5,9,9,1001,9,2,9,1002,9,5,9,4,9,99,3,9,1001,9,5,9,1002,9,4,9,4,9,99,3,9,101,3,9,9,4,9,99,3,9,1002,9,4,9,101,3,9,9,102,5,9,9,101,4,9,9,4,9,99,3,9,1002,9,3,9,1001,9,3,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,1,9,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1001,9,2,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,101,2,9,9,4,9,99,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,1,9,9,4,9,3,9,101,2,9,9,4,9,3,9,101,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,99,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,2,9,4,9,3,9,1001,9,1,9,4,9,3,9,1001,9,1,9,4,9,3,9,101,1,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,99,3,9,1001,9,1,9,4,9,3,9,102,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,1002,9,2,9,4,9,3,9,101,2,9,9,4,9,3,9,1001,9,1,9,4,9,3,9,1002,9,2,9,4,9,3,9,102,2,9,9,4,9,3,9,102,2,9,9,4,9,99 2 | -------------------------------------------------------------------------------- /2024/day22/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import defaultdict, deque 4 | from os import path 5 | 6 | MOD = 16777216 7 | 8 | 9 | def iterate(n: int) -> int: 10 | n ^= n * 64 11 | n %= MOD 12 | n ^= n // 32 13 | n %= MOD 14 | n ^= n * 2048 15 | n %= MOD 16 | return n 17 | 18 | 19 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 20 | numbers = list(map(int, file.read().splitlines())) 21 | 22 | part1 = 0 23 | for n in numbers: 24 | for _ in range(2000): 25 | n = iterate(n) 26 | part1 += n 27 | print("Part 1:", part1) 28 | 29 | seq_to_income = defaultdict(int) 30 | 31 | for n in numbers: 32 | seen_seqs = set() 33 | seq = deque() 34 | prev_price = n % 10 35 | for _ in range(2000): 36 | n = iterate(n) 37 | price = n % 10 38 | seq.append(price - prev_price) 39 | if len(seq) == 4: 40 | s = tuple(seq) 41 | if s not in seen_seqs: 42 | seq_to_income[s] += price 43 | seen_seqs.add(s) 44 | seq.popleft() 45 | prev_price = price 46 | 47 | print("Part 2:", max(seq_to_income.values())) 48 | -------------------------------------------------------------------------------- /2016/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | MOVES = [ 5 | (0, -1), 6 | (1, 0), 7 | (0, 1), 8 | (-1, 0) 9 | ] 10 | 11 | 12 | def main(): 13 | with open("input.txt") as f: 14 | instructions = f.readline().strip().split(", ") 15 | 16 | x = 0 17 | y = 0 18 | angle = 0 19 | seen = set() 20 | part1_solved = False 21 | part2_solved = False 22 | 23 | while True: 24 | for instr in instructions: 25 | turn, steps = instr[0], int(instr[1:]) 26 | angle = (angle + (1 if turn == "R" else -1)) % 4 27 | xd, yd = MOVES[angle] 28 | 29 | for _ in range(steps): 30 | if not part2_solved and (x, y) in seen: 31 | part2_solved = True 32 | print("Part 2:", abs(x) + abs(y)) 33 | else: 34 | seen.add((x, y)) 35 | 36 | x += xd 37 | y += yd 38 | 39 | if not part1_solved: 40 | part1_solved = True 41 | print("Part 1:", abs(x) + abs(y)) 42 | elif part2_solved: 43 | return 44 | 45 | 46 | if __name__ == "__main__": 47 | main() 48 | -------------------------------------------------------------------------------- /2016/day15/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import itertools 5 | import re 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | disks = [] 10 | 11 | for line in f: 12 | line = line.strip() 13 | mod, start = map(int, re.fullmatch(r"Disc #\d has (\d+) positions; at time=0, it is at position (\d+).", line).groups()) 14 | disks.append([mod, start]) 15 | 16 | last_mod, last_pos = 11, 0 17 | part1_done = False 18 | 19 | for time in itertools.count(): 20 | part1 = True 21 | part2 = True 22 | 23 | for i, disk in enumerate(disks, 1): 24 | mod, pos = disk 25 | 26 | if (pos + i) % mod != 0: 27 | part1 = part2 = False 28 | 29 | disk[1] = (pos + 1) % mod 30 | 31 | if (last_pos + len(disks) + 1) % last_mod != 0: 32 | part2 = False 33 | last_pos = (last_pos + 1) % last_mod 34 | 35 | if not part1_done and part1: 36 | print("Part 1:", time) 37 | part1_done = True 38 | 39 | if part2: 40 | print("Part 2:", time) 41 | break 42 | -------------------------------------------------------------------------------- /2018/day15/input.txt: -------------------------------------------------------------------------------- 1 | ################################ 2 | ##########..#################### 3 | ##########..G################### 4 | ##########..#.....########.##### 5 | ##########........########G##### 6 | ############...#..########.##### 7 | ################....######.##### 8 | #################..G####...##### 9 | ################...#..#....##### 10 | ################...G..#.....E### 11 | ##############.G..........G....# 12 | ###########.G...G..............# 13 | ###########G..#####..........### 14 | ###########..#######.........### 15 | ##########.G#########........#.# 16 | #########...#########....G.....# 17 | #########...#########.........## 18 | ##..........#########.........## 19 | ######....G.#########.....E....# 20 | ##...........#######.......#...# 21 | #...G.........#####E.......##### 22 | ##....................#..####### 23 | ##.G.................##.######## 24 | ##..#GG.............###...#..### 25 | #G..#..G.G........G.####.#..E### 26 | #.....#.##...........###.....### 27 | #######...............###EE..### 28 | ########.....E........###....### 29 | ########..............####..#### 30 | ##########....E....#...###.##### 31 | ###########...EE....#.########## 32 | ################################ 33 | -------------------------------------------------------------------------------- /2017/day02/input.txt: -------------------------------------------------------------------------------- 1 | 1919 2959 82 507 3219 239 3494 1440 3107 259 3544 683 207 562 276 2963 2 | 587 878 229 2465 2575 1367 2017 154 152 157 2420 2480 138 2512 2605 876 3 | 744 6916 1853 1044 2831 4797 213 4874 187 6051 6086 7768 5571 6203 247 285 4 | 1210 1207 1130 116 1141 563 1056 155 227 1085 697 735 192 1236 1065 156 5 | 682 883 187 307 269 673 290 693 199 132 505 206 231 200 760 612 6 | 1520 95 1664 1256 685 1446 253 88 92 313 754 1402 734 716 342 107 7 | 146 1169 159 3045 163 3192 1543 312 161 3504 3346 3231 771 3430 3355 3537 8 | 177 2129 3507 3635 2588 3735 3130 980 324 266 1130 3753 175 229 517 3893 9 | 4532 164 191 5169 4960 3349 3784 3130 5348 5036 2110 151 5356 193 1380 3580 10 | 2544 3199 3284 3009 3400 953 3344 3513 102 1532 161 143 2172 2845 136 2092 11 | 194 5189 3610 4019 210 256 5178 4485 5815 5329 5457 248 5204 4863 5880 3754 12 | 3140 4431 4534 4782 3043 209 216 5209 174 161 3313 5046 1160 160 4036 111 13 | 2533 140 4383 1581 139 141 2151 2104 2753 4524 4712 866 3338 2189 116 4677 14 | 1240 45 254 1008 1186 306 633 1232 1457 808 248 1166 775 1418 1175 287 15 | 851 132 939 1563 539 1351 1147 117 1484 100 123 490 152 798 1476 543 16 | 1158 2832 697 113 121 397 1508 118 2181 2122 809 2917 134 2824 3154 2791 17 | -------------------------------------------------------------------------------- /2021/day25/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | grid = [] 8 | for line in f.read().splitlines(): 9 | grid.append(list(line)) 10 | 11 | changed = True 12 | steps = 0 13 | 14 | while changed: 15 | steps += 1 16 | new = [["." for _ in row] for row in grid] 17 | changed = False 18 | for y, row in enumerate(grid): 19 | for x, c in enumerate(row): 20 | if c == ">" and row[(x + 1) % len(row)] == ".": 21 | new[y][(x + 1) % len(row)] = ">" 22 | changed = True 23 | elif c != ".": 24 | new[y][x] = c 25 | grid = new 26 | new = [["." for _ in row] for row in grid] 27 | for y, row in enumerate(grid): 28 | for x, c in enumerate(row): 29 | if c == "v" and grid[(y + 1) % len(grid)][x] == ".": 30 | new[(y + 1) % len(grid)][x] = "v" 31 | changed = True 32 | elif c != ".": 33 | new[y][x] = c 34 | grid = new 35 | 36 | print("Part 1:", steps) 37 | -------------------------------------------------------------------------------- /2024/day18/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import deque 4 | from os import path 5 | 6 | 7 | WIDTH = HEIGHT = 71 8 | 9 | 10 | def solve(grid: list[list[bool]]) -> int | None: 11 | todo = deque([(0, 0, 0)]) 12 | seen = set() 13 | while todo: 14 | x, y, d = todo.popleft() 15 | if x == WIDTH - 1 and y == HEIGHT - 1: 16 | return d 17 | if (x, y) in seen: 18 | continue 19 | seen.add((x, y)) 20 | for dx, dy in ((0, 1), (0, -1), (1, 0), (-1, 0)): 21 | nx, ny = x + dx, y + dy 22 | if nx in range(WIDTH) and ny in range(HEIGHT) and not grid[nx][ny]: 23 | todo.append((nx, ny, d + 1)) 24 | 25 | return None 26 | 27 | 28 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 29 | grid = [[False for _ in range(WIDTH)] for _ in range(HEIGHT)] 30 | 31 | for i, line in enumerate(file.read().splitlines()): 32 | x, y = map(int, line.split(",")) 33 | grid[y][x] = True 34 | 35 | if i == 1023: 36 | print("Part 1:", solve(grid)) 37 | elif i > 1023 and solve(grid) is None: 38 | print("Part 2:", line) 39 | break 40 | -------------------------------------------------------------------------------- /2024/day23/sol_without_networkx.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import defaultdict 4 | from os import path 5 | 6 | 7 | def expand_cliques(cliques: set[tuple[str, ...]]) -> set[tuple[str, ...]]: 8 | """Given the set of n-size cliques, returns the set of (n+1)-size cliques""" 9 | 10 | new_cliques = set() 11 | for a, *rest in cliques: 12 | for n in graph[a]: 13 | if n not in rest and all(n in graph[b] for b in rest): 14 | new_cliques.add(tuple(sorted((a, n, *rest)))) 15 | return new_cliques 16 | 17 | 18 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 19 | graph = defaultdict(set) 20 | two_cliques = set() 21 | for line in file.read().splitlines(): 22 | a, b = line.split("-") 23 | graph[a].add(b) 24 | graph[b].add(a) 25 | two_cliques.add(tuple(sorted((a, b)))) 26 | 27 | three_cliques = expand_cliques(two_cliques) 28 | print("Part 1:", sum(any(x.startswith("t") for x in cs) for cs in three_cliques)) 29 | 30 | cliques = three_cliques 31 | while len(cliques) > 1: 32 | cliques = expand_cliques(cliques) 33 | 34 | print("Part 2:", ",".join(sorted(cliques.pop()))) 35 | -------------------------------------------------------------------------------- /2016/day12/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | 6 | 7 | REG_NAMES = ("a", "b", "c", "d") 8 | 9 | 10 | def run(instrs, **inits): 11 | regs = defaultdict(int) 12 | ip = 0 13 | 14 | for k, v in inits.items(): 15 | regs[k] = v 16 | 17 | while ip < len(instrs): 18 | instr, *args = instrs[ip] 19 | if instr == "cpy": 20 | x, y = args 21 | if x in REG_NAMES: 22 | regs[y] = regs[x] 23 | else: 24 | regs[y] = int(x) 25 | elif instr == "inc": 26 | regs[args[0]] += 1 27 | elif instr == "dec": 28 | regs[args[0]] -= 1 29 | else: 30 | assert instr == "jnz" 31 | x, target = args 32 | val = regs[x] if x in REG_NAMES else int(x) 33 | if val != 0: 34 | ip += int(target) 35 | continue 36 | ip += 1 37 | 38 | return regs["a"] 39 | 40 | 41 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 42 | instrs = [line.strip().split() for line in f] 43 | print("Part 1:", run(instrs)) 44 | print("Part 2:", run(instrs, c=1)) 45 | -------------------------------------------------------------------------------- /2019/day01/input.txt: -------------------------------------------------------------------------------- 1 | 102111 2 | 81442 3 | 104967 4 | 146349 5 | 97956 6 | 148336 7 | 55752 8 | 110518 9 | 136080 10 | 79361 11 | 101709 12 | 71796 13 | 72543 14 | 88058 15 | 87187 16 | 67778 17 | 75471 18 | 135632 19 | 101309 20 | 67060 21 | 87048 22 | 64775 23 | 52160 24 | 119184 25 | 55284 26 | 83289 27 | 94809 28 | 78017 29 | 117854 30 | 76469 31 | 94504 32 | 92372 33 | 133595 34 | 107269 35 | 57182 36 | 85578 37 | 83777 38 | 91167 39 | 86119 40 | 114263 41 | 60369 42 | 72562 43 | 69544 44 | 92699 45 | 135045 46 | 147230 47 | 83105 48 | 143488 49 | 56494 50 | 63019 51 | 112366 52 | 128119 53 | 58293 54 | 132542 55 | 93266 56 | 76032 57 | 111651 58 | 127490 59 | 107633 60 | 61260 61 | 120876 62 | 116254 63 | 101151 64 | 128722 65 | 111623 66 | 103470 67 | 53224 68 | 93922 69 | 87624 70 | 131864 71 | 82431 72 | 90465 73 | 148373 74 | 90119 75 | 123744 76 | 118713 77 | 143700 78 | 113581 79 | 140687 80 | 119516 81 | 149497 82 | 72281 83 | 108641 84 | 111605 85 | 148274 86 | 69326 87 | 116571 88 | 56540 89 | 87595 90 | 55068 91 | 120602 92 | 56557 93 | 125534 94 | 133162 95 | 51828 96 | 117883 97 | 94637 98 | 54577 99 | 135114 100 | 83866 101 | -------------------------------------------------------------------------------- /2024/day25/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | from os import path 5 | 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | locks = [] 9 | keys = [] 10 | 11 | for schematic in file.read().split("\n\n"): 12 | lines = schematic.splitlines() 13 | height = len(lines) 14 | heights = [] 15 | if all(c == "#" for c in lines[0]): 16 | for x in range(len(lines[0])): 17 | for y in range(height): 18 | if lines[y][x] == ".": 19 | heights.append(y) 20 | break 21 | locks.append(heights) 22 | else: 23 | for x in range(len(lines[0])): 24 | for y in range(height): 25 | if lines[-y - 1][x] == ".": 26 | heights.append(y) 27 | break 28 | keys.append(heights) 29 | 30 | result = 0 31 | 32 | for key in keys: 33 | for lock in locks: 34 | for a, b in zip(key, lock): 35 | if a + b > height: 36 | break 37 | else: 38 | result += 1 39 | 40 | print("Part 1:", result) 41 | -------------------------------------------------------------------------------- /2016/day09/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def decompressed_length(line, start=0, end=None, recurse=True): 7 | count = 0 8 | i = start 9 | if end is None: 10 | end = len(line) 11 | 12 | while i < end: 13 | if line[i] == "(": 14 | marker = "" 15 | while True: 16 | i += 1 17 | if line[i] == ")": 18 | break 19 | marker += line[i] 20 | i += 1 21 | length, repeat = map(int, marker.split("x")) 22 | if recurse: 23 | total_length = decompressed_length(line, i, i + length) 24 | else: 25 | total_length = length 26 | count += total_length * repeat 27 | i += length 28 | else: 29 | count += 1 30 | i += 1 31 | 32 | return count 33 | 34 | 35 | def main(): 36 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 37 | line = f.readline().strip() 38 | 39 | print("Part 1:", decompressed_length(line, recurse=False)) 40 | print("Part 2:", decompressed_length(line)) 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /2021/day24/input_decoded_2.txt: -------------------------------------------------------------------------------- 1 | w = inp 2 | check *= 26 3 | check += w + 1 4 | 5 | w = inp 6 | check *= 26 7 | check += w + 11 8 | 9 | w = inp 10 | check *= 26 11 | check += w + 1 12 | 13 | w = inp 14 | check *= 26 15 | check += w + 11 16 | 17 | w = inp 18 | x = (check % 26) - 8 19 | check /= 26 20 | if x != w: check *= 26 21 | check += (w + 2) * (x != w) 22 | 23 | w = inp 24 | x = (check % 26) - 5 25 | check /= 26 26 | if x != w: check *= 26 27 | check += (w + 9) * (x != w) 28 | 29 | w = inp 30 | check *= 26 31 | check += w + 7 32 | 33 | w = inp 34 | x = (check % 26) - 13 35 | check /= 26 36 | if x != w: check *= 26 37 | check += (w + 11) * (x != w) 38 | 39 | w = inp 40 | check *= 26 41 | check += w + 6 42 | 43 | w = inp 44 | x = (check % 26) - 1 45 | check /= 26 46 | if x != w: check *= 26 47 | check += (w + 15) * (x != w) 48 | 49 | w = inp 50 | check *= 26 51 | check += w + 7 52 | 53 | w = inp 54 | x = (check % 26) - 5 55 | check /= 26 56 | if x != w: check *= 26 57 | check += (w + 1) * (x != w) 58 | 59 | w = inp 60 | x = (check % 26) - 4 61 | check /= 26 62 | if x != w: check *= 26 63 | check += (w + 8) * (x != w) 64 | 65 | w = inp 66 | x = (check % 26) - 8 67 | check /= 26 68 | if x != w: check *= 26 69 | check += (w + 6) * (x != w) 70 | -------------------------------------------------------------------------------- /2016/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | from collections import Counter 5 | 6 | 7 | def decrypt(name, shift): 8 | result = "" 9 | for c in name: 10 | if c == "-": 11 | result += " " 12 | else: 13 | c = (ord(c) + shift % 26) 14 | if c > ord("z"): 15 | c -= 26 16 | result += chr(c) 17 | return result 18 | 19 | 20 | def main(): 21 | rooms = [] 22 | 23 | with open("input.txt") as f: 24 | for line in f: 25 | name, room_id, checksum = re.findall(r"(.*?)-(\d+?)\[(.*?)\]", line.strip())[0] 26 | rooms.append((name, int(room_id), checksum)) 27 | 28 | count = 0 29 | for name, room_id, checksum in rooms: 30 | c = Counter(name.replace("-", "")) 31 | most_common = sorted(c.most_common(), key=lambda x: -x[1] * 1000 + ord(x[0])) 32 | most_common = "".join(x[0] for x in most_common[:5]) 33 | if most_common == checksum: 34 | count += room_id 35 | 36 | name = decrypt(name, room_id) 37 | if "north" in name: 38 | print("Part 2:", name, room_id) 39 | 40 | print("Part 1:", count) 41 | 42 | 43 | if __name__ == "__main__": 44 | main() 45 | -------------------------------------------------------------------------------- /2021/day15/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import heapq 5 | 6 | 7 | def solve(grid): 8 | goalx = max(x for x, _ in grid) 9 | goaly = max(y for _, y in grid) 10 | 11 | todo = [(0, 0, 0)] 12 | visited = set() 13 | while todo: 14 | r, x, y = heapq.heappop(todo) 15 | if x == goalx and y == goaly: 16 | return r 17 | if (x, y) in visited: 18 | continue 19 | visited.add((x, y)) 20 | for xd, yd in ((1, 0), (-1, 0), (0, 1), (0, -1)): 21 | nc = x + xd, y + yd 22 | if nc in grid and nc not in visited: 23 | heapq.heappush(todo, (r + grid[nc], *nc)) 24 | 25 | 26 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 27 | grid = {} 28 | for y, line in enumerate(f.read().splitlines()): 29 | for x, r in enumerate(line): 30 | grid[x, y] = int(r) 31 | 32 | w, h = x + 1, y + 1 33 | 34 | print("Part 1:", solve(grid)) 35 | 36 | grid2 = {} 37 | for (x, y), r in grid.items(): 38 | for dx in range(5): 39 | for dy in range(5): 40 | grid2[x + dx * w, y + dy * h] = (r + dx + dy - 1) % 9 + 1 41 | 42 | print("Part 2:", solve(grid2)) 43 | -------------------------------------------------------------------------------- /2023/day06/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import math 5 | import re 6 | 7 | 8 | def ints(string) -> list[int]: 9 | return list(map(int, re.findall(r"-?[0-9]+", string))) 10 | 11 | 12 | def solve(inp: str): 13 | result = 1 14 | times, dists = map(ints, inp.splitlines()) 15 | for t, d in zip(times, dists): 16 | # brute-force solution 17 | # result *= sum((t - hold_t) * hold_t > d for hold_t in range(t)) 18 | 19 | # mathematical solution. calculate the roots of the quadratic equation. the solution is the range they span. 20 | # d == (t - hold_t) * hold_t 21 | # 0 == (t - hold_t) * hold_t - d 22 | # 0 == t * hold_t - hold_t^2 - d 23 | # 0 == -hold_t^2 + t * hold_t - d 24 | # hold_t = (-t +- sqrt(t^2 - 4 * (-1) * (-d))) / -2 25 | sqrt = math.sqrt(t * t - 4 * d) 26 | a = (-t - sqrt) / -2 27 | b = (-t + sqrt) / -2 28 | a, b = math.floor(min(a, b) + 1), math.ceil(max(a, b) - 1) 29 | result *= b - a + 1 30 | 31 | return result 32 | 33 | 34 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 35 | input = f.read() 36 | print("Part 1:", solve(input)) 37 | print("Part 2:", solve(input.replace(" ", ""))) 38 | -------------------------------------------------------------------------------- /2019/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | DIRS = {"R": (1, 0), "L": (-1, 0), "U": (0, 1), "D": (0, -1)} 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | first_wire = [(c[0], int(c[1:])) for c in f.readline().strip().split(",")] 10 | second_wire = [(c[0], int(c[1:])) for c in f.readline().strip().split(",")] 11 | 12 | first_cords = set() 13 | first_steps = {} 14 | x, y, s = 0, 0, 0 15 | 16 | for (d, a) in first_wire: 17 | xd, yd = DIRS[d] 18 | for _ in range(a): 19 | x += xd 20 | y += yd 21 | s += 1 22 | first_steps[(x, y)] = s 23 | first_cords.add((x, y)) 24 | 25 | intersections = set() 26 | x, y, s = 0, 0, 0 27 | 28 | for (d, a) in second_wire: 29 | xd, yd = DIRS[d] 30 | for _ in range(a): 31 | x += xd 32 | y += yd 33 | s += 1 34 | if (x, y) in first_cords: 35 | intersections.add((x, y, s + first_steps[(x, y)])) 36 | 37 | min_dist = min(map(lambda x: abs(x[0]) + abs(x[1]), intersections)) 38 | print("Part 1:", min_dist) 39 | 40 | min_steps = min(map(lambda x: x[2], intersections)) 41 | print("Part 2:", min_steps) 42 | -------------------------------------------------------------------------------- /2024/day13/sol_no_z3.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | import re 4 | from os import path 5 | 6 | 7 | def ints(string) -> list[int]: 8 | return list(map(int, re.findall(r"-?[0-9]+", string))) 9 | 10 | 11 | def solve(a: list[int], b: list[int], p: list[int]) -> int | None: 12 | # px = ax * a + bx * b 13 | # py = ay * a + by * b 14 | # 15 | # a = (px - bx * b) / ax 16 | # py = ay * ((px - bx * b) / ax) + by * b 17 | # py = ay / ax * (px - bx * b) + by * b 18 | # py = ay / ax * px - ay / ax * bx * b + by * b 19 | # py = ay / ax * px - (ay / ax * bx - by) * b 20 | # b = (ay / ax * px - py) / (ay / ax * bx - by) 21 | ax, ay = a 22 | bx, by = b 23 | px, py = p 24 | bb = round((ay / ax * px - py) / (ay / ax * bx - by)) 25 | aa = (px - bx * bb) // ax 26 | if ax * aa + bx * bb == px and ay * aa + by * bb == py: 27 | return 3 * aa + bb 28 | 29 | 30 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 31 | part1 = part2 = 0 32 | 33 | for game in file.read().split("\n\n"): 34 | a, b, p = map(ints, game.splitlines()) 35 | part1 += solve(a, b, p) or 0 36 | part2 += solve(a, b, [x + 10_000_000_000_000 for x in p]) or 0 37 | 38 | print("Part 1:", part1) 39 | print("Part 2:", part2) 40 | -------------------------------------------------------------------------------- /2023/day18/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | DIRS = { 7 | "R": (1, 0), 8 | "L": (-1, 0), 9 | "U": (0, -1), 10 | "D": (0, 1), 11 | } 12 | 13 | 14 | def solve(instructions): 15 | edges = [] 16 | x, y = 0, 0 17 | for d, length in instructions: 18 | dx, dy = DIRS[d] 19 | x = x + dx * length 20 | y = y + dy * length 21 | edges.append((x, y)) 22 | 23 | # https://en.wikipedia.org/wiki/Shoelace_formula 24 | # https://de.wikipedia.org/wiki/Gau%C3%9Fsche_Trapezformel 25 | area, circumference = 0, 2 26 | for i in range(len(edges)): 27 | area += edges[i][0] * (edges[(i + 1) % len(edges)][1] - edges[i - 1][1]) 28 | circumference += abs(edges[i][0] - edges[i - 1][0]) 29 | circumference += abs(edges[i][1] - edges[i - 1][1]) 30 | return (area + circumference) // 2 31 | 32 | 33 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 34 | part1, part2 = [], [] 35 | for line in file.read().splitlines(): 36 | d, length, hx = line.split() 37 | part1.append((d, int(length))) 38 | hx = hx[2:-1] 39 | part2.append(("RDLU"[int(hx[-1])], int(hx[:-1], 16))) 40 | print("Part 1:", solve(part1)) 41 | print("Part 2:", solve(part2)) 42 | -------------------------------------------------------------------------------- /2021/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 7 | lines = f.read().splitlines() 8 | nums = map(int, lines[0].split(",")) 9 | lines = lines[2:] 10 | 11 | boards = [ 12 | [list(map(int, row.split())) for row in lines[6 * i : 6 * i + 5]] 13 | for i in range(len(lines) // 6) 14 | ] 15 | marked = [{} for _ in boards] 16 | won = set() 17 | for n in nums: 18 | for i, (m, b) in enumerate(zip(marked, boards)): 19 | if i in won: 20 | continue 21 | for y, row in enumerate(b): 22 | for x, c in enumerate(row): 23 | if c == n: 24 | m[(x, y)] = c 25 | if all((x, yy) in m for yy in range(5)) or all( 26 | (xx, y) in m for xx in range(5) 27 | ): 28 | score = (sum(sum(row) for row in b) - sum(m.values())) * n 29 | if not won: 30 | print("Part 1:", score) 31 | won.add(i) 32 | if len(won) == len(boards): 33 | print("Part 2:", score) 34 | -------------------------------------------------------------------------------- /2021/optimized/bare/day01.asm: -------------------------------------------------------------------------------- 1 | section .text 2 | global _start 3 | 4 | _start: 5 | mov rax, [rsp] ; argc 6 | cmp rax, 2 7 | jl bad_exit 8 | 9 | mov rdi, [rsp+0x10] ; argv[1] = input.txt 10 | mov eax, 2 ; sys_open 11 | xor esi, esi ; O_RDONLY 12 | syscall ; open(input.txt, "r") 13 | 14 | cmp rax, -1 15 | je bad_exit 16 | 17 | sub rsp, 144 18 | mov edi, eax ; fd 19 | mov eax, 5 ; sys_fstat 20 | mov rsi, rsp 21 | syscall ; fstat(fd, rsp) 22 | 23 | cmp rax, -1 24 | je bad_exit 25 | 26 | mov rax, 9 ; sys_mmap 27 | mov r8, rdi ; fd 28 | xor edi, edi ; addr = 0 29 | mov rsi, [rsp+48] ; len = fstat.st_size 30 | mov edx, 1 ; PROT_READ 31 | mov r10, 0x08001 ; MAP_SHARED | MAP_POPULATE 32 | xor r9, r9 ; offset = 0 33 | syscall ; mmap(0, fstat.st_size, PROT_READ, MAP_SHARED | MAP_POPULATE, fd, 0) 34 | 35 | cmp rax, -1 36 | je bad_exit 37 | 38 | ; rsi = len 39 | ; rax = input 40 | ; r8, r9, r10, r11 = last 4 numbers 41 | 42 | ; exit 0 43 | xor edi, edi 44 | mov eax, 60 45 | syscall 46 | 47 | bad_exit: 48 | ; exit -1 49 | mov edi, -1 50 | mov eax, 60 51 | syscall 52 | -------------------------------------------------------------------------------- /2022/day21/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from z3 import Solver, Int, ArithRef, sat 5 | 6 | 7 | def evaluate(curr): 8 | if isinstance(curr, ArithRef): 9 | return curr 10 | 11 | try: 12 | return int(curr) 13 | except: 14 | a, op, b = curr.split(" ") 15 | a = evaluate(monkeys[a]) 16 | b = evaluate(monkeys[b]) 17 | if op == "*": 18 | return a * b 19 | if op == "/": 20 | if isinstance(a, int) and isinstance(b, int): 21 | return a // b 22 | return a / b 23 | if op == "+": 24 | return a + b 25 | if op == "-": 26 | return a - b 27 | if op == "=": 28 | return a == b 29 | 30 | assert False 31 | 32 | 33 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 34 | monkeys = [line.split(": ") for line in f.read().splitlines()] 35 | monkeys = {name: equation for name, equation in monkeys} 36 | 37 | print("Part 1:", evaluate(monkeys["root"])) 38 | 39 | s = Solver() 40 | monkeys["humn"] = Int("humn") 41 | s.add(evaluate(monkeys["root"].replace("+", "="))) 42 | assert s.check() == sat 43 | model = s.model() 44 | print("Part 2:", model[monkeys["humn"]].as_long()) 45 | -------------------------------------------------------------------------------- /2024/day08/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import defaultdict 5 | import itertools 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 8 | lines = file.read().splitlines() 9 | width, height = len(lines[0]), len(lines) 10 | antennas = defaultdict(list) 11 | 12 | for y, line in enumerate(lines): 13 | for x, c in enumerate(line): 14 | if c != ".": 15 | antennas[c].append((x, y)) 16 | 17 | part1, part2 = set(), set() 18 | 19 | for points in antennas.values(): 20 | for (x1, y1), (x2, y2) in itertools.combinations(points, 2): 21 | dx, dy = x1 - x2, y1 - y2 22 | 23 | for i in (1, -2): 24 | nx, ny = x1 + i * dx, y1 + i * dy 25 | if 0 <= nx < width and 0 <= ny < height: 26 | part1.add((nx, ny)) 27 | 28 | for d in (1, -1): 29 | for i in itertools.count(step=d): 30 | nx, ny = x1 + i * dx, y1 + i * dy 31 | if 0 <= nx < width and 0 <= ny < height: 32 | part2.add((nx, ny)) 33 | else: 34 | break 35 | 36 | print("Part 1:", len(part1)) 37 | print("Part 2:", len(part2)) 38 | -------------------------------------------------------------------------------- /2016/day02/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | MOVES = { 5 | "U": (0, -1), 6 | "R": (1, 0), 7 | "D": (0, 1), 8 | "L": (-1, 0) 9 | } 10 | 11 | BUTTONS = [[ 12 | [0, 0, 0, 0, 0], 13 | [0, "1", "2", "3", 0], 14 | [0, "4", "5", "6", 0], 15 | [0, "7", "8", "9", 0], 16 | [0, 0, 0, 0, 0], 17 | ], [ 18 | [0, 0, 0, 0, 0, 0, 0], 19 | [0, 0, 0, "1", 0, 0, 0], 20 | [0, 0, "2", "3", "4", 0, 0], 21 | [0, "5", "6", "7", "8", "9", 0], 22 | [0, 0, "A", "B", "C", 0, 0], 23 | [0, 0, 0, "D", 0, 0, 0], 24 | [0, 0, 0, 0, 0, 0, 0], 25 | ]] 26 | 27 | 28 | def main(): 29 | instructions = [] 30 | 31 | with open("input.txt") as f: 32 | instructions = list(f) 33 | 34 | for part in range(2): 35 | x = len(BUTTONS[part]) // 2 + 1 36 | y = len(BUTTONS[part]) // 2 + 1 37 | code = "" 38 | 39 | for line in instructions: 40 | for c in line.strip(): 41 | xd, yd = MOVES[c] 42 | if BUTTONS[part][y + yd][x + xd] != 0: 43 | x += xd 44 | y += yd 45 | code += BUTTONS[part][y][x] 46 | print("Part {}: {}".format(part+1, code)) 47 | 48 | 49 | if __name__ == "__main__": 50 | main() 51 | -------------------------------------------------------------------------------- /2023/day11/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 7 | grid = file.read().splitlines() 8 | 9 | empty_rows = [] 10 | for y, row in enumerate(grid): 11 | if all(c == "." for c in row): 12 | empty_rows.append(y) 13 | 14 | empty_columns = [] 15 | for x in range(len(grid[0])): 16 | if all(grid[y][x] == "." for y in range(len(grid))): 17 | empty_columns.append(x) 18 | 19 | galaxies = [] 20 | for y, row in enumerate(grid): 21 | for x, c in enumerate(row): 22 | if c == "#": 23 | galaxies.append((x, y)) 24 | 25 | part1, part2 = 0, 0 26 | for i, (ax, ay) in enumerate(galaxies): 27 | for bx, by in galaxies[i + 1 :]: 28 | part1 += abs(ax - bx) + abs(ay - by) 29 | part2 += abs(ax - bx) + abs(ay - by) 30 | for y in empty_rows: 31 | if ay < y < by or by < y < ay: 32 | part1 += 1 33 | part2 += 999999 34 | for x in empty_columns: 35 | if ax < x < bx or bx < x < ax: 36 | part1 += 1 37 | part2 += 999999 38 | 39 | print("Part 1:", part1) 40 | print("Part 2:", part2) 41 | -------------------------------------------------------------------------------- /2016/day14/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import hashlib 5 | from collections import deque 6 | 7 | 8 | def md5(index, stretch): 9 | h = hashlib.md5(salt + str(index).encode()).hexdigest() 10 | for _ in range(stretch): 11 | h = hashlib.md5(h.encode()).hexdigest() 12 | return h 13 | 14 | 15 | def triple(s): 16 | for a, b, c in zip(s, s[1:], s[2:]): 17 | if a == b == c: 18 | return a 19 | return None 20 | 21 | 22 | def quint(s, k): 23 | for a, b, c, d, e in zip(s, s[1:], s[2:], s[3:], s[4:]): 24 | if k == a == b == c == d == e: 25 | return True 26 | return False 27 | 28 | 29 | def find(stretch): 30 | index = 0 31 | hashes = deque([md5(i, stretch) for i in range(1000)]) 32 | 33 | for _ in range(64): 34 | while True: 35 | h = hashes.popleft() 36 | hashes.append(md5(index + 1000, stretch)) 37 | trip = triple(h) 38 | index += 1 39 | if trip is not None and any(quint(h, trip) for h in hashes): 40 | break 41 | 42 | return index - 1 43 | 44 | 45 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 46 | salt = f.readline().strip().encode() 47 | 48 | print("Part 1:", find(0)) 49 | print("Part 1:", find(2016)) 50 | -------------------------------------------------------------------------------- /2018/day03/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from collections import defaultdict 4 | 5 | 6 | def parse(line): 7 | split = line.split(" ") 8 | i = split[0][1:] 9 | x, y = split[2][:-1].split(",") 10 | w, h = split[3].split("x") 11 | return map(int, (i, x, y, w, h)) 12 | 13 | 14 | def main(): 15 | squares = defaultdict(lambda: 0) 16 | count = 0 17 | 18 | with open("input.txt") as f: 19 | for line in f: 20 | _, x, y, w, h = parse(line) 21 | for i in range(x, x+w): 22 | for j in range(y, y+h): 23 | cord = (i, j) 24 | if squares[cord] == 1: 25 | count += 1 26 | squares[cord] += 1 27 | 28 | print("Part 1:", count) 29 | 30 | with open("input.txt") as f: 31 | for line in f: 32 | idx, x, y, w, h = parse(line) 33 | overlap = False 34 | for i in range(x, x+w): 35 | for j in range(y, y+h): 36 | if squares[(i, j)] > 1: 37 | overlap = True 38 | break 39 | if overlap: 40 | break 41 | 42 | if not overlap: 43 | print("Part 2:", idx) 44 | 45 | 46 | if __name__ == "__main__": 47 | main() 48 | -------------------------------------------------------------------------------- /2021/day09/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from math import prod 5 | 6 | 7 | def neighbors(coord): 8 | x, y = coord 9 | for xd, yd in ((1, 0), (-1, 0), (0, 1), (0, -1)): 10 | c = x + xd, y + yd 11 | if c in grid: 12 | yield c, grid[c] 13 | 14 | 15 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 16 | grid = {} 17 | 18 | for y, line in enumerate(f.read().splitlines()): 19 | for x, height in enumerate(line): 20 | grid[x, y] = int(height) 21 | 22 | low_points = [ 23 | coord 24 | for coord, height in grid.items() 25 | if all(nh > height for _, nh in neighbors(coord)) 26 | ] 27 | 28 | basins = [] 29 | 30 | for lp in low_points: 31 | todo = [coord for coord, height in neighbors(lp) if height < 9] 32 | done = set(todo) 33 | size = 1 34 | while todo: 35 | coord = todo.pop() 36 | size += 1 37 | height = grid[coord] 38 | for n, nh in neighbors(coord): 39 | if height < nh < 9 and n not in done: 40 | todo.append(n) 41 | done.add(n) 42 | basins.append(size) 43 | 44 | basins.sort() 45 | 46 | print("Part 1:", sum(grid[lp] + 1 for lp in low_points)) 47 | print("Part 2:", prod(basins[-3:])) 48 | -------------------------------------------------------------------------------- /2021/day16/input.txt: -------------------------------------------------------------------------------- 1 | 620D79802F60098803B10E20C3C1007A2EC4C84136F0600BCB8AD0066E200CC7D89D0C4401F87104E094FEA82B0726613C6B692400E14A305802D112239802125FB69FF0015095B9D4ADCEE5B6782005301762200628012E006B80162007B01060A0051801E200528014002A118016802003801E2006100460400C1A001AB3DED1A00063D0E25771189394253A6B2671908020394359B6799529E69600A6A6EB5C2D4C4D764F7F8263805531AA5FE8D3AE33BEC6AB148968D7BFEF2FBD204CA3980250A3C01591EF94E5FF6A2698027A0094599AA471F299EA4FBC9E47277149C35C88E4E3B30043B315B675B6B9FBCCEC0017991D690A5A412E011CA8BC08979FD665298B6445402F97089792D48CF589E00A56FFFDA3EF12CBD24FA200C9002190AE3AC293007A0A41784A600C42485F0E6089805D0CE517E3C493DC900180213D1C5F1988D6802D346F33C840A0804CB9FE1CE006E6000844528570A40010E86B09A32200107321A20164F66BAB5244929AD0FCBC65AF3B4893C9D7C46401A64BA4E00437232D6774D6DEA51CE4DA88041DF0042467DCD28B133BE73C733D8CD703EE005CADF7D15200F32C0129EC4E7EB4605D28A52F2C762BEA010C8B94239AAF3C5523CB271802F3CB12EAC0002FC6B8F2600ACBD15780337939531EAD32B5272A63D5A657880353B005A73744F97D3F4AE277A7DA8803C4989DDBA802459D82BCF7E5CC5ED6242013427A167FC00D500010F8F119A1A8803F0C62DC7D200CAA7E1BC40C7401794C766BB3C58A00845691ADEF875894400C0CFA7CD86CF8F98027600ACA12495BF6FFEF20691ADE96692013E27A3DE197802E00085C6E8F30600010882B18A25880352D6D5712AE97E194E4F71D279803000084C688A71F440188FB0FA2A8803D0AE31C1D200DE25F3AAC7F1BA35802B3BE6D9DF369802F1CB401393F2249F918800829A1B40088A54F25330B134950E0 2 | -------------------------------------------------------------------------------- /2022/day09/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def simulate(moves, length): 7 | pos = [[0, 0] for _ in range(length)] 8 | s = set() 9 | for line in moves: 10 | d, v = line.split() 11 | for _ in range(int(v)): 12 | s.add(tuple(pos[-1])) 13 | if d == "D": 14 | pos[0][1] += 1 15 | elif d == "U": 16 | pos[0][1] -= 1 17 | elif d == "L": 18 | pos[0][0] -= 1 19 | elif d == "R": 20 | pos[0][0] += 1 21 | for i, ((hx, hy), (tx, ty)) in enumerate(zip(pos, pos[1:])): 22 | if abs(hx - tx) > 1: 23 | tx += 1 if hx > tx else -1 24 | if abs(hy - ty) > 0: 25 | ty += 1 if hy > ty else -1 26 | elif abs(hy - ty) > 1: 27 | ty += 1 if hy > ty else -1 28 | if abs(hx - tx) > 0: 29 | tx += 1 if hx > tx else -1 30 | pos[i + 1][0] = tx 31 | pos[i + 1][1] = ty 32 | 33 | s.add(tuple(pos[-1])) 34 | 35 | return len(s) 36 | 37 | 38 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 39 | inp = f.read().splitlines() 40 | print("Part 1:", simulate(inp, 2)) 41 | print("Part 2:", simulate(inp, 10)) 42 | -------------------------------------------------------------------------------- /2024/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 7 | grid = file.read().splitlines() 8 | width, height = len(grid[0]), len(grid) 9 | 10 | part1 = 0 11 | for dx, dy in ((0, 1), (1, 0), (1, 1), (1, -1)): 12 | for x in range(width): 13 | for y in range(height): 14 | if x + 3 * dx not in range(width) or y + 3 * dy not in range(height): 15 | continue 16 | if all(grid[y + i * dy][x + i * dx] == c for i, c in enumerate("XMAS")): 17 | part1 += 1 18 | if all(grid[y + i * dy][x + i * dx] == c for i, c in enumerate("SAMX")): 19 | part1 += 1 20 | print("Part 1:", part1) 21 | 22 | part2 = 0 23 | for x in range(1, width - 1): 24 | for y in range(1, height - 1): 25 | if ( 26 | grid[y][x] == "A" 27 | and grid[y - 1][x - 1] in "MS" 28 | and grid[y + 1][x + 1] in "MS" 29 | and grid[y - 1][x - 1] != grid[y + 1][x + 1] 30 | and grid[y - 1][x + 1] in "MS" 31 | and grid[y + 1][x - 1] in "MS" 32 | and grid[y - 1][x + 1] != grid[y + 1][x - 1] 33 | ): 34 | part2 += 1 35 | print("Part 2:", part2) 36 | -------------------------------------------------------------------------------- /2020/day21/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from functools import reduce 5 | from collections import Counter 6 | 7 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 8 | could_be_in = {} 9 | count = Counter() 10 | for line in f.read().splitlines(): 11 | ingredients, allergens = line.split(" (contains ") 12 | ingredients = set(ingredients.split()) 13 | allergens = allergens[:-1].split(", ") 14 | 15 | count.update(ingredients) 16 | 17 | for a in allergens: 18 | if a in could_be_in: 19 | could_be_in[a] &= ingredients 20 | else: 21 | could_be_in[a] = set(ingredients) 22 | 23 | could_be_alergic = reduce(set.__or__, could_be_in.values(), set()) 24 | print("Part 1:", sum(c for i, c in count.items() if i not in could_be_alergic)) 25 | 26 | mapping = [] 27 | while could_be_in: 28 | new = [] 29 | for a, ii in could_be_in.items(): 30 | if len(ii) == 1: 31 | new.append((a, ii.pop())) 32 | 33 | for a, i in new: 34 | del could_be_in[a] 35 | for v in could_be_in.values(): 36 | if i in v: 37 | v.remove(i) 38 | 39 | mapping += new 40 | 41 | mapping.sort() 42 | print("Part 2:", ",".join(i for _, i in mapping)) 43 | -------------------------------------------------------------------------------- /2020/day12/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | DIRECTIONS = {d: i for i, d in enumerate(["N", "E", "S", "W"])} 7 | MOVE = [(0, -1), (1, 0), (0, 1), (-1, 0)] 8 | ROTX = [(1, 0), (0, -1), (-1, 0), (0, 1)] 9 | ROTY = [(0, 1), (1, 0), (0, -1), (-1, 0)] 10 | 11 | 12 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 13 | x, y, x2, y2, d = 0, 0, 0, 0, DIRECTIONS["E"] 14 | wpx, wpy = 10, -1 15 | 16 | for line in f: 17 | op, arg = line[0], int(line[1:]) 18 | 19 | # Part 1 20 | if op == "R": 21 | d = (d + arg // 90) % 4 22 | elif op == "L": 23 | d = (d - arg // 90) % 4 24 | else: 25 | dx, dy = MOVE[DIRECTIONS.get(op, d)] 26 | x += dx * arg 27 | y += dy * arg 28 | 29 | # Part 2 30 | if op in DIRECTIONS: 31 | dx, dy = MOVE[DIRECTIONS[op]] 32 | wpx += dx * arg 33 | wpy += dy * arg 34 | elif op == "F": 35 | x2 += wpx * arg 36 | y2 += wpy * arg 37 | else: 38 | rot = arg // 90 if op == "R" else ((-arg) % 360) // 90 39 | rotx, roty = ROTX[rot], ROTY[rot] 40 | wpx, wpy = wpx * rotx[0] + wpy * rotx[1], wpx * roty[0] + wpy * roty[1] 41 | 42 | print("Part 1:", abs(x) + abs(y)) 43 | print("Part 2:", abs(x2) + abs(y2)) 44 | -------------------------------------------------------------------------------- /2020/day14/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import itertools as iter 5 | import re 6 | 7 | 8 | def powerset(xs): 9 | return iter.chain.from_iterable(iter.combinations(xs, r) for r in range(len(xs) + 1)) 10 | 11 | 12 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 13 | mem1 = {} 14 | mem2 = {} 15 | 16 | for line in f.read().splitlines(): 17 | if line.startswith("mask"): 18 | mask = re.fullmatch(r"mask = ([X10]{36})", line).groups()[0] 19 | 20 | or_mask1 = int(mask.replace("X", "0"), 2) 21 | and_mask1 = int(mask.replace("X", "1"), 2) 22 | 23 | or_mask2 = int(mask.replace("X", "0"), 2) 24 | and_mask2 = int(mask.replace("0", "1").replace("X", "0"), 2) 25 | floating = [35 - i for i, c in enumerate(mask) if c == "X"] 26 | else: 27 | addr, val = map(int, re.fullmatch(r"mem\[(\d+)] = (\d+)", line).groups()) 28 | 29 | mem1[addr] = (val | or_mask1) & and_mask1 30 | 31 | addr &= and_mask2 32 | addr |= or_mask2 33 | for comb in powerset(floating): 34 | new_addr = addr 35 | for i in comb: 36 | new_addr |= 1 << i 37 | mem2[new_addr] = val 38 | 39 | print("Part 1:", sum(mem1.values())) 40 | print("Part 2:", sum(mem2.values())) 41 | -------------------------------------------------------------------------------- /2022/day18/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | def free_neighbors(point): 7 | for i in range(3): 8 | for d in (-1, 1): 9 | p = list(point) 10 | p[i] += d 11 | p = tuple(p) 12 | if p not in drops: 13 | yield p, i 14 | 15 | 16 | def floodfill(cc): 17 | todo = [cc] 18 | connected = set() 19 | while todo: 20 | c = todo.pop() 21 | for n, i in free_neighbors(c): 22 | if n[i] < mins[i] or n[i] > maxs[i]: 23 | outside.update(connected) 24 | return True 25 | if n not in connected: 26 | connected.add(n) 27 | todo.append(n) 28 | pockets.update(connected) 29 | return False 30 | 31 | 32 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 33 | drops = set(tuple(map(int, line.split(","))) for line in f.read().splitlines()) 34 | part1, part2 = 0, 0 35 | mins = [min(c[i] for c in drops) for i in range(3)] 36 | maxs = [max(c[i] for c in drops) for i in range(3)] 37 | pockets, outside = set(), set() 38 | for c in drops: 39 | for n, _ in free_neighbors(c): 40 | part1 += 1 41 | if n not in pockets and (n in outside or floodfill(n)): 42 | part2 += 1 43 | print("Part 1:", part1) 44 | print("Part 2:", part2) 45 | -------------------------------------------------------------------------------- /2021/day14/input.txt: -------------------------------------------------------------------------------- 1 | FSKBVOSKPCPPHVOPVFPC 2 | 3 | BV -> O 4 | OS -> P 5 | KP -> P 6 | VK -> S 7 | FS -> C 8 | OK -> P 9 | KC -> S 10 | HV -> F 11 | HC -> K 12 | PF -> N 13 | NK -> F 14 | SC -> V 15 | CO -> K 16 | PO -> F 17 | FB -> P 18 | CN -> K 19 | KF -> N 20 | NH -> S 21 | SF -> P 22 | HP -> P 23 | NP -> F 24 | OV -> O 25 | OP -> P 26 | HH -> C 27 | FP -> P 28 | CS -> O 29 | SK -> O 30 | NS -> F 31 | SN -> S 32 | SP -> H 33 | BH -> B 34 | NO -> O 35 | CB -> N 36 | FO -> N 37 | NC -> C 38 | VF -> N 39 | CK -> C 40 | PC -> H 41 | BP -> B 42 | NF -> O 43 | BB -> C 44 | VN -> K 45 | OH -> K 46 | CH -> F 47 | VB -> N 48 | HO -> P 49 | FH -> K 50 | PK -> H 51 | CC -> B 52 | VH -> B 53 | BF -> N 54 | KS -> V 55 | PV -> B 56 | CP -> N 57 | PB -> S 58 | VP -> V 59 | BO -> B 60 | HS -> H 61 | BS -> F 62 | ON -> B 63 | HB -> K 64 | KH -> B 65 | PP -> H 66 | BN -> C 67 | BC -> F 68 | KV -> K 69 | VO -> P 70 | SO -> V 71 | OF -> O 72 | BK -> S 73 | PH -> V 74 | SV -> F 75 | CV -> H 76 | OB -> N 77 | SS -> H 78 | VV -> B 79 | OO -> V 80 | CF -> H 81 | KB -> F 82 | NV -> B 83 | FV -> V 84 | HK -> P 85 | VS -> P 86 | FF -> P 87 | HN -> N 88 | FN -> F 89 | OC -> K 90 | SH -> V 91 | KO -> C 92 | HF -> B 93 | PN -> N 94 | SB -> F 95 | VC -> B 96 | FK -> S 97 | KK -> N 98 | FC -> F 99 | NN -> P 100 | NB -> V 101 | PS -> S 102 | KN -> S 103 | -------------------------------------------------------------------------------- /2023/day15/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | 6 | 7 | def hash(inp: str) -> int: 8 | result = 0 9 | for c in inp: 10 | result += ord(c) 11 | result *= 17 12 | result %= 256 13 | return result 14 | 15 | 16 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 17 | steps = file.read().strip().split(",") 18 | 19 | print("Part 1:", sum(hash(step) for step in steps)) 20 | 21 | boxes = [[] for _ in range(256)] 22 | for step in steps: 23 | label, op, focus = re.findall(r"([a-z]+)(=|-)([0-9]*)", step)[0] 24 | box = boxes[hash(label)] 25 | match op: 26 | case "-": 27 | for i, lens in enumerate(box): 28 | if lens[0] == label: 29 | box.pop(i) 30 | break 31 | case "=": 32 | for i, lens in enumerate(box): 33 | if lens[0] == label: 34 | box[i] = (label, focus) 35 | break 36 | else: 37 | box.append((label, focus)) 38 | case _: 39 | assert False 40 | 41 | result = 0 42 | for i, box in enumerate(boxes, 1): 43 | for j, (label, focus) in enumerate(box, 1): 44 | result += i * j * int(focus) 45 | print("Part 2:", result) 46 | -------------------------------------------------------------------------------- /2022/day14/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from itertools import count 5 | 6 | 7 | def simulate(grid): 8 | max_y = max(y for _, y in grid) 9 | for i in count(): 10 | x, y = 500, 0 11 | while True: 12 | if y == max_y: 13 | return i 14 | elif (x, y + 1) not in grid: 15 | y += 1 16 | elif (x - 1, y + 1) not in grid: 17 | x -= 1 18 | y += 1 19 | elif (x + 1, y + 1) not in grid: 20 | x += 1 21 | y += 1 22 | elif y == 0: 23 | return i + 1 24 | else: 25 | grid.add((x, y)) 26 | break 27 | 28 | 29 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 30 | grid = set() 31 | for line in f.read().splitlines(): 32 | points = [list(map(int, p.split(","))) for p in line.split(" -> ")] 33 | for (x1, y1), (x2, y2) in zip(points, points[1:]): 34 | for x in range(min(x1, x2), max(x1, x2) + 1): 35 | for y in range(min(y1, y2), max(y1, y2) + 1): 36 | grid.add((x, y)) 37 | 38 | print("Part 1:", simulate(grid.copy())) 39 | 40 | max_y = max(y for _, y in grid) 41 | for x in range(-100000, 100000): 42 | grid.add((x, max_y + 2)) 43 | 44 | print("Part 2:", simulate(grid)) 45 | -------------------------------------------------------------------------------- /2023/day25/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import * 5 | 6 | import networkx as nx 7 | import matplotlib.pyplot as plt 8 | from copy import deepcopy 9 | from tqdm import tqdm 10 | import itertools 11 | import math 12 | import re 13 | import pyperclip 14 | 15 | 16 | def ints(string) -> list[int]: 17 | return list(map(int, re.findall(r"-?[0-9]+", string))) 18 | 19 | 20 | def solve(inp: str): 21 | graph = defaultdict(set) 22 | G = nx.Graph() 23 | for line in inp.splitlines(): 24 | start, ends = line.split(": ") 25 | ends = ends.split() 26 | for end in ends: 27 | graph[start].add(end) 28 | graph[end].add(start) 29 | G.add_edge(start, end) 30 | G.remove_edge("pmn", "kdc") 31 | G.remove_edge("hvm", "grd") 32 | G.remove_edge("zfk", "jmn") 33 | return math.prod(map(len, nx.connected_components(G))) 34 | # pos = nx.spring_layout(G) 35 | # nx.draw_networkx(G) 36 | # plt.show() 37 | 38 | 39 | example = """\ 40 | 41 | """ 42 | 43 | if example and not example.isspace(): 44 | print("Example:", solve(example)) 45 | else: 46 | print("No example provided") 47 | 48 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 49 | result = solve(file.read()) 50 | print("Output:", result) 51 | pyperclip.copy(str(result)) 52 | print("Copied to clipboard") 53 | -------------------------------------------------------------------------------- /2020/day04/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | 6 | 7 | REQUIREMENTS = [ 8 | ("byr", lambda x: 1920 <= int(x) <= 2002), 9 | ("iyr", lambda x: 2010 <= int(x) <= 2020), 10 | ("eyr", lambda x: 2020 <= int(x) <= 2030), 11 | ( 12 | "hgt", 13 | lambda x: (x[-2:] == "cm" and 150 <= int(x[:-2]) <= 193) 14 | or (x[-2:] == "in" and 59 <= int(x[:-2]) <= 76), 15 | ), 16 | ("hcl", lambda x: re.fullmatch(r"#[0-9a-f]{6}", x)), 17 | ("ecl", lambda x: x in ("amb", "blu", "brn", "gry", "grn", "hzl", "oth")), 18 | ("pid", lambda x: re.fullmatch(r"[0-9]{9}", x)), 19 | ] 20 | 21 | 22 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 23 | part1 = part2 = 0 24 | 25 | for passport_lines in f.read().split("\n\n"): 26 | passport = {} 27 | 28 | for parts in passport_lines.split(): 29 | key, value = parts.split(":") 30 | passport[key] = value 31 | 32 | valid1 = True 33 | valid2 = True 34 | 35 | for key, req in REQUIREMENTS: 36 | if key not in passport: 37 | valid1 = False 38 | break 39 | 40 | if not req(passport[key]): 41 | valid2 = False 42 | 43 | if valid1: 44 | part1 += 1 45 | part2 += valid2 46 | 47 | print("Part 1:", part1) 48 | print("Part 2:", part2) 49 | -------------------------------------------------------------------------------- /2016/day17/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from collections import deque 5 | from hashlib import md5 6 | 7 | 8 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 9 | passcode = f.read().strip().encode() 10 | todo = deque([(0, 0, b"")]) 11 | shortest, longest = None, 0 12 | 13 | while todo: 14 | x, y, path = todo.popleft() 15 | h = md5(passcode + path).hexdigest()[:4] 16 | if x > 0 and int(h[2], 16) > 10: 17 | todo.append((x - 1, y, path + b"L")) 18 | if x < 3 and int(h[3], 16) > 10: 19 | if x == 2 and y == 3: 20 | if shortest is None: 21 | shortest = path.decode() + "R" 22 | elif len(path) + 1 > longest: 23 | longest = len(path) + 1 24 | else: 25 | todo.append((x + 1, y, path + b"R")) 26 | if y > 0 and int(h[0], 16) > 10: 27 | todo.append((x, y - 1, path + b"U")) 28 | if y < 3 and int(h[1], 16) > 10: 29 | if x == 3 and y == 2: 30 | if shortest is None: 31 | shortest = path.decode() + "D" 32 | elif len(path) + 1 > longest: 33 | longest = len(path) + 1 34 | else: 35 | todo.append((x, y + 1, path + b"D")) 36 | 37 | print("Part 1:", shortest) 38 | print("Part 2:", longest) 39 | -------------------------------------------------------------------------------- /2018/day08/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | 4 | from collections import defaultdict 5 | 6 | 7 | class Node: 8 | def __init__(self, children, metadata): 9 | self.children = children 10 | self.metadata = metadata 11 | 12 | def sum_metadata(self): 13 | total = sum(self.metadata) 14 | for child in self.children: 15 | total += child.sum_metadata() 16 | return total 17 | 18 | def value(self): 19 | if not self.children: 20 | return sum(self.metadata) 21 | 22 | total = 0 23 | for m in self.metadata: 24 | if 0 < m <= len(self.children): 25 | total += self.children[m-1].value() 26 | return total 27 | 28 | 29 | def parse(line, start=0): 30 | children_count, metadata_count = line[start:start+2] 31 | children = [] 32 | start += 2 33 | for _ in range(children_count): 34 | start, child = parse(line, start) 35 | children.append(child) 36 | metadata = line[start:start+metadata_count] 37 | return start+metadata_count, Node(children, metadata) 38 | 39 | 40 | def main(): 41 | with open("input.txt") as f: 42 | numbers = [int(x) for x in f.readline().strip().split(" ")] 43 | _, root = parse(numbers) 44 | 45 | print("Part 1:", root.sum_metadata()) 46 | print("Part 2:", root.value()) 47 | 48 | 49 | if __name__ == "__main__": 50 | main() 51 | -------------------------------------------------------------------------------- /2023/day12/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from functools import cache 5 | 6 | 7 | @cache 8 | def arrangements(symbols, numbers, i=0): 9 | if not numbers: 10 | if not any(s == "#" for s in symbols[i:]): 11 | return 1 12 | return 0 13 | 14 | nxt = numbers[0] 15 | 16 | while True: 17 | if i + nxt > len(symbols): 18 | return 0 19 | can_place_here = all(c in "#?" for c in symbols[i : i + nxt]) 20 | can_leave_next_empty = i + nxt >= len(symbols) or symbols[i + nxt] != "#" 21 | if can_place_here and can_leave_next_empty: 22 | count = arrangements(symbols, numbers[1:], i + nxt + 1) 23 | if symbols[i] == "?": 24 | count += arrangements(symbols, numbers, i + 1) 25 | return count 26 | if symbols[i] == "#": 27 | return 0 28 | i += 1 29 | 30 | 31 | with open(path.join(path.dirname(__file__), "input.txt")) as file: 32 | part1, part2 = 0, 0 33 | 34 | for line in file.read().splitlines(): 35 | symbols, numbers = line.split() 36 | numbers = tuple(map(int, numbers.split(","))) 37 | 38 | part1 += arrangements(symbols, numbers) 39 | 40 | symbols = "?".join(5 * [symbols]) 41 | numbers *= 5 42 | part2 += arrangements(symbols, numbers) 43 | 44 | print("Part 1:", part1) 45 | print("Part 2:", part2) 46 | -------------------------------------------------------------------------------- /2019/day19/input.txt: -------------------------------------------------------------------------------- 1 | 109,424,203,1,21102,11,1,0,1106,0,282,21102,18,1,0,1106,0,259,1201,1,0,221,203,1,21102,31,1,0,1106,0,282,21101,38,0,0,1106,0,259,21002,23,1,2,22102,1,1,3,21101,1,0,1,21102,57,1,0,1105,1,303,2102,1,1,222,20101,0,221,3,20101,0,221,2,21102,259,1,1,21101,80,0,0,1106,0,225,21101,0,44,2,21102,91,1,0,1105,1,303,1201,1,0,223,20101,0,222,4,21101,0,259,3,21102,225,1,2,21101,225,0,1,21102,118,1,0,1105,1,225,21002,222,1,3,21101,100,0,2,21101,133,0,0,1105,1,303,21202,1,-1,1,22001,223,1,1,21101,148,0,0,1106,0,259,2102,1,1,223,20102,1,221,4,21002,222,1,3,21102,1,12,2,1001,132,-2,224,1002,224,2,224,1001,224,3,224,1002,132,-1,132,1,224,132,224,21001,224,1,1,21102,1,195,0,106,0,108,20207,1,223,2,21002,23,1,1,21102,-1,1,3,21101,0,214,0,1105,1,303,22101,1,1,1,204,1,99,0,0,0,0,109,5,2102,1,-4,249,21201,-3,0,1,22101,0,-2,2,22101,0,-1,3,21101,0,250,0,1105,1,225,22102,1,1,-4,109,-5,2106,0,0,109,3,22107,0,-2,-1,21202,-1,2,-1,21201,-1,-1,-1,22202,-1,-2,-2,109,-3,2106,0,0,109,3,21207,-2,0,-1,1206,-1,294,104,0,99,21202,-2,1,-2,109,-3,2105,1,0,109,5,22207,-3,-4,-1,1206,-1,346,22201,-4,-3,-4,21202,-3,-1,-1,22201,-4,-1,2,21202,2,-1,-1,22201,-4,-1,1,22102,1,-2,3,21101,0,343,0,1105,1,303,1105,1,415,22207,-2,-3,-1,1206,-1,387,22201,-3,-2,-3,21202,-2,-1,-1,22201,-3,-1,3,21202,3,-1,-1,22201,-3,-1,2,21201,-4,0,1,21101,0,384,0,1106,0,303,1106,0,415,21202,-4,-1,-4,22201,-4,-3,-4,22202,-3,-2,-2,22202,-2,-4,-4,22202,-3,-2,-3,21202,-4,-1,-2,22201,-3,-2,1,22102,1,1,-4,109,-5,2106,0,0 2 | -------------------------------------------------------------------------------- /2021/day21/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | from functools import lru_cache 5 | 6 | 7 | @lru_cache(maxsize=None) 8 | def wins(scores, positions, turn): 9 | if scores[0] >= 21: 10 | return 1, 0 11 | if scores[1] >= 21: 12 | return 0, 1 13 | 14 | wins1, wins2 = 0, 0 15 | for i in range(3): 16 | p = turn // 3 17 | pos = (positions[p] + i) % 10 + 1 18 | score = scores[p] + (turn % 3 == 2 and pos) 19 | w1, w2 = wins( 20 | (scores[0], score) if p == 1 else (score, scores[1]), 21 | (positions[0], pos) if p == 1 else (pos, positions[1]), 22 | (turn + 1) % 6, 23 | ) 24 | wins1 += w1 25 | wins2 += w2 26 | return wins1, wins2 27 | 28 | 29 | def part1(pos): 30 | pos = list(pos) 31 | scores = [0, 0] 32 | throws = 0 33 | while True: 34 | p = (throws // 3) % 2 35 | for _ in range(3): 36 | throw = throws % 100 + 1 37 | pos[p] = (pos[p] + throw - 1) % 10 + 1 38 | throws += 1 39 | scores[p] += pos[p] 40 | if scores[p] >= 1000: 41 | return scores[1 - p] * throws 42 | 43 | 44 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 45 | positions = tuple([int(line.split()[-1]) for line in f.read().splitlines()]) 46 | print("Part 1:", part1(positions)) 47 | print("Part 2:", max(wins((0, 0), positions, 0))) 48 | -------------------------------------------------------------------------------- /2021/day11/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import itertools 5 | 6 | 7 | def neighbors(coord): 8 | x, y = coord 9 | for xd in (-1, 0, 1): 10 | for yd in (-1, 0, 1): 11 | if xd == 0 and yd == 0: 12 | continue 13 | c = x + xd, y + yd 14 | if c in grid: 15 | yield c 16 | 17 | 18 | def try_flash(k): 19 | if grid[k] <= 9 or k in flashed: 20 | return 21 | 22 | flashed.add(k) 23 | 24 | for n in neighbors(k): 25 | grid[n] += 1 26 | try_flash(n) 27 | 28 | 29 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 30 | grid = {} 31 | 32 | for y, line in enumerate(f.read().splitlines()): 33 | for x, c in enumerate(line): 34 | grid[x, y] = int(c) 35 | 36 | part1 = 0 37 | part2 = None 38 | 39 | for i in itertools.count(1): 40 | flashed = set() 41 | 42 | for k in grid: 43 | grid[k] += 1 44 | 45 | for k in grid: 46 | try_flash(k) 47 | 48 | for k, v in grid.items(): 49 | if v > 9: 50 | grid[k] = 0 51 | 52 | if i <= 100: 53 | part1 += len(flashed) 54 | if part2 is None and len(flashed) == len(grid): 55 | part2 = i 56 | if i >= 100 and part2 is not None: 57 | break 58 | 59 | print("Part 1:", part1) 60 | print("Part 2:", part2) 61 | -------------------------------------------------------------------------------- /2017/day07/sol.py: -------------------------------------------------------------------------------- 1 | def calc_weight(memory, name): 2 | weight = 0 3 | subweight_one = None 4 | for s in memory[name]["next"]: 5 | if "subweight" not in memory[s]: 6 | calc_weight(memory, s) 7 | s_weight = memory[s]["subweight"] + memory[s]["weight"] 8 | if subweight_one is None: 9 | subweight_one = s_weight 10 | elif subweight_one != s_weight: 11 | print(s, "has", s_weight, "while others have", subweight_one) 12 | weight += s_weight 13 | memory[name]["subweight"] = weight 14 | 15 | def find_wrong(memory, name): 16 | calc_weight(memory, name) 17 | 18 | with open("input.txt", "r") as f: 19 | memory = {} 20 | for line in f: 21 | parts = line.strip().split(" ") 22 | name = parts[0] 23 | new = memory.get(name, {"next": [], "prev": None}) 24 | new["weight"] = int(parts[1][1:-1]) 25 | if len(parts) > 2: 26 | for s in parts[3:]: 27 | s = s.replace(",", "") 28 | if s in memory: 29 | memory[s]["prev"] = name 30 | else: 31 | memory[s] = {"next": [], "prev": name} 32 | new["next"].append(s) 33 | memory[name] = new 34 | for name in memory: 35 | if memory[name]["prev"] is None: 36 | print("Bottom:", name) 37 | print(find_wrong(memory, name)) 38 | break 39 | -------------------------------------------------------------------------------- /2020/day19/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | import re 5 | 6 | 7 | def build_re(rule): 8 | if "|" in rule: 9 | parts = rule.split(" | ") 10 | return "(?:" + "|".join(f"(?:{build_re(x)})" for x in parts) + ")" 11 | if rule[0] == '"': 12 | return rule[1] 13 | if rule[-1] == "+": 14 | return build_re(rules[rule[:-1]]) + "+" 15 | return "".join(build_re(rules[r]) for r in rule.split()) 16 | 17 | 18 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 19 | lines = (line.strip() for line in f) 20 | rules = {} 21 | for line in lines: 22 | if not line: 23 | break 24 | num, rule = line.split(": ") 25 | rules[num] = rule 26 | 27 | regex1 = build_re(rules["0"]) 28 | 29 | # rules["8"] = "42 | 42 8" 30 | # rules["11"] = "42 31 | 42 11 31" 31 | 32 | rules["8"] = "42+" 33 | rules["11"] = "42 31 | 42 42 31 31 | 42 42 42 31 31 31 | 42 42 42 42 31 31 31 31 | 42 42 42 42 42 31 31 31 31 31 | 42 42 42 42 42 42 31 31 31 31 31 31 | 42 42 42 42 42 42 42 31 31 31 31 31 31 31 | 42 42 42 42 42 42 42 42 31 31 31 31 31 31 31 31" 34 | 35 | regex2 = build_re(rules["0"]) 36 | 37 | part1 = part2 = 0 38 | for line in lines: 39 | if re.fullmatch(regex1, line): 40 | part1 += 1 41 | if re.fullmatch(regex2, line): 42 | part2 += 1 43 | 44 | print("Part 1:", part1) 45 | print("Part 2:", part2) 46 | -------------------------------------------------------------------------------- /2023/day01/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | NUMBERS_PART_1 = { 7 | "0": 0, 8 | "1": 1, 9 | "2": 2, 10 | "3": 3, 11 | "4": 4, 12 | "5": 5, 13 | "6": 6, 14 | "7": 7, 15 | "8": 8, 16 | "9": 9, 17 | } 18 | 19 | NUMBERS_PART_2 = { 20 | "zero": 0, 21 | "one": 1, 22 | "two": 2, 23 | "three": 3, 24 | "four": 4, 25 | "five": 5, 26 | "six": 6, 27 | "seven": 7, 28 | "eight": 8, 29 | "nine": 9, 30 | } 31 | 32 | 33 | def try_parse(line: str, numbers: dict[str, int]) -> int | None: 34 | for num, value in numbers.items(): 35 | if line.startswith(num): 36 | return value 37 | 38 | 39 | def solve(lines: list[str], numbers: dict[str, int]): 40 | result = 0 41 | for line in lines: 42 | for i in range(len(line)): 43 | value = try_parse(line[i:], numbers) 44 | if value is not None: 45 | result += value * 10 46 | break 47 | 48 | for i in reversed(range(len(line))): 49 | value = try_parse(line[i:], numbers) 50 | if value is not None: 51 | result += value 52 | break 53 | 54 | return result 55 | 56 | 57 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 58 | lines = f.read().splitlines() 59 | print("Part 1:", solve(lines, NUMBERS_PART_1)) 60 | print("Part 2:", solve(lines, NUMBERS_PART_1 | NUMBERS_PART_2)) 61 | -------------------------------------------------------------------------------- /2015/day18/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 6 | grid = [list(row) for row in f.read().splitlines()] 7 | 8 | 9 | def compute(grid, part2=False): 10 | def neighbors(x, y): 11 | count = 0 12 | for dx in (-1, 0, 1): 13 | for dy in (-1, 0, 1): 14 | if dx == 0 and dy == 0: 15 | continue 16 | xx = x + dx 17 | yy = y + dy 18 | if 0 <= xx < 100 and 0 <= yy < 100 and grid[yy][xx] == "#": 19 | count += 1 20 | return count 21 | 22 | for _ in range(100): 23 | new = [] 24 | for y, row in enumerate(grid): 25 | new_row = [] 26 | for x, c in enumerate(row): 27 | if c == "#": 28 | new_row.append("#" if neighbors(x, y) in (2, 3) else ".") 29 | else: 30 | new_row.append("#" if neighbors(x, y) == 3 else ".") 31 | new.append(new_row) 32 | grid = new 33 | if part2: 34 | for x in (0, 99): 35 | for y in (0, 99): 36 | grid[y][x] = "#" 37 | 38 | return sum(sum(c == "#" for c in row) for row in grid) 39 | 40 | 41 | print("Part 1:", compute(grid)) 42 | 43 | for x in (0, 99): 44 | for y in (0, 99): 45 | grid[y][x] = "#" 46 | 47 | print("Part 2:", compute(grid, True)) 48 | -------------------------------------------------------------------------------- /2020/day24/sol.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | from os import path 4 | 5 | 6 | DIFF = { 7 | "e": (1, 0), 8 | "w": (-1, 0), 9 | "se": (0, 1), 10 | "sw": (-1, 1), 11 | "ne": (1, -1), 12 | "nw": (0, -1), 13 | } 14 | 15 | with open(path.join(path.dirname(__file__), "input.txt")) as f: 16 | black = set() 17 | 18 | for line in f.read().splitlines(): 19 | q, r = 0, 0 20 | while line: 21 | for k, (qd, rd) in DIFF.items(): 22 | if line.startswith(k): 23 | line = line[len(k) :] 24 | q += qd 25 | r += rd 26 | if (q,r) in black: 27 | black.remove((q,r)) 28 | else: 29 | black.add((q, r)) 30 | 31 | print("Part 1:", len(black)) 32 | 33 | for _ in range(100): 34 | new = set() 35 | todo = set() 36 | for q, r in black: 37 | black_count = 0 38 | for dq, dr in DIFF.values(): 39 | n = q+dq,r+dr 40 | if n in black: 41 | black_count += 1 42 | else: 43 | todo.add(n) 44 | if 0 < black_count <= 2: 45 | new.add((q, r)) 46 | for q, r in todo: 47 | black_count = sum((q+dq,r+dr) in black for dq, dr in DIFF.values()) 48 | if black_count == 2: 49 | new.add((q, r)) 50 | black = new 51 | 52 | print("Part 2:", len(black)) 53 | --------------------------------------------------------------------------------