Compare commits
	
		
			31 Commits
		
	
	
		
			a35be7ec92
			...
			main
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						
						
							
						
						93e365d10b
	
				 | 
					
					
						|||
| 
						
						
							
						
						9300d786f8
	
				 | 
					
					
						|||
| 
						
						
							
						
						ec75800078
	
				 | 
					
					
						|||
| 
						
						
							
						
						16a0d9b3f8
	
				 | 
					
					
						|||
| 
						
						
							
						
						d830919adc
	
				 | 
					
					
						|||
| 
						
						
							
						
						056e29b96c
	
				 | 
					
					
						|||
| 
						
						
							
						
						47fbeb1ce9
	
				 | 
					
					
						|||
| 
						
						
							
						
						4826c0d21e
	
				 | 
					
					
						|||
| 
						
						
							
						
						0950e76d8d
	
				 | 
					
					
						|||
| 
						
						
							
						
						5921a90c73
	
				 | 
					
					
						|||
| 
						
						
							
						
						4a429bd62b
	
				 | 
					
					
						|||
| 
						
						
							
						
						953716b95a
	
				 | 
					
					
						|||
| 
						
						
							
						
						43c6d9e325
	
				 | 
					
					
						|||
| 
						
						
							
						
						927feb071e
	
				 | 
					
					
						|||
| 
						
						
							
						
						5107cf7f82
	
				 | 
					
					
						|||
| 
						
						
							
						
						9a7eef5186
	
				 | 
					
					
						|||
| 
						
						
							
						
						dd1e8a81fd
	
				 | 
					
					
						|||
| 
						
						
							
						
						05f1f99ac1
	
				 | 
					
					
						|||
| 
						
						
							
						
						befa6403ba
	
				 | 
					
					
						|||
| 
						
						
							
						
						ab60a501f5
	
				 | 
					
					
						|||
| 
						
						
							
						
						6ab8ef6d26
	
				 | 
					
					
						|||
| 
						
						
							
						
						ad4adc2372
	
				 | 
					
					
						|||
| 
						
						
							
						
						6caa3516e6
	
				 | 
					
					
						|||
| 
						
						
							
						
						c23ff2ea4b
	
				 | 
					
					
						|||
| 
						
						
							
						
						60a9ee3d2d
	
				 | 
					
					
						|||
| 
						
						
							
						
						a6edde9139
	
				 | 
					
					
						|||
| 
						
						
							
						
						2d0001ec12
	
				 | 
					
					
						|||
| 
						
						
							
						
						32fc0862a7
	
				 | 
					
					
						|||
| 
						
						
							
						
						8dddcd6224
	
				 | 
					
					
						|||
| 
						
						
							
						
						242eaeb634
	
				 | 
					
					
						|||
| 
						
						
							
						
						2cbd13d224
	
				 | 
					
					
						
							
								
								
									
										75
									
								
								day14.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								day14.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,75 @@
 | 
				
			|||||||
 | 
					import re
 | 
				
			||||||
 | 
					import pygame
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					with open("res/inputs/day14.txt", "r") as f:
 | 
				
			||||||
 | 
					    lines = f.read().split("\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bots = []
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					for line in lines:
 | 
				
			||||||
 | 
					    m = re.match("^p=(.*?),(.*?) v=(.*?),(.*?)$", line)
 | 
				
			||||||
 | 
					    bot = {
 | 
				
			||||||
 | 
					        "pos": {
 | 
				
			||||||
 | 
					            "x": int(m.group(1)),
 | 
				
			||||||
 | 
					            "y": int(m.group(2)),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        "vel": {
 | 
				
			||||||
 | 
					            "x": int(m.group(3)),
 | 
				
			||||||
 | 
					            "y": int(m.group(4)),
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bots.append(bot)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					W = 101
 | 
				
			||||||
 | 
					H = 103
 | 
				
			||||||
 | 
					pygame.init()
 | 
				
			||||||
 | 
					win = pygame.display.set_mode([W, H])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def display():
 | 
				
			||||||
 | 
					    win.fill(0)
 | 
				
			||||||
 | 
					    for bot in bots:
 | 
				
			||||||
 | 
					        win.set_at((bot["pos"]["x"], bot["pos"]["y"]), (255, 255, 255))
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    pygame.display.flip()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def move(f):
 | 
				
			||||||
 | 
					    for bot in bots:
 | 
				
			||||||
 | 
					        bot["pos"]["x"] = (bot["pos"]["x"] + bot["vel"]["x"] * f) % W
 | 
				
			||||||
 | 
					        bot["pos"]["y"] = (bot["pos"]["y"] + bot["vel"]["y"] * f) % H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					clock = pygame.time.Clock()
 | 
				
			||||||
 | 
					running = True
 | 
				
			||||||
 | 
					step = 0
 | 
				
			||||||
 | 
					update = True
 | 
				
			||||||
 | 
					auto = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					while running:
 | 
				
			||||||
 | 
					    for event in pygame.event.get():
 | 
				
			||||||
 | 
					        if event.type == pygame.QUIT:
 | 
				
			||||||
 | 
					            running = False
 | 
				
			||||||
 | 
					        elif event.type == pygame.KEYDOWN:
 | 
				
			||||||
 | 
					            if event.key == pygame.K_ESCAPE:
 | 
				
			||||||
 | 
					                running = False
 | 
				
			||||||
 | 
					            elif event.key == pygame.K_RIGHT:
 | 
				
			||||||
 | 
					                step += 1
 | 
				
			||||||
 | 
					                move(1)
 | 
				
			||||||
 | 
					                update = True
 | 
				
			||||||
 | 
					            elif event.key == pygame.K_LEFT:
 | 
				
			||||||
 | 
					                step -= 1
 | 
				
			||||||
 | 
					                move(-1)
 | 
				
			||||||
 | 
					                update = True
 | 
				
			||||||
 | 
					            elif event.key == pygame.K_SPACE:
 | 
				
			||||||
 | 
					                auto = not auto
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if auto:
 | 
				
			||||||
 | 
					        move(1)
 | 
				
			||||||
 | 
					        step += 1
 | 
				
			||||||
 | 
					        update = True
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if update:
 | 
				
			||||||
 | 
					        print(step)
 | 
				
			||||||
 | 
					        pygame.display.set_caption(f"Day 14 - step {step} - {clock.get_fps():.2f}fps")
 | 
				
			||||||
 | 
					        display()
 | 
				
			||||||
 | 
					        update = False
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    clock.tick(20)
 | 
				
			||||||
							
								
								
									
										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: 119 KiB After Width: | Height: | Size: 135 KiB  | 
@@ -16,3 +16,35 @@
 | 
				
			|||||||
  stars: 2
 | 
					  stars: 2
 | 
				
			||||||
9:
 | 
					9:
 | 
				
			||||||
  stars: 2
 | 
					  stars: 2
 | 
				
			||||||
 | 
					10:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					11:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					12:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					13:
 | 
				
			||||||
 | 
					  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
 | 
				
			||||||
							
								
								
									
										8
									
								
								res/examples/day10.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								res/examples/day10.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,8 @@
 | 
				
			|||||||
 | 
					89010123
 | 
				
			||||||
 | 
					78121874
 | 
				
			||||||
 | 
					87430965
 | 
				
			||||||
 | 
					96549874
 | 
				
			||||||
 | 
					45678903
 | 
				
			||||||
 | 
					32019012
 | 
				
			||||||
 | 
					01329801
 | 
				
			||||||
 | 
					10456732
 | 
				
			||||||
							
								
								
									
										1
									
								
								res/examples/day11.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								res/examples/day11.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					125 17
 | 
				
			||||||
							
								
								
									
										4
									
								
								res/examples/day12_1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								res/examples/day12_1.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					AAAA
 | 
				
			||||||
 | 
					BBCD
 | 
				
			||||||
 | 
					BBCC
 | 
				
			||||||
 | 
					EEEC
 | 
				
			||||||
							
								
								
									
										5
									
								
								res/examples/day12_2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								res/examples/day12_2.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					OOOOO
 | 
				
			||||||
 | 
					OXOXO
 | 
				
			||||||
 | 
					OOOOO
 | 
				
			||||||
 | 
					OXOXO
 | 
				
			||||||
 | 
					OOOOO
 | 
				
			||||||
							
								
								
									
										10
									
								
								res/examples/day12_3.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								res/examples/day12_3.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					RRRRIICCFF
 | 
				
			||||||
 | 
					RRRRIICCCF
 | 
				
			||||||
 | 
					VVRRRCCFFF
 | 
				
			||||||
 | 
					VVRCCCJFFF
 | 
				
			||||||
 | 
					VVVVCJJCFE
 | 
				
			||||||
 | 
					VVIVCCJJEE
 | 
				
			||||||
 | 
					VVIIICJJEE
 | 
				
			||||||
 | 
					MIIIIIJJEE
 | 
				
			||||||
 | 
					MIIISIJEEE
 | 
				
			||||||
 | 
					MMMISSJEEE
 | 
				
			||||||
							
								
								
									
										5
									
								
								res/examples/day12_4.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								res/examples/day12_4.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,5 @@
 | 
				
			|||||||
 | 
					EEEEE
 | 
				
			||||||
 | 
					EXXXX
 | 
				
			||||||
 | 
					EEEEE
 | 
				
			||||||
 | 
					EXXXX
 | 
				
			||||||
 | 
					EEEEE
 | 
				
			||||||
							
								
								
									
										6
									
								
								res/examples/day12_5.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								res/examples/day12_5.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					AAAAAA
 | 
				
			||||||
 | 
					AAABBA
 | 
				
			||||||
 | 
					AAABBA
 | 
				
			||||||
 | 
					ABBAAA
 | 
				
			||||||
 | 
					ABBAAA
 | 
				
			||||||
 | 
					AAAAAA
 | 
				
			||||||
							
								
								
									
										15
									
								
								res/examples/day13.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								res/examples/day13.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					Button A: X+94, Y+34
 | 
				
			||||||
 | 
					Button B: X+22, Y+67
 | 
				
			||||||
 | 
					Prize: X=8400, Y=5400
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Button A: X+26, Y+66
 | 
				
			||||||
 | 
					Button B: X+67, Y+21
 | 
				
			||||||
 | 
					Prize: X=12748, Y=12176
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Button A: X+17, Y+86
 | 
				
			||||||
 | 
					Button B: X+84, Y+37
 | 
				
			||||||
 | 
					Prize: X=7870, Y=6450
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Button A: X+69, Y+23
 | 
				
			||||||
 | 
					Button B: X+27, Y+71
 | 
				
			||||||
 | 
					Prize: X=18641, Y=10279
 | 
				
			||||||
							
								
								
									
										12
									
								
								res/examples/day14.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								res/examples/day14.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					p=0,4 v=3,-3
 | 
				
			||||||
 | 
					p=6,3 v=-1,-3
 | 
				
			||||||
 | 
					p=10,3 v=-1,2
 | 
				
			||||||
 | 
					p=2,0 v=2,-1
 | 
				
			||||||
 | 
					p=0,0 v=1,3
 | 
				
			||||||
 | 
					p=3,0 v=-2,-2
 | 
				
			||||||
 | 
					p=7,6 v=-1,-3
 | 
				
			||||||
 | 
					p=3,0 v=-1,-2
 | 
				
			||||||
 | 
					p=9,3 v=2,3
 | 
				
			||||||
 | 
					p=7,3 v=-1,2
 | 
				
			||||||
 | 
					p=2,4 v=2,-3
 | 
				
			||||||
 | 
					p=9,5 v=-3,-3
 | 
				
			||||||
							
								
								
									
										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 @@
 | 
				
			|||||||
 | 
					#####
 | 
				
			||||||
 | 
					.####
 | 
				
			||||||
 | 
					.####
 | 
				
			||||||
 | 
					.####
 | 
				
			||||||
 | 
					.#.#.
 | 
				
			||||||
 | 
					.#...
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#####
 | 
				
			||||||
 | 
					##.##
 | 
				
			||||||
 | 
					.#.##
 | 
				
			||||||
 | 
					...##
 | 
				
			||||||
 | 
					...#.
 | 
				
			||||||
 | 
					...#.
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					#....
 | 
				
			||||||
 | 
					#....
 | 
				
			||||||
 | 
					#...#
 | 
				
			||||||
 | 
					#.#.#
 | 
				
			||||||
 | 
					#.###
 | 
				
			||||||
 | 
					#####
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					#.#..
 | 
				
			||||||
 | 
					###..
 | 
				
			||||||
 | 
					###.#
 | 
				
			||||||
 | 
					###.#
 | 
				
			||||||
 | 
					#####
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					#....
 | 
				
			||||||
 | 
					#.#..
 | 
				
			||||||
 | 
					#.#.#
 | 
				
			||||||
 | 
					#####
 | 
				
			||||||
							
								
								
									
										143
									
								
								src/day10/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								src/day10/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw, matrix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let offsets = (
 | 
				
			||||||
 | 
					  (-1, 0),
 | 
				
			||||||
 | 
					  (0, -1),
 | 
				
			||||||
 | 
					  (1, 0),
 | 
				
			||||||
 | 
					  (0, 1)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-grid(w, h, x, y) = {
 | 
				
			||||||
 | 
					  return 0 <= x and x < w and 0 <= y and y < h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let count-paths(grid, w, h, ox, oy) = {
 | 
				
			||||||
 | 
					  let tails = ()
 | 
				
			||||||
 | 
					  let in-grid = in-grid.with(w, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let to-visit = ((ox, oy),)
 | 
				
			||||||
 | 
					  while to-visit.len() != 0 {
 | 
				
			||||||
 | 
					    let (x, y) = to-visit.remove(0)
 | 
				
			||||||
 | 
					    let v = grid.at(y).at(x)
 | 
				
			||||||
 | 
					    for (dx, dy) in offsets {
 | 
				
			||||||
 | 
					      let (x2, y2) = (x + dx, y + dy)
 | 
				
			||||||
 | 
					      if not in-grid(x2, y2) {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let v2 = grid.at(y2).at(x2)
 | 
				
			||||||
 | 
					      if v2 == v + 1 {
 | 
				
			||||||
 | 
					        let pos2 = (x2, y2)
 | 
				
			||||||
 | 
					        if v2 == 9 {
 | 
				
			||||||
 | 
					          if pos2 not in tails {
 | 
				
			||||||
 | 
					            tails.push(pos2)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          to-visit.push(pos2)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return tails.len()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let get-paths(grid, w, h, ox, oy) = {
 | 
				
			||||||
 | 
					  let paths = ()
 | 
				
			||||||
 | 
					  let tails = ()
 | 
				
			||||||
 | 
					  let in-grid = in-grid.with(w, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let to-visit = ((ox, oy),)
 | 
				
			||||||
 | 
					  while to-visit.len() != 0 {
 | 
				
			||||||
 | 
					    let (x, y) = to-visit.remove(0)
 | 
				
			||||||
 | 
					    let v = grid.at(y).at(x)
 | 
				
			||||||
 | 
					    for (dx, dy) in offsets {
 | 
				
			||||||
 | 
					      let (x2, y2) = (x + dx, y + dy)
 | 
				
			||||||
 | 
					      if not in-grid(x2, y2) {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let v2 = grid.at(y2).at(x2)
 | 
				
			||||||
 | 
					      if v2 == v + 1 {
 | 
				
			||||||
 | 
					        let pos2 = (x2, y2)
 | 
				
			||||||
 | 
					        if v2 == 9 {
 | 
				
			||||||
 | 
					          if pos2 not in tails {
 | 
				
			||||||
 | 
					            tails.push(pos2)
 | 
				
			||||||
 | 
					            paths.push(((ox, oy), (x2, y2)))
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          to-visit.push(pos2)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return paths
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let grid = input.split("\n").map(l => l.clusters().map(int))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let count-paths = count-paths.with(grid, w, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      if grid.at(y).at(x) == 0 {
 | 
				
			||||||
 | 
					        total += count-paths(x, y)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let grid = input.split("\n").map(l => l.clusters().map(int))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let count-paths = count-paths.with(grid, w, h)
 | 
				
			||||||
 | 
					  let get-paths = get-paths.with(grid, w, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  canvas({
 | 
				
			||||||
 | 
					    let starts = ()
 | 
				
			||||||
 | 
					    let c
 | 
				
			||||||
 | 
					    for y in range(h) {
 | 
				
			||||||
 | 
					      for x in range(w) {
 | 
				
			||||||
 | 
					        c = grid.at(y).at(x)
 | 
				
			||||||
 | 
					        draw.rect(
 | 
				
			||||||
 | 
					          (x, -y),
 | 
				
			||||||
 | 
					          (x + 1, -y - 1),
 | 
				
			||||||
 | 
					          fill: black.lighten((9 - c) / 9 * 90% + 10%)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if c == 0 {
 | 
				
			||||||
 | 
					          starts.push((x, y))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (ox, oy) in starts {
 | 
				
			||||||
 | 
					      let paths = get-paths(ox, oy)
 | 
				
			||||||
 | 
					      for path in paths {
 | 
				
			||||||
 | 
					        draw.line(
 | 
				
			||||||
 | 
					          ..path.map(
 | 
				
			||||||
 | 
					            ((x, y)) => (x + .5, -y - .5)
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          stroke: red,
 | 
				
			||||||
 | 
					          mark: (end: ">")
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  10, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 36,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										74
									
								
								src/day10/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/day10/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#let offsets = (
 | 
				
			||||||
 | 
					  (-1, 0),
 | 
				
			||||||
 | 
					  (0, -1),
 | 
				
			||||||
 | 
					  (1, 0),
 | 
				
			||||||
 | 
					  (0, 1)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-grid(w, h, x, y) = {
 | 
				
			||||||
 | 
					  return 0 <= x and x < w and 0 <= y and y < h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let count-paths(grid, w, h, ox, oy) = {
 | 
				
			||||||
 | 
					  let in-grid = in-grid.with(w, h)
 | 
				
			||||||
 | 
					  let rating = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let to-visit = ((ox, oy),)
 | 
				
			||||||
 | 
					  while to-visit.len() != 0 {
 | 
				
			||||||
 | 
					    let (x, y) = to-visit.remove(0)
 | 
				
			||||||
 | 
					    let v = grid.at(y).at(x)
 | 
				
			||||||
 | 
					    let branches = 0
 | 
				
			||||||
 | 
					    for (dx, dy) in offsets {
 | 
				
			||||||
 | 
					      let (x2, y2) = (x + dx, y + dy)
 | 
				
			||||||
 | 
					      if not in-grid(x2, y2) {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let v2 = grid.at(y2).at(x2)
 | 
				
			||||||
 | 
					      if v2 == v + 1 {
 | 
				
			||||||
 | 
					        let pos2 = (x2, y2)
 | 
				
			||||||
 | 
					        branches += 1
 | 
				
			||||||
 | 
					        if v2 != 9 {
 | 
				
			||||||
 | 
					          to-visit.push(pos2)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rating += if v == 0 {
 | 
				
			||||||
 | 
					      branches
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      branches - 1  // If no branch -> -1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return rating
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let grid = input.split("\n").map(l => l.clusters().map(int))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let count-paths = count-paths.with(grid, w, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let ratings = ()
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      if grid.at(y).at(x) == 0 {
 | 
				
			||||||
 | 
					        let rating = count-paths(x, y)
 | 
				
			||||||
 | 
					        total += rating
 | 
				
			||||||
 | 
					        ratings.push(rating)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let a = ratings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  10, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 81
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										64
									
								
								src/day11/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/day11/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas
 | 
				
			||||||
 | 
					#import "@preview/cetz-plot:0.1.0": plot
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let process(rock) = {
 | 
				
			||||||
 | 
					  if rock == 0 {
 | 
				
			||||||
 | 
					    return (1,)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let rock-str = str(rock)
 | 
				
			||||||
 | 
					  if calc.rem(rock-str.len(), 2) == 0 {
 | 
				
			||||||
 | 
					    let hl = calc.div-euclid(rock-str.len(), 2)
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      int(rock-str.slice(0, hl)),
 | 
				
			||||||
 | 
					      int(rock-str.slice(hl))
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return (rock * 2024,)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let blink(rocks) = {
 | 
				
			||||||
 | 
					  let new-rocks = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for rock in rocks {
 | 
				
			||||||
 | 
					    new-rocks += process(rock)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return new-rocks
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let rocks = input.split(" ").map(int)
 | 
				
			||||||
 | 
					  for _ in range(25) {
 | 
				
			||||||
 | 
					    rocks = blink(rocks)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return rocks.len()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let rocks = input.split(" ").map(int)
 | 
				
			||||||
 | 
					  let values = (rocks.len(),)
 | 
				
			||||||
 | 
					  for _ in range(25) {
 | 
				
			||||||
 | 
					    rocks = blink(rocks)
 | 
				
			||||||
 | 
					    values.push(rocks.len())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  canvas({
 | 
				
			||||||
 | 
					    plot.plot(
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
					        plot.add(range(26).zip(values))
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      size: (6,6),
 | 
				
			||||||
 | 
					      x-tick-step: 5,
 | 
				
			||||||
 | 
					      y-tick-step: 10000,
 | 
				
			||||||
 | 
					      x-label: "Blinks",
 | 
				
			||||||
 | 
					      y-label: "Rocks"
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  11, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 55312,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										36
									
								
								src/day11/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/day11/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,36 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let process(rock, depth) = {
 | 
				
			||||||
 | 
					  if depth == 0 {
 | 
				
			||||||
 | 
					    return 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if rock == 0 {
 | 
				
			||||||
 | 
					    return process(1, depth - 1)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let rock-str = str(rock)
 | 
				
			||||||
 | 
					  if calc.rem(rock-str.len(), 2) == 0 {
 | 
				
			||||||
 | 
					    let hl = calc.div-euclid(rock-str.len(), 2)
 | 
				
			||||||
 | 
					    let a = int(rock-str.slice(0, hl))
 | 
				
			||||||
 | 
					    let b = int(rock-str.slice(hl))
 | 
				
			||||||
 | 
					    return process(a, depth - 1) + process(b, depth - 1)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return process(rock * 2024, depth - 1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let rocks = input.split(" ").map(int)
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for rock in rocks {
 | 
				
			||||||
 | 
					    total += process(rock, 75)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  11, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  only-example: true
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Too long to recompile everytime
 | 
				
			||||||
 | 
					#show-result(228651922369703)
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/day12/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/day12/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let offsets = (
 | 
				
			||||||
 | 
					  (-1, 0),
 | 
				
			||||||
 | 
					  (0, -1),
 | 
				
			||||||
 | 
					  (1, 0),
 | 
				
			||||||
 | 
					  (0, 1)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-grid(w, h, x, y) = {
 | 
				
			||||||
 | 
					  return 0 <= x and x < w and 0 <= y and y < h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let grid = input.split("\n").map(l => l.clusters())
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					  let in-grid = in-grid.with(w, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let visited = ((false,) * w,) * h
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      if visited.at(y).at(x) {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let char = grid.at(y).at(x)
 | 
				
			||||||
 | 
					      let cells = ()
 | 
				
			||||||
 | 
					      let borders = 0
 | 
				
			||||||
 | 
					      let next-cells = ((x, y),)
 | 
				
			||||||
 | 
					      while next-cells.len() != 0 {
 | 
				
			||||||
 | 
					        let (cx, cy) = next-cells.remove(0)
 | 
				
			||||||
 | 
					        cells.push((cx, cy))
 | 
				
			||||||
 | 
					        visited.at(cy).at(cx) = true
 | 
				
			||||||
 | 
					        for (dx, dy) in offsets {
 | 
				
			||||||
 | 
					          let (x2, y2) = (cx + dx, cy + dy)
 | 
				
			||||||
 | 
					          if (x2, y2) in cells or (x2, y2) in next-cells {
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if in-grid(x2, y2) {
 | 
				
			||||||
 | 
					            let char2 = grid.at(y2).at(x2)
 | 
				
			||||||
 | 
					            if char2 == char {
 | 
				
			||||||
 | 
					              next-cells.push((x2, y2))
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					              borders += 1
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            borders += 1
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      total += borders * cells.len()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  12, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: (
 | 
				
			||||||
 | 
					    "1": 140,
 | 
				
			||||||
 | 
					    "2": 772,
 | 
				
			||||||
 | 
					    "3": 1930
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										151
									
								
								src/day12/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										151
									
								
								src/day12/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,151 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let offsets = (
 | 
				
			||||||
 | 
					  (-1, 0),
 | 
				
			||||||
 | 
					  (0, -1),
 | 
				
			||||||
 | 
					  (1, 0),
 | 
				
			||||||
 | 
					  (0, 1)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-grid(w, h, x, y) = {
 | 
				
			||||||
 | 
					  return 0 <= x and x < w and 0 <= y and y < h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let grid = input.split("\n").map(l => l.clusters())
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					  let in-grid = in-grid.with(w, h)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let zone-grid = ((none,) * w,) * h
 | 
				
			||||||
 | 
					  let zone-id = 0
 | 
				
			||||||
 | 
					  let zone-sides = ()
 | 
				
			||||||
 | 
					  let zone-areas = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      if zone-grid.at(y).at(x) != none {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let char = grid.at(y).at(x)
 | 
				
			||||||
 | 
					      let area = 0
 | 
				
			||||||
 | 
					      let borders = 0
 | 
				
			||||||
 | 
					      let next-cells = ((x, y),)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while next-cells.len() != 0 {
 | 
				
			||||||
 | 
					        let (cx, cy) = next-cells.remove(0)
 | 
				
			||||||
 | 
					        zone-grid.at(cy).at(cx) = zone-id
 | 
				
			||||||
 | 
					        area += 1
 | 
				
			||||||
 | 
					        for (dx, dy) in offsets {
 | 
				
			||||||
 | 
					          let (x2, y2) = (cx + dx, cy + dy)
 | 
				
			||||||
 | 
					          if (x2, y2) in next-cells {
 | 
				
			||||||
 | 
					            continue
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					          if in-grid(x2, y2) {
 | 
				
			||||||
 | 
					            if zone-grid.at(y2).at(x2) == zone-id {
 | 
				
			||||||
 | 
					              continue
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					            let char2 = grid.at(y2).at(x2)
 | 
				
			||||||
 | 
					            if char2 == char {
 | 
				
			||||||
 | 
					              next-cells.push((x2, y2))
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      zone-areas.push(area)
 | 
				
			||||||
 | 
					      zone-sides.push(0)
 | 
				
			||||||
 | 
					      zone-id += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let first-zone0 = -1
 | 
				
			||||||
 | 
					  let last-zone0 = -1
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    let first-zone = zone-grid.at(y).at(0)
 | 
				
			||||||
 | 
					    let last-zone = zone-grid.at(y).at(w - 1)
 | 
				
			||||||
 | 
					    if first-zone0 != first-zone {
 | 
				
			||||||
 | 
					      zone-sides.at(first-zone) += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if last-zone0 != last-zone {
 | 
				
			||||||
 | 
					      zone-sides.at(last-zone) += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    first-zone0 = first-zone
 | 
				
			||||||
 | 
					    last-zone0 = last-zone
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  first-zone0 = -1
 | 
				
			||||||
 | 
					  last-zone0 = -1
 | 
				
			||||||
 | 
					  for x in range(w) {
 | 
				
			||||||
 | 
					    let first-zone = zone-grid.at(0).at(x)
 | 
				
			||||||
 | 
					    let last-zone = zone-grid.at(h - 1).at(x)
 | 
				
			||||||
 | 
					    if first-zone0 != first-zone {
 | 
				
			||||||
 | 
					      zone-sides.at(first-zone) += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if last-zone0 != last-zone {
 | 
				
			||||||
 | 
					      zone-sides.at(last-zone) += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    first-zone0 = first-zone
 | 
				
			||||||
 | 
					    last-zone0 = last-zone
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for x in range(w - 1) {
 | 
				
			||||||
 | 
					    let zone-a0 = -1
 | 
				
			||||||
 | 
					    let zone-b0 = -1
 | 
				
			||||||
 | 
					    for y in range(h) {
 | 
				
			||||||
 | 
					      let zone-a = zone-grid.at(y).at(x)
 | 
				
			||||||
 | 
					      let zone-b = zone-grid.at(y).at(x + 1)
 | 
				
			||||||
 | 
					      if zone-a != zone-b {
 | 
				
			||||||
 | 
					        if zone-a != zone-a0 {
 | 
				
			||||||
 | 
					          zone-sides.at(zone-a) += 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if zone-b != zone-b0 {
 | 
				
			||||||
 | 
					          zone-sides.at(zone-b) += 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        zone-a0 = zone-a
 | 
				
			||||||
 | 
					        zone-b0 = zone-b
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        zone-a0 = -1
 | 
				
			||||||
 | 
					        zone-b0 = -1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for y in range(h - 1) {
 | 
				
			||||||
 | 
					    let zone-a0 = -1
 | 
				
			||||||
 | 
					    let zone-b0 = -1
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      let zone-a = zone-grid.at(y).at(x)
 | 
				
			||||||
 | 
					      let zone-b = zone-grid.at(y + 1).at(x)
 | 
				
			||||||
 | 
					      if zone-a != zone-b {
 | 
				
			||||||
 | 
					        if zone-a != zone-a0 {
 | 
				
			||||||
 | 
					          zone-sides.at(zone-a) += 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if zone-b != zone-b0 {
 | 
				
			||||||
 | 
					          zone-sides.at(zone-b) += 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        zone-a0 = zone-a
 | 
				
			||||||
 | 
					        zone-b0 = zone-b
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        zone-a0 = -1
 | 
				
			||||||
 | 
					        zone-b0 = -1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = range(zone-id).map(i => {
 | 
				
			||||||
 | 
					    zone-sides.at(i) * zone-areas.at(i)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					  return total.sum()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  12, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: (
 | 
				
			||||||
 | 
					    "1": 80,
 | 
				
			||||||
 | 
					    "2": 436,
 | 
				
			||||||
 | 
					    "3": 1206,
 | 
				
			||||||
 | 
					    "4": 236,
 | 
				
			||||||
 | 
					    "5": 368
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										86
									
								
								src/day13/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/day13/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let parse-machine(lines) = {
 | 
				
			||||||
 | 
					  let lines = lines.split("\n")
 | 
				
			||||||
 | 
					  let match-a = lines.at(0).match(regex("Button A: X\\+(\d+), Y\\+(\d+)"))
 | 
				
			||||||
 | 
					  let match-b = lines.at(1).match(regex("Button B: X\\+(\d+), Y\\+(\d+)"))
 | 
				
			||||||
 | 
					  let match-p = lines.at(2).match(regex("Prize: X=(\d+), Y=(\d+)"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    a: (
 | 
				
			||||||
 | 
					      x: int(match-a.captures.first()),
 | 
				
			||||||
 | 
					      y: int(match-a.captures.last()),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    b: (
 | 
				
			||||||
 | 
					      x: int(match-b.captures.first()),
 | 
				
			||||||
 | 
					      y: int(match-b.captures.last()),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    prize: (
 | 
				
			||||||
 | 
					      x: int(match-p.captures.first()),
 | 
				
			||||||
 | 
					      y: int(match-p.captures.last()),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-line(v1, v2) = {
 | 
				
			||||||
 | 
					  let f1 = v2.x / v1.x
 | 
				
			||||||
 | 
					  let f2 = v2.y / v1.y
 | 
				
			||||||
 | 
					  return f1 == f2
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let machines = input.split("\n\n")
 | 
				
			||||||
 | 
					  machines = machines.map(parse-machine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for m in machines {
 | 
				
			||||||
 | 
					    let totals = ()
 | 
				
			||||||
 | 
					    let are-inline = in-line(m.a, m.b)
 | 
				
			||||||
 | 
					    for b in range(101) {
 | 
				
			||||||
 | 
					      let bx = b * m.b.x
 | 
				
			||||||
 | 
					      let by = b * m.b.y
 | 
				
			||||||
 | 
					      let rx = m.prize.x - bx
 | 
				
			||||||
 | 
					      let ry = m.prize.y - by
 | 
				
			||||||
 | 
					      if rx < 0 or ry < 0 {
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let rax = calc.rem(
 | 
				
			||||||
 | 
					        rx,
 | 
				
			||||||
 | 
					        m.a.x
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      let ray = calc.rem(
 | 
				
			||||||
 | 
					        ry,
 | 
				
			||||||
 | 
					        m.a.y
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      if rax != 0 or ray != 0 {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let a1 = calc.div-euclid(
 | 
				
			||||||
 | 
					        rx,
 | 
				
			||||||
 | 
					        m.a.x
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      let a2 = calc.div-euclid(
 | 
				
			||||||
 | 
					        ry,
 | 
				
			||||||
 | 
					        m.a.y
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      if a1 != a2 or a1 > 100 {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      totals.push(b + a1 * 3)
 | 
				
			||||||
 | 
					      if not are-inline {
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if totals.len() != 0 {
 | 
				
			||||||
 | 
					      total += calc.min(..totals)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  13, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 480
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										74
									
								
								src/day13/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/day13/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,74 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let parse-machine(lines) = {
 | 
				
			||||||
 | 
					  let lines = lines.split("\n")
 | 
				
			||||||
 | 
					  let match-a = lines.at(0).match(regex("Button A: X\\+(\d+), Y\\+(\d+)"))
 | 
				
			||||||
 | 
					  let match-b = lines.at(1).match(regex("Button B: X\\+(\d+), Y\\+(\d+)"))
 | 
				
			||||||
 | 
					  let match-p = lines.at(2).match(regex("Prize: X=(\d+), Y=(\d+)"))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    a: (
 | 
				
			||||||
 | 
					      x: int(match-a.captures.first()),
 | 
				
			||||||
 | 
					      y: int(match-a.captures.last()),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    b: (
 | 
				
			||||||
 | 
					      x: int(match-b.captures.first()),
 | 
				
			||||||
 | 
					      y: int(match-b.captures.last()),
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    prize: (
 | 
				
			||||||
 | 
					      x: int(match-p.captures.first()) + 10000000000000,
 | 
				
			||||||
 | 
					      y: int(match-p.captures.last()) + 10000000000000,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let mat-mul-v(mat, v) = {
 | 
				
			||||||
 | 
					  return (
 | 
				
			||||||
 | 
					    mat.at(0).at(0) * v.at(0) + mat.at(0).at(1) * v.at(1),
 | 
				
			||||||
 | 
					    mat.at(1).at(0) * v.at(0) + mat.at(1).at(1) * v.at(1)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let det(mat) = {
 | 
				
			||||||
 | 
					  return mat.at(0).at(0) * mat.at(1).at(1) - mat.at(1).at(0) * mat.at(0).at(1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let machines = input.split("\n\n")
 | 
				
			||||||
 | 
					  machines = machines.map(parse-machine)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for m in machines {
 | 
				
			||||||
 | 
					    let mat = (
 | 
				
			||||||
 | 
					      (m.a.x, m.b.x),
 | 
				
			||||||
 | 
					      (m.a.y, m.b.y)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    let v = (m.prize.x, m.prize.y)
 | 
				
			||||||
 | 
					    let mat2 = (
 | 
				
			||||||
 | 
					      (mat.at(1).at(1), -mat.at(0).at(1)),
 | 
				
			||||||
 | 
					      (-mat.at(1).at(0), mat.at(0).at(0)),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let (a, b) = mat-mul-v(mat2, v)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let d = det(mat)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Check integer solution
 | 
				
			||||||
 | 
					    if calc.rem(a, d) != 0 or calc.rem(b, d) != 0 {
 | 
				
			||||||
 | 
					      continue
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    a = int(a / d)
 | 
				
			||||||
 | 
					    b = int(b / d)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if a > 0 and b > 0 {
 | 
				
			||||||
 | 
					      total += a * 3 + b
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  13, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 875318608908
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										65
									
								
								src/day14/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								src/day14/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let regexp = regex("^p=(.*?),(.*?) v=(.*?),(.*?)$")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let simulate(v0, dv, max: 1, steps: 1) = {
 | 
				
			||||||
 | 
					  return calc.rem-euclid(v0 + dv * steps, max)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let parse-input(input) = {
 | 
				
			||||||
 | 
					  return input.split("\n").map(b => {
 | 
				
			||||||
 | 
					    let m = b.match(regexp)
 | 
				
			||||||
 | 
					    return (
 | 
				
			||||||
 | 
					      pos: (
 | 
				
			||||||
 | 
					        x: int(m.captures.at(0)),
 | 
				
			||||||
 | 
					        y: int(m.captures.at(1)),
 | 
				
			||||||
 | 
					      ),
 | 
				
			||||||
 | 
					      vel: (
 | 
				
			||||||
 | 
					        x: int(m.captures.at(2)),
 | 
				
			||||||
 | 
					        y: int(m.captures.at(3))
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(w: 0, h: 0, steps: 100, input) = {
 | 
				
			||||||
 | 
					  assert(w != 0, message: "Width must be != 0")
 | 
				
			||||||
 | 
					  assert(h != 0, message: "Height must be != 0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let bots = parse-input(input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let quadrants = (
 | 
				
			||||||
 | 
					    tl: 0,
 | 
				
			||||||
 | 
					    tr: 0,
 | 
				
			||||||
 | 
					    bl: 0,
 | 
				
			||||||
 | 
					    br: 0
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  let half-w = calc.div-euclid(w, 2)
 | 
				
			||||||
 | 
					  let half-h = calc.div-euclid(h, 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let sim-x = simulate.with(max: w, steps: steps)
 | 
				
			||||||
 | 
					  let sim-y = simulate.with(max: h, steps: steps)
 | 
				
			||||||
 | 
					  for bot in bots {
 | 
				
			||||||
 | 
					    let x2 = sim-x(bot.pos.x, bot.vel.x)
 | 
				
			||||||
 | 
					    let y2 = sim-y(bot.pos.y, bot.vel.y)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if x2 == half-w or y2 == half-h {
 | 
				
			||||||
 | 
					      continue
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let quadrant = (
 | 
				
			||||||
 | 
					      (if y2 < half-h {"t"} else {"b"}) +
 | 
				
			||||||
 | 
					      (if x2 < half-w {"l"} else {"r"})
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    quadrants.at(quadrant) += 1
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return quadrants.values().product()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  14, 1,
 | 
				
			||||||
 | 
					  solve.with(w: 101, h: 103),
 | 
				
			||||||
 | 
					  example: (
 | 
				
			||||||
 | 
					    (result: 12, args: (w: 11, h: 7)),
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										33
									
								
								src/day14/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/day14/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw
 | 
				
			||||||
 | 
					#import "puzzle1.typ": parse-input, simulate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let input = get-input(14)
 | 
				
			||||||
 | 
					#let bots = parse-input(input)
 | 
				
			||||||
 | 
					#let res = 8270
 | 
				
			||||||
 | 
					#let (width, height) = (101, 103)
 | 
				
			||||||
 | 
					#let sim-x = simulate.with(max: width, steps: res)
 | 
				
			||||||
 | 
					#let sim-y = simulate.with(max: height, steps: res)
 | 
				
			||||||
 | 
					#let size = 0.1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#figure(
 | 
				
			||||||
 | 
					  canvas({
 | 
				
			||||||
 | 
					    draw.rect(
 | 
				
			||||||
 | 
					      (0, 0),
 | 
				
			||||||
 | 
					      (width * size, -height * size)
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    for bot in bots {
 | 
				
			||||||
 | 
					      let x = sim-x(bot.pos.x, bot.vel.x)
 | 
				
			||||||
 | 
					      let y = sim-y(bot.pos.y, bot.vel.y)
 | 
				
			||||||
 | 
					      draw.rect(
 | 
				
			||||||
 | 
					        (x * size, -y * size),
 | 
				
			||||||
 | 
					        ((x + 1) * size, -(y + 1) * size),
 | 
				
			||||||
 | 
					        stroke: none,
 | 
				
			||||||
 | 
					        fill: black
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }),
 | 
				
			||||||
 | 
					  caption: "Christmas tree easter egg"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-result(res)
 | 
				
			||||||
							
								
								
									
										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.
										
									
								
							@@ -81,6 +81,12 @@
 | 
				
			|||||||
      for (suffix, result) in example.pairs() {
 | 
					      for (suffix, result) in example.pairs() {
 | 
				
			||||||
        check-example(day, func, result, suffix: suffix)
 | 
					        check-example(day, func, result, suffix: suffix)
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
 | 
					    } else if type(example) == array {
 | 
				
			||||||
 | 
					      for ex in example {
 | 
				
			||||||
 | 
					        let suffix = ex.at("suffix", default: none)
 | 
				
			||||||
 | 
					        let args = ex.at("args", default: (:))
 | 
				
			||||||
 | 
					        check-example(day, func.with(..args), ex.result, suffix: suffix)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      check-example(day, func, example)
 | 
					      check-example(day, func, example)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -189,5 +195,12 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#let template(body) = {
 | 
					#let template(body) = {
 | 
				
			||||||
  set text(font: "Source Sans 3")
 | 
					  set text(font: "Source Sans 3")
 | 
				
			||||||
 | 
					  set page(
 | 
				
			||||||
 | 
					    footer: context {
 | 
				
			||||||
 | 
					      if counter(page).get().first() != 1 {
 | 
				
			||||||
 | 
					        align(center, counter(page).display("1 / 1", both: true))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
  body
 | 
					  body
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user