Compare commits
	
		
			18 Commits
		
	
	
		
			befa6403ba
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 93e365d10b | |||
| 9300d786f8 | |||
| ec75800078 | |||
| 16a0d9b3f8 | |||
| d830919adc | |||
| 056e29b96c | |||
| 47fbeb1ce9 | |||
| 4826c0d21e | |||
| 0950e76d8d | |||
| 5921a90c73 | |||
| 4a429bd62b | |||
| 953716b95a | |||
| 43c6d9e325 | |||
| 927feb071e | |||
| 5107cf7f82 | |||
| 9a7eef5186 | |||
| dd1e8a81fd | |||
| 05f1f99ac1 | 
							
								
								
									
										26
									
								
								day22.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								day22.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| with open("res/inputs/day22.txt", "r") as f: | ||||
|     values = list(map(int, f.read().split("\n"))) | ||||
|  | ||||
| calls = 0 | ||||
|  | ||||
| def step(a, shift): | ||||
|     global calls | ||||
|     calls += 1 | ||||
|     s = (a >> -shift) if shift < 0 else (a << shift) | ||||
|     return (s ^ a) & 0xffffff | ||||
|  | ||||
| def rand(value): | ||||
|     b = step(value, 6) | ||||
|     c = step(b, -5) | ||||
|     d = step(c, 11) | ||||
|     return d | ||||
|  | ||||
|  | ||||
| total = 0 | ||||
| for v in values: | ||||
|     for _ in range(2000): | ||||
|         v = rand(v) | ||||
|     total += v | ||||
|  | ||||
| print(total) | ||||
| print(calls) | ||||
							
								
								
									
										
											BIN
										
									
								
								progress.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								progress.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 135 KiB | 
| @@ -26,3 +26,25 @@ | ||||
|   stars: 2 | ||||
| 14: | ||||
|   stars: 2 | ||||
| 15: | ||||
|   stars: 2 | ||||
| 16: | ||||
|   stars: 0 | ||||
| 17: | ||||
|   stars: 1 | ||||
| 18: | ||||
|   stars: 2 | ||||
| 19: | ||||
|   stars: 2 | ||||
| 20: | ||||
|   stars: 1 | ||||
| 21: | ||||
|   stars: 0 | ||||
| 22: | ||||
|   stars: 1 | ||||
| 23: | ||||
|   stars: 2 | ||||
| 24: | ||||
|   stars: 1 | ||||
| 25: | ||||
|   stars: 1 | ||||
							
								
								
									
										10
									
								
								res/examples/day15_1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								res/examples/day15_1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| ######## | ||||
| #..O.O.# | ||||
| ##@.O..# | ||||
| #...O..# | ||||
| #.#.O..# | ||||
| #...O..# | ||||
| #......# | ||||
| ######## | ||||
|  | ||||
| <^^>>>vv<v>>v<< | ||||
							
								
								
									
										21
									
								
								res/examples/day15_2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								res/examples/day15_2.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,21 @@ | ||||
| ########## | ||||
| #..O..O.O# | ||||
| #......O.# | ||||
| #.OO..O.O# | ||||
| #..O@..O.# | ||||
| #O#..O...# | ||||
| #O..O..O.# | ||||
| #.OO.O.OO# | ||||
| #....O...# | ||||
| ########## | ||||
|  | ||||
| <vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^ | ||||
| vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v | ||||
| ><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv< | ||||
| <<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^ | ||||
| ^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^>< | ||||
| ^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^ | ||||
| >^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^ | ||||
| <><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<> | ||||
| ^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v> | ||||
| v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^ | ||||
							
								
								
									
										15
									
								
								res/examples/day16_1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								res/examples/day16_1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| ############### | ||||
| #.......#....E# | ||||
| #.#.###.#.###.# | ||||
| #.....#.#...#.# | ||||
| #.###.#####.#.# | ||||
| #.#.#.......#.# | ||||
| #.#.#####.###.# | ||||
| #...........#.# | ||||
| ###.#.#####.#.# | ||||
| #...#.....#.#.# | ||||
| #.#.#.###.#.#.# | ||||
| #.....#...#.#.# | ||||
| #.###.#.#.#.#.# | ||||
| #S..#.....#...# | ||||
| ############### | ||||
							
								
								
									
										17
									
								
								res/examples/day16_2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								res/examples/day16_2.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| ################# | ||||
| #...#...#...#..E# | ||||
| #.#.#.#.#.#.#.#.# | ||||
| #.#.#.#...#...#.# | ||||
| #.#.#.#.###.#.#.# | ||||
| #...#.#.#.....#.# | ||||
| #.#.#.#.#.#####.# | ||||
| #.#...#.#.#.....# | ||||
| #.#.#####.#.###.# | ||||
| #.#.#.......#...# | ||||
| #.#.###.#####.### | ||||
| #.#.#...#.....#.# | ||||
| #.#.#.#####.###.# | ||||
| #.#.#.........#.# | ||||
| #.#.#.#########.# | ||||
| #S#.............# | ||||
| ################# | ||||
							
								
								
									
										5
									
								
								res/examples/day17_1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								res/examples/day17_1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| Register A: 729 | ||||
| Register B: 0 | ||||
| Register C: 0 | ||||
|  | ||||
| Program: 0,1,5,4,3,0 | ||||
							
								
								
									
										5
									
								
								res/examples/day17_2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								res/examples/day17_2.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| Register A: 2024 | ||||
| Register B: 0 | ||||
| Register C: 0 | ||||
|  | ||||
| Program: 0,3,5,4,3,0 | ||||
							
								
								
									
										25
									
								
								res/examples/day18.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								res/examples/day18.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| 5,4 | ||||
| 4,2 | ||||
| 4,5 | ||||
| 3,0 | ||||
| 2,1 | ||||
| 6,3 | ||||
| 2,4 | ||||
| 1,5 | ||||
| 0,6 | ||||
| 3,3 | ||||
| 2,6 | ||||
| 5,1 | ||||
| 1,2 | ||||
| 5,5 | ||||
| 2,5 | ||||
| 6,5 | ||||
| 1,4 | ||||
| 0,4 | ||||
| 6,4 | ||||
| 1,1 | ||||
| 6,1 | ||||
| 1,0 | ||||
| 0,5 | ||||
| 1,6 | ||||
| 2,0 | ||||
							
								
								
									
										10
									
								
								res/examples/day19.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								res/examples/day19.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| r, wr, b, g, bwu, rb, gb, br | ||||
|  | ||||
| brwrr | ||||
| bggr | ||||
| gbbr | ||||
| rrbgbr | ||||
| ubwu | ||||
| bwurrg | ||||
| brgr | ||||
| bbrgwb | ||||
							
								
								
									
										15
									
								
								res/examples/day20.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								res/examples/day20.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| ############### | ||||
| #...#...#.....# | ||||
| #.#.#.#.#.###.# | ||||
| #S#...#.#.#...# | ||||
| #######.#.#.### | ||||
| #######.#.#...# | ||||
| #######.#.###.# | ||||
| ###..E#...#...# | ||||
| ###.#######.### | ||||
| #...###...#...# | ||||
| #.#####.#.###.# | ||||
| #.#...#.#.#...# | ||||
| #.#.#.#.#.#.### | ||||
| #...#...#...### | ||||
| ############### | ||||
							
								
								
									
										5
									
								
								res/examples/day21.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								res/examples/day21.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| 029A | ||||
| 980A | ||||
| 179A | ||||
| 456A | ||||
| 379A | ||||
							
								
								
									
										1
									
								
								res/examples/day22.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								res/examples/day22.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| 123 | ||||
							
								
								
									
										32
									
								
								res/examples/day23.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								res/examples/day23.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| kh-tc | ||||
| qp-kh | ||||
| de-cg | ||||
| ka-co | ||||
| yn-aq | ||||
| qp-ub | ||||
| cg-tb | ||||
| vc-aq | ||||
| tb-ka | ||||
| wh-tc | ||||
| yn-cg | ||||
| kh-ub | ||||
| ta-co | ||||
| de-co | ||||
| tc-td | ||||
| tb-wq | ||||
| wh-td | ||||
| ta-ka | ||||
| td-qp | ||||
| aq-cg | ||||
| wq-ub | ||||
| ub-vc | ||||
| de-ta | ||||
| wq-aq | ||||
| wq-vc | ||||
| wh-yn | ||||
| ka-de | ||||
| kh-ta | ||||
| co-tc | ||||
| wh-qp | ||||
| tb-vc | ||||
| td-yn | ||||
							
								
								
									
										10
									
								
								res/examples/day24_1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								res/examples/day24_1.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| x00: 1 | ||||
| x01: 1 | ||||
| x02: 1 | ||||
| y00: 0 | ||||
| y01: 1 | ||||
| y02: 0 | ||||
|  | ||||
| x00 AND y00 -> z00 | ||||
| x01 XOR y01 -> z01 | ||||
| x02 OR y02 -> z02 | ||||
							
								
								
									
										47
									
								
								res/examples/day24_2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								res/examples/day24_2.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,47 @@ | ||||
| x00: 1 | ||||
| x01: 0 | ||||
| x02: 1 | ||||
| x03: 1 | ||||
| x04: 0 | ||||
| y00: 1 | ||||
| y01: 1 | ||||
| y02: 1 | ||||
| y03: 1 | ||||
| y04: 1 | ||||
|  | ||||
| ntg XOR fgs -> mjb | ||||
| y02 OR x01 -> tnw | ||||
| kwq OR kpj -> z05 | ||||
| x00 OR x03 -> fst | ||||
| tgd XOR rvg -> z01 | ||||
| vdt OR tnw -> bfw | ||||
| bfw AND frj -> z10 | ||||
| ffh OR nrd -> bqk | ||||
| y00 AND y03 -> djm | ||||
| y03 OR y00 -> psh | ||||
| bqk OR frj -> z08 | ||||
| tnw OR fst -> frj | ||||
| gnj AND tgd -> z11 | ||||
| bfw XOR mjb -> z00 | ||||
| x03 OR x00 -> vdt | ||||
| gnj AND wpb -> z02 | ||||
| x04 AND y00 -> kjc | ||||
| djm OR pbm -> qhw | ||||
| nrd AND vdt -> hwm | ||||
| kjc AND fst -> rvg | ||||
| y04 OR y02 -> fgs | ||||
| y01 AND x02 -> pbm | ||||
| ntg OR kjc -> kwq | ||||
| psh XOR fgs -> tgd | ||||
| qhw XOR tgd -> z09 | ||||
| pbm OR djm -> kpj | ||||
| x03 XOR y03 -> ffh | ||||
| x00 XOR y04 -> ntg | ||||
| bfw OR bqk -> z06 | ||||
| nrd XOR fgs -> wpb | ||||
| frj XOR qhw -> z04 | ||||
| bqk OR frj -> z07 | ||||
| y03 OR x01 -> nrd | ||||
| hwm AND bqk -> z03 | ||||
| tgd XOR rvg -> z12 | ||||
| tnw OR pbm -> gnj | ||||
							
								
								
									
										39
									
								
								res/examples/day25.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								res/examples/day25.txt
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,39 @@ | ||||
| ##### | ||||
| .#### | ||||
| .#### | ||||
| .#### | ||||
| .#.#. | ||||
| .#... | ||||
| ..... | ||||
|  | ||||
| ##### | ||||
| ##.## | ||||
| .#.## | ||||
| ...## | ||||
| ...#. | ||||
| ...#. | ||||
| ..... | ||||
|  | ||||
| ..... | ||||
| #.... | ||||
| #.... | ||||
| #...# | ||||
| #.#.# | ||||
| #.### | ||||
| ##### | ||||
|  | ||||
| ..... | ||||
| ..... | ||||
| #.#.. | ||||
| ###.. | ||||
| ###.# | ||||
| ###.# | ||||
| ##### | ||||
|  | ||||
| ..... | ||||
| ..... | ||||
| ..... | ||||
| #.... | ||||
| #.#.. | ||||
| #.#.# | ||||
| ##### | ||||
							
								
								
									
										91
									
								
								src/day15/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/day15/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,91 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let WALL = "#" | ||||
| #let BOX = "O" | ||||
| #let BOT = "@" | ||||
| #let EMPTY = "." | ||||
|  | ||||
| #let offsets = ( | ||||
|   "^": (0, -1), | ||||
|   "<": (-1, 0), | ||||
|   "v": (0, 1), | ||||
|   ">": (1, 0) | ||||
| ) | ||||
|  | ||||
| #let compute-value(grid) = { | ||||
|   let total = 0 | ||||
|   for (y, row) in grid.enumerate() { | ||||
|     for (x, type) in row.enumerate() { | ||||
|       if type == BOX { | ||||
|         total += y * 100 + x | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return total | ||||
| } | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let (grid-data, move-data) = input.split("\n\n") | ||||
|   let grid = grid-data.split("\n").map(r => r.clusters()) | ||||
|   let rows = () | ||||
|   let cols = () | ||||
|    | ||||
|   let w = grid.first().len() | ||||
|   let h = grid.len() | ||||
|   let bot-pos = none | ||||
|  | ||||
|   for y in range(h) { | ||||
|     for x in range(w) { | ||||
|       let type = grid.at(y).at(x) | ||||
|       if type == BOT { | ||||
|         bot-pos = (x, y) | ||||
|         grid.at(y).at(x) = EMPTY | ||||
|         break | ||||
|       } | ||||
|     } | ||||
|     if bot-pos != none { | ||||
|       break | ||||
|     } | ||||
|   } | ||||
|   let (bot-x, bot-y) = bot-pos | ||||
|  | ||||
|   let moves = move-data.replace("\n", "").clusters() | ||||
|   for (move-i, move) in moves.enumerate() { | ||||
|     let (dx, dy) = offsets.at(move) | ||||
|     let (x2, y2) = (bot-x + dx, bot-y + dy) | ||||
|     let type = grid.at(y2).at(x2) | ||||
|     if type == WALL { | ||||
|       continue | ||||
|     } | ||||
|     if type == EMPTY { | ||||
|       (bot-x, bot-y) = (x2, y2) | ||||
|       continue | ||||
|     } | ||||
|  | ||||
|     let type2 = type | ||||
|     let (x3, y3) = (x2, y2) | ||||
|     while type2 == BOX { | ||||
|       x3 += dx | ||||
|       y3 += dy | ||||
|       type2 = grid.at(y3).at(x3) | ||||
|     } | ||||
|  | ||||
|     if type2 == WALL { | ||||
|       continue | ||||
|     } | ||||
|     grid.at(y3).at(x3) = BOX | ||||
|     grid.at(y2).at(x2) = EMPTY | ||||
|     (bot-x, bot-y) = (x2, y2) | ||||
|   } | ||||
|  | ||||
|   return compute-value(grid) | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   15, 1, | ||||
|   solve, | ||||
|   example: ( | ||||
|     "1": 2028, | ||||
|     "2": 10092 | ||||
|   ) | ||||
| ) | ||||
							
								
								
									
										123
									
								
								src/day15/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										123
									
								
								src/day15/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,123 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let WALL = "#" | ||||
| #let BOX = "O" | ||||
| #let BOX-L = "[" | ||||
| #let BOX-R = "]" | ||||
| #let BOT = "@" | ||||
| #let EMPTY = "." | ||||
|  | ||||
| #let offsets = ( | ||||
|   "^": (0, -1), | ||||
|   "<": (-1, 0), | ||||
|   "v": (0, 1), | ||||
|   ">": (1, 0) | ||||
| ) | ||||
|  | ||||
| #let compute-value(grid) = { | ||||
|   let total = 0 | ||||
|   for (y, row) in grid.enumerate() { | ||||
|     for (x, type) in row.enumerate() { | ||||
|       if type == BOX-L { | ||||
|         total += y * 100 + x | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return total | ||||
| } | ||||
|  | ||||
| #let can-move(grid, w, h, x, y, dx, dy) = { | ||||
|   let (x2, y2) = (x + dx, y + dy) | ||||
|   let type = grid.at(y2).at(x2) | ||||
|   if type == EMPTY { | ||||
|     return true | ||||
|   } | ||||
|   if type == WALL { | ||||
|     return false | ||||
|   } | ||||
|  | ||||
|   // Horizontal move | ||||
|   if dy == 0 { | ||||
|     return can-move(grid, w, h, x2, y2, dx, dy) | ||||
|   } | ||||
|    | ||||
|   let lx = if type == BOX-L {x2} else {x2 - 1} | ||||
|  | ||||
|   return ( | ||||
|     can-move(grid, w, h, lx, y2, dx, dy) and | ||||
|     can-move(grid, w, h, lx + 1, y2, dx, dy) | ||||
|   ) | ||||
| } | ||||
|  | ||||
| #let do-move(grid, w, h, x, y, dx, dy) = { | ||||
|   let type = grid.at(y).at(x) | ||||
|   let (x2, y2) = (x + dx, y + dy) | ||||
|   let type2 = grid.at(y2).at(x2) | ||||
|    | ||||
|   if type2 in (BOX-L, BOX-R) { | ||||
|     grid = do-move(grid, w, h, x2, y2, dx, dy) | ||||
|     // Vertical move | ||||
|     if dx == 0 { | ||||
|       let x3 = if type2 == BOX-L {x2 + 1} else {x2 - 1} | ||||
|       grid = do-move(grid, w, h, x3, y2, dx, dy) | ||||
|     } | ||||
|     type2 = EMPTY | ||||
|   } | ||||
|   if type2 == EMPTY { | ||||
|     grid.at(y2).at(x2) = type | ||||
|     grid.at(y).at(x) = EMPTY | ||||
|     return grid | ||||
|   } | ||||
|   if type2 == WALL { | ||||
|     panic() | ||||
|   } | ||||
|   return grid | ||||
| } | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let (grid-data, move-data) = input.split("\n\n") | ||||
|   let grid = grid-data.split("\n").map(r => r.clusters()) | ||||
|    | ||||
|   let w = grid.first().len() | ||||
|   let h = grid.len() | ||||
|   let (w2, h2) = (w * 2, h) | ||||
|   let bot-pos = none | ||||
|   let grid2 = ((EMPTY,) * w2,) * h2 | ||||
|  | ||||
|   for y in range(h) { | ||||
|     for x in range(w) { | ||||
|       let type = grid.at(y).at(x) | ||||
|       if type == BOT { | ||||
|         bot-pos = (x*2, y) | ||||
|         type = EMPTY | ||||
|         continue | ||||
|       } | ||||
|       grid2.at(y).at(x*2) = if type == BOX {BOX-L} else {type} | ||||
|       grid2.at(y).at(x*2 + 1) = if type == BOX {BOX-R} else {type} | ||||
|     } | ||||
|   } | ||||
|   let (bot-x, bot-y) = bot-pos | ||||
|  | ||||
|   let moves = move-data.replace("\n", "").clusters() | ||||
|   for (move-i, move) in moves.enumerate() { | ||||
|     let (dx, dy) = offsets.at(move) | ||||
|     let (x2, y2) = (bot-x + dx, bot-y + dy) | ||||
|  | ||||
|     if not can-move(grid2, w2, h2, bot-x, bot-y, dx, dy) { | ||||
|       continue | ||||
|     } | ||||
|  | ||||
|     grid2 = do-move(grid2, w2, h2, bot-x, bot-y, dx, dy) | ||||
|     (bot-x, bot-y) = (x2, y2) | ||||
|   } | ||||
|  | ||||
|   return compute-value(grid2) | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   15, 2, | ||||
|   solve, | ||||
|   example: ( | ||||
|     "2": 9021 | ||||
|   ) | ||||
| ) | ||||
							
								
								
									
										67
									
								
								src/day16/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								src/day16/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let START = "S" | ||||
| #let END = "E" | ||||
| #let WALL = "#" | ||||
| #let EMPTY = "." | ||||
|  | ||||
| #let offsets = ( | ||||
|   (1, 0), | ||||
|   (0, 1), | ||||
|   (-1, 0), | ||||
|   (0, -1) | ||||
| ) | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let grid = input.split("\n").map(l => l.clusters()) | ||||
|   let w = grid.first().len() | ||||
|   let h = grid.len() | ||||
|  | ||||
|   let (sx, sy) = (0, 0) | ||||
|   let (ex, ey) = (0, 0) | ||||
|   for y in range(h) { | ||||
|     for x in range(w) { | ||||
|       let c = grid.at(y).at(x) | ||||
|       if c == START { | ||||
|         (sx, sy) = (x, y) | ||||
|       } else if c == END { | ||||
|         (ex, ey) = (x, y) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let choices = ((sx, sy, 0, 0),) | ||||
|   let (x, y, dir, score) = (0, 0, 0, 0) | ||||
|   while choices.len() != 0 { | ||||
|     let min-score = calc.min(..choices.map(c => c.last())) | ||||
|     let i = choices.position(c => c.last() == min-score) | ||||
|     (x, y, dir, score) = choices.remove(i) | ||||
|     for (d, (dx, dy)) in offsets.enumerate() { | ||||
|       // Ignore backflips | ||||
|       if calc.abs(d - dir) == 2 { | ||||
|         continue | ||||
|       } | ||||
|       let (x2, y2) = (x + dx, y + dy) | ||||
|       let c = grid.at(y2).at(x2) | ||||
|       if c == WALL { | ||||
|         continue | ||||
|       } | ||||
|       let score2 = score + 1 + if d != dir {1000} else {0} | ||||
|       if c == END { | ||||
|         return score2 | ||||
|       } | ||||
|       choices.push((x2, y2, d, score2)) | ||||
|     } | ||||
|     break | ||||
|   } | ||||
|   return 0 | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   16, 1, | ||||
|   solve, | ||||
|   example: ( | ||||
|     "1": 7036, | ||||
|     "2": 11048 | ||||
|   ) | ||||
| ) | ||||
							
								
								
									
										0
									
								
								src/day16/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/day16/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										87
									
								
								src/day17/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								src/day17/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let ADV = 0 | ||||
| #let BXL = 1 | ||||
| #let BST = 2 | ||||
| #let JNZ = 3 | ||||
| #let BXC = 4 | ||||
| #let OUT = 5 | ||||
| #let BDV = 6 | ||||
| #let CDV = 7 | ||||
|  | ||||
| #let get-combo(regs, value) = { | ||||
|   if value >= 7 { | ||||
|     panic() | ||||
|   } | ||||
|   if value <= 3 { | ||||
|     return value | ||||
|   } | ||||
|   return regs.at(value - 4) | ||||
| } | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let (registers, program) = input.split("\n\n") | ||||
|  | ||||
|   let regs = () | ||||
|   for line in registers.split("\n") { | ||||
|     regs.push( | ||||
|       int( | ||||
|         line.split(": ") | ||||
|             .last() | ||||
|       ) | ||||
|     ) | ||||
|   } | ||||
|   program = program.split(": ") | ||||
|                    .last() | ||||
|                    .split(",") | ||||
|                    .map(int) | ||||
|  | ||||
|   let out = () | ||||
|   let pc = 0 | ||||
|   while pc < program.len() { | ||||
|     let op = program.at(pc) | ||||
|     let val = program.at(pc + 1) | ||||
|     if op == ADV { | ||||
|       let num = regs.at(0) | ||||
|       let den = get-combo(regs, val) | ||||
|       let res = num.bit-rshift(den) | ||||
|       regs.at(0) = res | ||||
|     } else if op == BXL { | ||||
|       regs.at(1) = regs.at(1).bit-xor(val) | ||||
|     } else if op == BST { | ||||
|       regs.at(1) = get-combo(regs, val).bit-and(0b111) | ||||
|     } else if op == JNZ { | ||||
|       if regs.at(0) != 0 { | ||||
|         pc = val | ||||
|         continue | ||||
|       } | ||||
|     } else if op == BXC { | ||||
|       regs.at(1) = regs.at(1).bit-xor(regs.at(2)) | ||||
|     } else if op == OUT { | ||||
|       out.push(get-combo(regs, val).bit-and(0b111)) | ||||
|     } else if op == BDV { | ||||
|       let num = regs.at(0) | ||||
|       let den = get-combo(regs, val) | ||||
|       let res = num.bit-rshift(den) | ||||
|       regs.at(1) = res | ||||
|     } else if op == CDV { | ||||
|       let num = regs.at(0) | ||||
|       let den = get-combo(regs, val) | ||||
|       let res = num.bit-rshift(den) | ||||
|       regs.at(2) = res | ||||
|     } else { | ||||
|       panic("Unknown instruction " + str(op)) | ||||
|     } | ||||
|     pc += 2 | ||||
|   } | ||||
|  | ||||
|   return out.map(str).join(",") | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   17, 1, | ||||
|   solve, | ||||
|   example: ( | ||||
|     "1": "4,6,3,5,6,3,5,2,1,0" | ||||
|   ) | ||||
| ) | ||||
							
								
								
									
										108
									
								
								src/day17/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										108
									
								
								src/day17/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,108 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let ADV = 0 | ||||
| #let BXL = 1 | ||||
| #let BST = 2 | ||||
| #let JNZ = 3 | ||||
| #let BXC = 4 | ||||
| #let OUT = 5 | ||||
| #let BDV = 6 | ||||
| #let CDV = 7 | ||||
|  | ||||
| #let ops = ("ADV", "BXL", "BST", "JNZ", "BXC", "OUT", "BDV", "CDV") | ||||
|  | ||||
| #let get-combo(regs, value) = { | ||||
|   if value >= 7 { | ||||
|     panic() | ||||
|   } | ||||
|   if value <= 3 { | ||||
|     return value | ||||
|   } | ||||
|   return regs.at(value - 4) | ||||
| } | ||||
|  | ||||
| #let describe-combo(value) = { | ||||
|   if value == 7 { | ||||
|     return "<invalid>" | ||||
|   } | ||||
|   if value <= 3 { | ||||
|     return str(value) | ||||
|   } | ||||
|   return "ABC".at(value - 4) | ||||
| } | ||||
|  | ||||
| #let describe(program) = { | ||||
|   let res = "" | ||||
|   res += program.map(str).join(",") | ||||
|  | ||||
|   for i in range(0, program.len(), step: 2) { | ||||
|     res += "\n" | ||||
|     let op = program.at(i) | ||||
|     let val = program.at(i + 1) | ||||
|     let combo = describe-combo(val) | ||||
|  | ||||
|     res += ops.at(op) + ": " | ||||
|     if op == ADV { | ||||
|       res += "A >>= " + combo | ||||
|     } else if op == BXL { | ||||
|       res += "B ^= " + str(val) | ||||
|     } else if op == BST { | ||||
|       res += "B = " + combo + " & 0b111" | ||||
|     } else if op == JNZ { | ||||
|       res += "IF A != 0 {PC = " + str(val) + "}" | ||||
|     } else if op == BXC { | ||||
|       res += "B ^= C" | ||||
|     } else if op == OUT { | ||||
|       res += "OUT(" + combo + " & 0b111)" | ||||
|     } else if op == BDV { | ||||
|       res += "B = A >> " + combo | ||||
|     } else if op == CDV { | ||||
|       res += "C = A >> " + combo | ||||
|     } | ||||
|   } | ||||
|   return res | ||||
| } | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let (registers, program) = input.split("\n\n") | ||||
|  | ||||
|   let regs = () | ||||
|   for line in registers.split("\n") { | ||||
|     regs.push( | ||||
|       int( | ||||
|         line.split(": ") | ||||
|             .last() | ||||
|       ) | ||||
|     ) | ||||
|   } | ||||
|   program = program.split(": ") | ||||
|                    .last() | ||||
|                    .split(",") | ||||
|                    .map(int) | ||||
|  | ||||
|   let out = () | ||||
|   let pc = 0 | ||||
|  | ||||
|   regs.first() = 0 | ||||
|  | ||||
|   for n in program { | ||||
|     let b = n | ||||
|   } | ||||
|    | ||||
|   return raw(block: true, describe(program)) | ||||
| } | ||||
|  | ||||
| Example: | ||||
| #solve(read(get-example-path(17, suffix: "2"))) | ||||
|  | ||||
| Input: | ||||
| #solve(get-input(17)) | ||||
|  | ||||
| /* | ||||
| #show-puzzle( | ||||
|   17, 2, | ||||
|   solve, | ||||
|   example: ( | ||||
|     "2": 117440 | ||||
|   ) | ||||
| )*/ | ||||
							
								
								
									
										143
									
								
								src/day18/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/day18/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,143 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let offsets = ( | ||||
|   (1, 0), | ||||
|   (0, 1), | ||||
|   (-1, 0), | ||||
|   (0, -1) | ||||
| ) | ||||
|  | ||||
| #let get-lowest(nodes, scores) = { | ||||
|   let lowest-score = none | ||||
|   let lowest-i = none | ||||
|   for (i, (x, y)) in nodes.enumerate() { | ||||
|     let score = scores.at(y).at(x) | ||||
|  | ||||
|     if lowest-i == none or score < lowest-score { | ||||
|       lowest-score = score | ||||
|       lowest-i = i | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if lowest-i == none { | ||||
|     panic() | ||||
|   } | ||||
|   return lowest-i | ||||
| } | ||||
|  | ||||
| #let make-path(parents, end) = { | ||||
|   let path = (end,) | ||||
|   let (x, y) = end | ||||
|   let pos = parents.at(y).at(x) | ||||
|   while pos != none { | ||||
|     (x, y) = pos | ||||
|     path.insert(0, pos) | ||||
|     pos = parents.at(y).at(x) | ||||
|   } | ||||
|   return path | ||||
| } | ||||
|  | ||||
| #let find-path(w, h, grid) = { | ||||
|   let end = (w - 1, h - 1) | ||||
|   let (x, y) = (0, 0) | ||||
|   let open = ((x, y),) | ||||
|   let closed = () | ||||
|   let g-scores = ((0,) * w,) * h | ||||
|   let f-scores = ((calc.inf,) * w,) * h | ||||
|   let parents = ((none,)*w,)*h | ||||
|   while open.len() != 0 { | ||||
|     let cur = open.remove(get-lowest(open, f-scores)) | ||||
|      | ||||
|     if cur == end { | ||||
|       return make-path(parents, cur) | ||||
|     } | ||||
|  | ||||
|     let g-score = g-scores.at(y).at(x) | ||||
|     let f-score = f-scores.at(y).at(x) | ||||
|      | ||||
|     let (x, y) = cur | ||||
|     for (dx, dy) in offsets { | ||||
|       let (x2, y2) = (x + dx, y + dy) | ||||
|       if x2 < 0 or x2 >= w or y2 < 0 or y2 >= h { | ||||
|         continue | ||||
|       } | ||||
|       if grid.at(y2).at(x2) { | ||||
|         continue | ||||
|       } | ||||
|       if (x2, y2) in closed { | ||||
|         continue | ||||
|       } | ||||
|       let g = x2 + y2 | ||||
|       let h = calc.abs(end.first() - x2) + calc.abs(end.last() - y2) | ||||
|       let f = g + h | ||||
|  | ||||
|       if f < f-scores.at(y2).at(x2) { | ||||
|         g-scores.at(y2).at(x2) = g | ||||
|         f-scores.at(y2).at(x2) = f | ||||
|         parents.at(y2).at(x2) = (x, y) | ||||
|         open.push((x2, y2)) | ||||
|       } | ||||
|     } | ||||
|     closed.push(cur) | ||||
|   } | ||||
|   panic("No path was found") | ||||
| } | ||||
|  | ||||
| #let solve(input, w: 0, h: 0, n-bytes: 0) = { | ||||
|   assert(w != 0, message: "Width cannot be 0") | ||||
|   assert(h != 0, message: "Height cannot be 0") | ||||
|   let grid = ((false,) * w,) * h | ||||
|  | ||||
|   let obstacles = input.split("\n") | ||||
|   for obs in obstacles.slice(0, n-bytes) { | ||||
|     let (x, y) = obs.split(",").map(int) | ||||
|     grid.at(y).at(x) = true | ||||
|   } | ||||
|  | ||||
|   let path = find-path(w, h, grid) | ||||
|   return path.len() - 1 | ||||
| } | ||||
|  | ||||
| #let visualize(input, w: 0, h: 0, n-bytes: 0, s: 2em) = { | ||||
|   let grid_ = ((false,) * w,) * h | ||||
|  | ||||
|   let obstacles = input.split("\n") | ||||
|   for obs in obstacles.slice(0, n-bytes) { | ||||
|     let (x, y) = obs.split(",").map(int) | ||||
|     grid_.at(y).at(x) = true | ||||
|   } | ||||
|  | ||||
|   let path = find-path(w, h, grid_) | ||||
|   for (x, y) in path { | ||||
|     grid_.at(y).at(x) = grid.cell(fill: green.lighten(60%))[O] | ||||
|   } | ||||
|    | ||||
|   let cells = grid_.flatten().map(c => { | ||||
|     if c == false [] | ||||
|     else if c == true {grid.cell(fill: red.lighten(60%))[\#]} | ||||
|     else {c} | ||||
|   }) | ||||
|    | ||||
|   grid( | ||||
|     columns: (s,) * w, | ||||
|     rows: (s,) * h, | ||||
|     align: center + horizon, | ||||
|     stroke: black, | ||||
|     ..cells | ||||
|   ) | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   18, 1, | ||||
|   solve.with(w: 71, h: 71, n-bytes: 1024), | ||||
|   example: ( | ||||
|     (result: 22, args: (w: 7, h: 7, n-bytes: 12)), | ||||
|   ), | ||||
|   visualize: visualize.with(w: 7, h: 7, n-bytes: 12) | ||||
| ) | ||||
|  | ||||
| /* | ||||
| #pagebreak() | ||||
| #set page(width: auto, height: auto) | ||||
| #visualize(get-input(18), w: 71, h: 71, n-bytes: 1024, s: 1em) | ||||
| */ | ||||
							
								
								
									
										117
									
								
								src/day18/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										117
									
								
								src/day18/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,117 @@ | ||||
| #import "/src/utils.typ": * | ||||
| #let offsets = ( | ||||
|   (1, 0), | ||||
|   (0, 1), | ||||
|   (-1, 0), | ||||
|   (0, -1) | ||||
| ) | ||||
|  | ||||
| #let get-lowest(nodes, scores) = { | ||||
|   let lowest-score = none | ||||
|   let lowest-i = none | ||||
|   for (i, (x, y)) in nodes.enumerate() { | ||||
|     let score = scores.at(y).at(x) | ||||
|  | ||||
|     if lowest-i == none or score < lowest-score { | ||||
|       lowest-score = score | ||||
|       lowest-i = i | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   if lowest-i == none { | ||||
|     panic() | ||||
|   } | ||||
|   return lowest-i | ||||
| } | ||||
|  | ||||
| #let make-path(parents, end) = { | ||||
|   let path = (end,) | ||||
|   let (x, y) = end | ||||
|   let pos = parents.at(y).at(x) | ||||
|   while pos != none { | ||||
|     (x, y) = pos | ||||
|     path.insert(0, pos) | ||||
|     pos = parents.at(y).at(x) | ||||
|   } | ||||
|   return path | ||||
| } | ||||
|  | ||||
| #let find-path(w, h, grid, start: (0, 0), end: auto) = { | ||||
|   let end = if end == auto {(w - 1, h - 1)} else {end} | ||||
|   let (x, y) = start | ||||
|   let open = ((x, y),) | ||||
|   let closed = () | ||||
|   let g-scores = ((0,) * w,) * h | ||||
|   let f-scores = ((calc.inf,) * w,) * h | ||||
|   let parents = ((none,)*w,)*h | ||||
|   while open.len() != 0 { | ||||
|     let cur = open.remove(get-lowest(open, f-scores)) | ||||
|      | ||||
|     if cur == end { | ||||
|       return true | ||||
|     } | ||||
|  | ||||
|     let g-score = g-scores.at(y).at(x) | ||||
|     let f-score = f-scores.at(y).at(x) | ||||
|      | ||||
|     let (x, y) = cur | ||||
|     for (dx, dy) in offsets { | ||||
|       let (x2, y2) = (x + dx, y + dy) | ||||
|       if x2 < 0 or x2 >= w or y2 < 0 or y2 >= h { | ||||
|         continue | ||||
|       } | ||||
|       if grid.at(y2).at(x2) { | ||||
|         continue | ||||
|       } | ||||
|       if (x2, y2) in closed { | ||||
|         continue | ||||
|       } | ||||
|       let g = calc.abs(x2 - start.first()) + calc.abs(y2 - start.last()) | ||||
|       let h = calc.abs(end.first() - x2) + calc.abs(end.last() - y2) | ||||
|       let f = g + h | ||||
|  | ||||
|       if f < f-scores.at(y2).at(x2) { | ||||
|         g-scores.at(y2).at(x2) = g | ||||
|         f-scores.at(y2).at(x2) = f | ||||
|         parents.at(y2).at(x2) = (x, y) | ||||
|         open.push((x2, y2)) | ||||
|       } | ||||
|     } | ||||
|     closed.push(cur) | ||||
|   } | ||||
|   return false | ||||
| } | ||||
|  | ||||
| #let solve(input, w: 0, h: 0, n-bytes: 0) = { | ||||
|   assert(w != 0, message: "Width cannot be 0") | ||||
|   assert(h != 0, message: "Height cannot be 0") | ||||
|   let grid = ((false,) * w,) * h | ||||
|  | ||||
|   let obstacles = input.split("\n") | ||||
|   let a = 0 | ||||
|   let b = obstacles.len() | ||||
|  | ||||
|   while b - a > 1 { | ||||
|     let m = calc.div-euclid(a + b, 2) | ||||
|     let grid2 = grid | ||||
|     for obs in obstacles.slice(0, m) { | ||||
|       let (x, y) = obs.split(",").map(int) | ||||
|       grid2.at(y).at(x) = true | ||||
|     } | ||||
|  | ||||
|     if find-path(w, h, grid2) { | ||||
|       a = m | ||||
|     } else { | ||||
|       b = m | ||||
|     } | ||||
|   } | ||||
|   return obstacles.at(a) | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   18, 2, | ||||
|   solve.with(w: 71, h: 71), | ||||
|   example: ( | ||||
|     (result: "6,1", args: (w: 7, h: 7)), | ||||
|   ) | ||||
| ) | ||||
							
								
								
									
										41
									
								
								src/day19/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								src/day19/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let is-possible(target, towels: none) = { | ||||
|   if towels == none { | ||||
|     panic() | ||||
|   } | ||||
|  | ||||
|   for towel in towels { | ||||
|     if towel == target { | ||||
|       return true | ||||
|     } | ||||
|     if target.starts-with(towel) { | ||||
|       if is-possible(target.slice(towel.len()), towels: towels) { | ||||
|         return true | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return false | ||||
| } | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let (towels, targets) = input.split("\n\n") | ||||
|  | ||||
|   towels = towels.split(", ") | ||||
|   let is-possible = is-possible.with(towels: towels) | ||||
|  | ||||
|   let total = 0 | ||||
|   for target in targets.split("\n") { | ||||
|     if is-possible(target) { | ||||
|       total += 1 | ||||
|     } | ||||
|   } | ||||
|   return total | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   19, 1, | ||||
|   solve, | ||||
|   example: 6 | ||||
| ) | ||||
							
								
								
									
										46
									
								
								src/day19/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/day19/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,46 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let (towels, targets) = input.split("\n\n") | ||||
|  | ||||
|   towels = towels.split(", ") | ||||
|   let by-initial = (:) | ||||
|   for towel in towels { | ||||
|     let initial = towel.first() | ||||
|     if initial not in by-initial { | ||||
|       by-initial.insert(initial, ()) | ||||
|     } | ||||
|     by-initial.at(initial).push(towel) | ||||
|   } | ||||
|  | ||||
|   let count(target) = { | ||||
|     let initial = target.first() | ||||
|     if initial not in by-initial { | ||||
|       return 0 | ||||
|     } | ||||
|     let cnt = 0 | ||||
|     for towel in by-initial.at(initial) { | ||||
|       if towel == target { | ||||
|         cnt += 1 | ||||
|       } else if target.starts-with(towel) { | ||||
|         cnt += count(target.slice(towel.len())) | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     return cnt | ||||
|   } | ||||
|  | ||||
|   let total = 0 | ||||
|   for target in targets.split("\n") { | ||||
|     total += count(target) | ||||
|   } | ||||
|   return total | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   19, 2, | ||||
|   solve, | ||||
|   example: 16, | ||||
|   only-example: true | ||||
| ) | ||||
| #show-result(632423618484345) | ||||
							
								
								
									
										128
									
								
								src/day20/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								src/day20/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,128 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let offsets = ( | ||||
|   (1, 0), | ||||
|   (0, 1), | ||||
|   (-1, 0), | ||||
|   (0, -1) | ||||
| ) | ||||
|  | ||||
| #let EMPTY = "." | ||||
| #let WALL = "#" | ||||
| #let START = "S" | ||||
| #let END = "E" | ||||
|  | ||||
| #let in-grid(w, h, x, y) = { | ||||
|   return 0 <= x and x < w and 0 <= y and y < h | ||||
| } | ||||
|  | ||||
| #let count-shortcuts(grid, w, h, save, path) = { | ||||
|   let in-grid = in-grid.with(w, h) | ||||
|   let total = 0 | ||||
|   for (x1, y1) in path { | ||||
|     let v1 = grid.at(y1).at(x1) | ||||
|  | ||||
|     for (dx1, dy1) in offsets { | ||||
|       let (x2, y2) = (x1 + dx1, y1 + dy1) | ||||
|       if not in-grid(x2, y2) { | ||||
|         continue | ||||
|       } | ||||
|       let v2 = grid.at(y2).at(x2) | ||||
|       if v2 < 0 { | ||||
|         for (dx2, dy2) in offsets { | ||||
|           let (x3, y3) = (x2 + dx2, y2 + dy2) | ||||
|           if not in-grid(x3, y3) { | ||||
|             continue | ||||
|           } | ||||
|           let v3 = grid.at(y3).at(x3) | ||||
|           if v3 > 0 { | ||||
|             if v3 - v1 >= save + 2 { | ||||
|               total += 1 | ||||
|             } | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   return total | ||||
| } | ||||
|  | ||||
| #let solve(input, save: 1) = { | ||||
|   let input-grid = input.split("\n").map(l => l.clusters()) | ||||
|  | ||||
|   let w = input-grid.first().len() | ||||
|   let h = input-grid.len() | ||||
|   let in-grid = in-grid.with(w, h) | ||||
|  | ||||
|   let grid = ((-1,)*w,)*h | ||||
|   let start = none | ||||
|   let end = none | ||||
|   for y in range(h) { | ||||
|     for x in range(w) { | ||||
|       let c = input-grid.at(y).at(x) | ||||
|       if c == WALL { | ||||
|         continue | ||||
|       } | ||||
|       if c == START { | ||||
|         start = (x, y) | ||||
|       } else if c == END { | ||||
|         end = (x, y) | ||||
|       } | ||||
|       grid.at(y).at(x) = 0 | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let (x, y) = start | ||||
|   let (ex, ey) = end | ||||
|   let path = () | ||||
|  | ||||
|   while x != ex or y != ey { | ||||
|     path.push((x, y)) | ||||
|     grid.at(y).at(x) = path.len() | ||||
|     for (dx, dy) in offsets { | ||||
|       let (x2, y2) = (x + dx, y + dy) | ||||
|       if not in-grid(x2, y2) { | ||||
|         continue | ||||
|       } | ||||
|       let v = grid.at(y2).at(x2) | ||||
|       if v == 0 { | ||||
|         (x, y) = (x2, y2) | ||||
|         break | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   path.push((x, y)) | ||||
|   grid.at(y).at(x) = path.len() | ||||
|  | ||||
|   return count-shortcuts( | ||||
|     grid, w, h, | ||||
|     save, | ||||
|     path | ||||
|   ) | ||||
| } | ||||
|  | ||||
| #let examples = ( | ||||
|   (64, 1), | ||||
|   (40, 1), | ||||
|   (38, 1), | ||||
|   (36, 1), | ||||
|   (20, 1), | ||||
|   (12, 3), | ||||
|   (10, 2), | ||||
|   (8, 4), | ||||
|   (6, 2), | ||||
|   (4, 14), | ||||
|   (2, 14), | ||||
| ) | ||||
| #let n-tot = 0 | ||||
| #let examples2 = () | ||||
| #for (save, cnt) in examples { | ||||
|   n-tot += cnt | ||||
|   examples2.push((result: n-tot, args: (save: save))) | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   20, 1, | ||||
|   solve.with(save: 100), | ||||
|   example: examples2 | ||||
| ) | ||||
							
								
								
									
										0
									
								
								src/day20/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/day20/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										100
									
								
								src/day21/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										100
									
								
								src/day21/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,100 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let nums = ( | ||||
|   "7": (0, 0), | ||||
|   "8": (1, 0), | ||||
|   "9": (2, 0), | ||||
|   "4": (0, 1), | ||||
|   "5": (1, 1), | ||||
|   "6": (2, 1), | ||||
|   "1": (0, 2), | ||||
|   "2": (1, 2), | ||||
|   "3": (2, 2), | ||||
|   "0": (1, 3), | ||||
|   "A": (2, 3), | ||||
| ) | ||||
|  | ||||
| #let arrows = ( | ||||
|   "^": (1, 0), | ||||
|   "A": (2, 0), | ||||
|   "<": (0, 1), | ||||
|   "v": (1, 1), | ||||
|   ">": (2, 1), | ||||
| ) | ||||
|  | ||||
| #let get-num-path(code) = { | ||||
|   let path = "" | ||||
|   let (x, y) = nums.at("A") | ||||
|   for char in code { | ||||
|     let (x2, y2) = nums.at(char) | ||||
|     let dy = y2 - y | ||||
|     let dx = x2 - x | ||||
|     let ver = if dy == 0 { | ||||
|       "" | ||||
|     } else { | ||||
|       (if dy < 0 {"^"} else {"v"}) * calc.abs(dy) | ||||
|     } | ||||
|     let hor = if dx == 0 { | ||||
|       "" | ||||
|     } else { | ||||
|       (if dx < 0 {"<"} else {">"}) * calc.abs(dx) | ||||
|     } | ||||
|     path += if y == 3 and x2 == 0 { | ||||
|       ver + hor | ||||
|     } else { | ||||
|       hor + ver | ||||
|     } + "A" | ||||
|     (x, y) = (x2, y2) | ||||
|   } | ||||
|   return path | ||||
| } | ||||
|  | ||||
| #let get-dir-path(code) = { | ||||
|   let path = "" | ||||
|   let (x, y) = arrows.at("A") | ||||
|   for char in code { | ||||
|     let (x2, y2) = arrows.at(char) | ||||
|     let dy = y2 - y | ||||
|     let dx = x2 - x | ||||
|     let ver = if dy == 0 { | ||||
|       "" | ||||
|     } else { | ||||
|       (if dy < 0 {"^"} else {"v"}) * calc.abs(dy) | ||||
|     } | ||||
|     let hor = if dx == 0 { | ||||
|       "" | ||||
|     } else { | ||||
|       (if dx < 0 {"<"} else {">"}) * calc.abs(dx) | ||||
|     } | ||||
|     path += if x == 0 and dy < 0 { | ||||
|       hor + ver | ||||
|     } else { | ||||
|       ver + hor | ||||
|     } + "A" | ||||
|     (x, y) = (x2, y2) | ||||
|   } | ||||
|   return path | ||||
| } | ||||
|  | ||||
| #let get-path(code) = { | ||||
|   let num-path = get-num-path(code) | ||||
|   let dir-path-1 = get-dir-path(num-path) | ||||
|   let dir-path-2 = get-dir-path(dir-path-1) | ||||
|   return dir-path-2 | ||||
| } | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let codes = input.split("\n") | ||||
|   let total = 0 | ||||
|   for code in codes { | ||||
|     let len = get-path(code).len() | ||||
|     let num = int(code.slice(0, code.len() - 1)) | ||||
|     total += len * num | ||||
|   } | ||||
|   return total | ||||
| } | ||||
| #show-puzzle( | ||||
|   21, 1, | ||||
|   solve, | ||||
|   example: 126384 | ||||
| ) | ||||
							
								
								
									
										0
									
								
								src/day21/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/day21/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										61
									
								
								src/day22/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/day22/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let random(prev) = { | ||||
|   let step(a, shift) = { | ||||
|     let s = if shift < 0 { | ||||
|       a.bit-rshift(-shift) | ||||
|     } else { | ||||
|       a.bit-lshift(shift) | ||||
|     } | ||||
|     return s.bit-xor(a).bit-and(0xffffff) | ||||
|   } | ||||
|   let b = step(prev, 6) | ||||
|   let c = step(b, -5) | ||||
|   let d = step(c, 11) | ||||
|   return d | ||||
| } | ||||
|  | ||||
| #let solve(input, steps: 1) = { | ||||
|   let values = input.split("\n").map(int) | ||||
|   let total = 0 | ||||
|   for value in values { | ||||
|     for _ in range(steps) { | ||||
|       value = random(value) | ||||
|     } | ||||
|     total += value | ||||
|   } | ||||
|   return total | ||||
| } | ||||
|  | ||||
| #let examples = () | ||||
| #let results = ( | ||||
|   15887950, | ||||
|   16495136, | ||||
|   527345, | ||||
|   704524, | ||||
|   1553684, | ||||
|   12683156, | ||||
|   11100544, | ||||
|   12249484, | ||||
|   7753432, | ||||
|   5908254 | ||||
| ) | ||||
| #for (i, res) in results.enumerate() { | ||||
|   examples.push(( | ||||
|     result: res, | ||||
|     args: (steps: i + 1) | ||||
|   )) | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   22, 1, | ||||
|   solve.with(steps: 2000), | ||||
|   example: examples, | ||||
|   only-example: true | ||||
| ) | ||||
|  | ||||
| #show-result(18261820068) | ||||
|  | ||||
| //B = ((A << 6) ^ A) & 0xffffff\ | ||||
| //C = ((B >> 5) ^ B) & 0xffffff\ | ||||
| //D = ((C << 11) ^ C) & 0xffffff | ||||
							
								
								
									
										0
									
								
								src/day22/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/day22/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										50
									
								
								src/day23/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/day23/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,50 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let links = input.split("\n") | ||||
|  | ||||
|   let links-dict = (:) | ||||
|  | ||||
|   let to-test = () | ||||
|   for link in links { | ||||
|     let (a, b) = link.split("-") | ||||
|     if a not in links-dict { | ||||
|       links-dict.insert(a, ()) | ||||
|     } | ||||
|     if b not in links-dict { | ||||
|       links-dict.insert(b, ()) | ||||
|     } | ||||
|     links-dict.at(a).push(b) | ||||
|     links-dict.at(b).push(a) | ||||
|     if a.starts-with("t") { | ||||
|       to-test.push(a) | ||||
|     } | ||||
|     if b.starts-with("t") { | ||||
|       to-test.push(b) | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let total = 0 | ||||
|   let groups = () | ||||
|   for comp1 in to-test.dedup() { | ||||
|     for comp2 in links-dict.at(comp1) { | ||||
|       for comp3 in links-dict.at(comp2) { | ||||
|         if comp1 in links-dict.at(comp3) { | ||||
|           let group = (comp1, comp2, comp3).sorted() | ||||
|           if group not in groups { | ||||
|             total += 1 | ||||
|             groups.push(group) | ||||
|           } | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return total | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   23, 1, | ||||
|   solve, | ||||
|   example: 7 | ||||
| ) | ||||
							
								
								
									
										104
									
								
								src/day23/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								src/day23/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,104 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let bron-kerbosch(links, R, P, X) = { | ||||
|   if P.len() == 0 and X.len() == 0 { | ||||
|     return if R.len() > 2 { | ||||
|       R.sorted() | ||||
|     } else { | ||||
|       none | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let longest-len = 0 | ||||
|   let longest = none | ||||
|   let to-visit = P | ||||
|   for v in to-visit { | ||||
|     let neighbors = links.at(v) | ||||
|     let clique = bron-kerbosch( | ||||
|       links, | ||||
|       R + (v,), | ||||
|       P.filter(n => n in neighbors), | ||||
|       X.filter(n => n in neighbors) | ||||
|     ) | ||||
|     if clique != none { | ||||
|       let l = clique.len() | ||||
|       if longest == none or l > longest-len { | ||||
|         longest = clique | ||||
|         longest-len = l | ||||
|       } | ||||
|     } | ||||
|     let _ = P.remove(0) | ||||
|     X.push(v) | ||||
|   } | ||||
|   return longest | ||||
| } | ||||
|  | ||||
| #let bron-kerbosch2(links, R, P, X) = { | ||||
|   if P.len() == 0 and X.len() == 0 { | ||||
|     return if R.len() > 2 { | ||||
|       R.sorted() | ||||
|     } else { | ||||
|       none | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let pivot = if P.len() != 0 { | ||||
|     P.first() | ||||
|   } else { | ||||
|     X.first() | ||||
|   } | ||||
|   let pivot-neighbors = links.at(pivot) | ||||
|   let longest-len = 0 | ||||
|   let longest = none | ||||
|   let to-visit = P.filter(n => n not in pivot-neighbors) | ||||
|   for v in to-visit { | ||||
|     let neighbors = links.at(v) | ||||
|     let clique = bron-kerbosch2( | ||||
|       links, | ||||
|       R + (v,), | ||||
|       P.filter(n => n in neighbors), | ||||
|       X.filter(n => n in neighbors) | ||||
|     ) | ||||
|     if clique != none { | ||||
|       let l = clique.len() | ||||
|       if longest == none or l > longest-len { | ||||
|         longest = clique | ||||
|         longest-len = l | ||||
|       } | ||||
|     } | ||||
|     let _ = P.remove(P.position(n => n == v)) | ||||
|     X.push(v) | ||||
|   } | ||||
|   return longest | ||||
| } | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let links = input.split("\n") | ||||
|  | ||||
|   let links-dict = (:) | ||||
|  | ||||
|   let to-test = () | ||||
|   for link in links { | ||||
|     let (a, b) = link.split("-") | ||||
|     if a not in links-dict { | ||||
|       links-dict.insert(a, ()) | ||||
|     } | ||||
|     if b not in links-dict { | ||||
|       links-dict.insert(b, ()) | ||||
|     } | ||||
|     links-dict.at(a).push(b) | ||||
|     links-dict.at(b).push(a) | ||||
|   } | ||||
|  | ||||
|   let clique = bron-kerbosch2(links-dict, (), links-dict.keys(), ()) | ||||
|  | ||||
|   return clique.join(",") | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   23, 2, | ||||
|   solve, | ||||
|   example: "co,de,ka,ta", | ||||
|   only-example: true | ||||
| ) | ||||
| #show-result("ab,al,cq,cr,da,db,dr,fw,ly,mn,od,py,uh") | ||||
							
								
								
									
										78
									
								
								src/day24/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								src/day24/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,78 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let (inputs, gates) = input.split("\n\n") | ||||
|   let ids = () | ||||
|   inputs = inputs.split("\n").map(i => { | ||||
|     let (id, value) = i.split(": ") | ||||
|     return (id: id, value: value == "1") | ||||
|   }) | ||||
|   ids += inputs.map(i => i.id) | ||||
|   gates = gates.split("\n").map(g => { | ||||
|     let (gate, output) = g.split(" -> ") | ||||
|     let (i1, op, i2) = gate.split(" ") | ||||
|     return ( | ||||
|       in1: i1, | ||||
|       in2: i2, | ||||
|       op: op, | ||||
|       out: output | ||||
|     ) | ||||
|   }) | ||||
|   let gates-by-id = (:) | ||||
|   for gate in gates { | ||||
|     gates-by-id.insert(gate.out, gate) | ||||
|   } | ||||
|  | ||||
|   ids += gates.map(g => g.out) | ||||
|   ids = ids.dedup() | ||||
|   let dbg = (inputs, gates) | ||||
|  | ||||
|   let values = (:) | ||||
|   for input in inputs { | ||||
|     values.insert(input.id, input.value) | ||||
|   } | ||||
|  | ||||
|   let stack = ids.filter(id => id.starts-with("z")) | ||||
|   let output = (0,) * stack.len() | ||||
|   while stack.len() != 0 { | ||||
|     let v = stack.pop() | ||||
|     if v in values { | ||||
|       if v.starts-with("z") { | ||||
|         let i = int(v.slice(1)) | ||||
|         output.at(i) = int(values.at(v)) | ||||
|       } | ||||
|     } else { | ||||
|       stack.push(v) | ||||
|       let gate = gates-by-id.at(v) | ||||
|       if gate.in1 in values and gate.in2 in values { | ||||
|         let v1 = values.at(gate.in1) | ||||
|         let v2 = values.at(gate.in2) | ||||
|         let value = if gate.op == "AND" { | ||||
|           v1 and v2 | ||||
|         } else if gate.op == "OR" { | ||||
|           v1 or v2 | ||||
|         } else if gate.op == "XOR" { | ||||
|           (v1 or v2) and not (v1 and v2) | ||||
|         } | ||||
|         values.insert(v, value) | ||||
|       } else { | ||||
|         stack.push(gate.in1) | ||||
|         stack.push(gate.in2) | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   let result = output.rev() | ||||
|                      .fold(0, (a, b) => a.bit-lshift(1).bit-or(b)) | ||||
|  | ||||
|   return result | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   24, 1, | ||||
|   solve, | ||||
|   example: ( | ||||
|     "1": 4, | ||||
|     "2": 2024 | ||||
|   ) | ||||
| ) | ||||
							
								
								
									
										0
									
								
								src/day24/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/day24/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										61
									
								
								src/day25/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								src/day25/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,61 @@ | ||||
| #import "/src/utils.typ": * | ||||
|  | ||||
| #let parse-schematic(schematic) = { | ||||
|   let lines = schematic.split("\n") | ||||
|   let is-key = "." in schematic.first() | ||||
|  | ||||
|   let n-cols = lines.first().len() | ||||
|   let n-rows = lines.len() | ||||
|   let heights = () | ||||
|  | ||||
|   for x in range(n-cols) { | ||||
|     let y = if is-key {n-rows - 2} else {1} | ||||
|     let h = 0 | ||||
|     for i in range(n-rows) { | ||||
|       if lines.at(y).at(x) == "." { | ||||
|         break | ||||
|       } | ||||
|       h += 1 | ||||
|       y += if is-key {-1} else {1} | ||||
|     } | ||||
|     heights.push(h) | ||||
|   } | ||||
|  | ||||
|   return (if is-key {"key"} else {"lock"}, heights) | ||||
| } | ||||
|  | ||||
| #let fits(lock, key) = { | ||||
|   let tmp = lock.zip(key).map(p => p.sum()) | ||||
|   return calc.max(..tmp) <= 5 | ||||
| } | ||||
|  | ||||
| #let solve(input) = { | ||||
|   let schematics = input.split("\n\n") | ||||
|  | ||||
|   let locks = () | ||||
|   let keys = () | ||||
|   for schematic in schematics { | ||||
|     let (type, heights) = parse-schematic(schematic) | ||||
|     if type == "key" { | ||||
|       keys.push(heights) | ||||
|     } else { | ||||
|       locks.push(heights) | ||||
|     } | ||||
|   } | ||||
|   let total = 0 | ||||
|   for key in keys { | ||||
|     for lock in locks { | ||||
|       if fits(lock, key) { | ||||
|         total += 1 | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   return total | ||||
| } | ||||
|  | ||||
| #show-puzzle( | ||||
|   25, 1, | ||||
|   solve, | ||||
|   example: 3 | ||||
| ) | ||||
							
								
								
									
										0
									
								
								src/day25/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								src/day25/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										
											BIN
										
									
								
								src/main.pdf
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main.pdf
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| @@ -197,7 +197,9 @@ | ||||
|   set text(font: "Source Sans 3") | ||||
|   set page( | ||||
|     footer: context { | ||||
|       align(center, counter(page).display("1 / 1", both: true)) | ||||
|       if counter(page).get().first() != 1 { | ||||
|         align(center, counter(page).display("1 / 1", both: true)) | ||||
|       } | ||||
|     } | ||||
|   ) | ||||
|   body | ||||
|   | ||||
		Reference in New Issue
	
	Block a user