Compare commits
	
		
			61 Commits
		
	
	
		
			7a8faae694
			...
			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
	
				 | 
					
					
						|||
| 
						
						
							
						
						a35be7ec92
	
				 | 
					
					
						|||
| 
						
						
							
						
						cee23e5034
	
				 | 
					
					
						|||
| 
						
						
							
						
						679e03217b
	
				 | 
					
					
						|||
| 
						
						
							
						
						402372f802
	
				 | 
					
					
						|||
| 
						
						
							
						
						c525fdc3d7
	
				 | 
					
					
						|||
| 
						
						
							
						
						da0f00375b
	
				 | 
					
					
						|||
| 
						
						
							
						
						1ed3f6d981
	
				 | 
					
					
						|||
| 
						
						
							
						
						5e304ef1e5
	
				 | 
					
					
						|||
| 
						
						
							
						
						1f0389071c
	
				 | 
					
					
						|||
| 
						
						
							
						
						6374452e24
	
				 | 
					
					
						|||
| 
						
						
							
						
						da56d30760
	
				 | 
					
					
						|||
| 
						
						
							
						
						0d89355f86
	
				 | 
					
					
						|||
| 
						
						
							
						
						9a1b3bedc2
	
				 | 
					
					
						|||
| 
						
						
							
						
						03063514a5
	
				 | 
					
					
						|||
| 
						
						
							
						
						52a253c1ea
	
				 | 
					
					
						|||
| 
						
						
							
						
						c97fd6e8af
	
				 | 
					
					
						|||
| 
						
						
							
						
						acdb212ddd
	
				 | 
					
					
						|||
| 
						
						
							
						
						6071366ed6
	
				 | 
					
					
						|||
| 
						
						
							
						
						eeebb6e721
	
				 | 
					
					
						|||
| 
						
						
							
						
						54d93d6a1b
	
				 | 
					
					
						|||
| 
						
						
							
						
						4fb226448f
	
				 | 
					
					
						|||
| 
						
						
							
						
						590ba743a1
	
				 | 
					
					
						|||
| 
						
						
							
						
						11277f0677
	
				 | 
					
					
						|||
| 
						
						
							
						
						b45cc17e0d
	
				 | 
					
					
						|||
| 
						
						
							
						
						7bcab0b085
	
				 | 
					
					
						|||
| 
						
						
							
						
						65beed1c51
	
				 | 
					
					
						|||
| 
						
						
							
						
						66f04ed889
	
				 | 
					
					
						|||
| 
						
						
							
						
						95981a219a
	
				 | 
					
					
						|||
| 
						
						
							
						
						a8611658a2
	
				 | 
					
					
						|||
| 
						
						
							
						
						8b4120c53f
	
				 | 
					
					
						
							
								
								
									
										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: 89 KiB After Width: | Height: | Size: 135 KiB  | 
@@ -10,4 +10,10 @@
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  return p
 | 
					  return p
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
#make-progress(links: false)
 | 
					#make-progress(
 | 
				
			||||||
 | 
					  links: false,
 | 
				
			||||||
 | 
					  badge: (
 | 
				
			||||||
 | 
					    name: "LordBaryhobal",
 | 
				
			||||||
 | 
					    img: image("res/me.jpg", width: 3em)
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -1,4 +1,50 @@
 | 
				
			|||||||
1:
 | 
					1:
 | 
				
			||||||
  stars: 2
 | 
					  stars: 2
 | 
				
			||||||
2:
 | 
					2:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					3:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					4:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					5:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					6:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					7:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					8:
 | 
				
			||||||
 | 
					  stars: 2
 | 
				
			||||||
 | 
					9:
 | 
				
			||||||
 | 
					  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
 | 
					  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 @@
 | 
				
			|||||||
 | 
					#####
 | 
				
			||||||
 | 
					.####
 | 
				
			||||||
 | 
					.####
 | 
				
			||||||
 | 
					.####
 | 
				
			||||||
 | 
					.#.#.
 | 
				
			||||||
 | 
					.#...
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#####
 | 
				
			||||||
 | 
					##.##
 | 
				
			||||||
 | 
					.#.##
 | 
				
			||||||
 | 
					...##
 | 
				
			||||||
 | 
					...#.
 | 
				
			||||||
 | 
					...#.
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					#....
 | 
				
			||||||
 | 
					#....
 | 
				
			||||||
 | 
					#...#
 | 
				
			||||||
 | 
					#.#.#
 | 
				
			||||||
 | 
					#.###
 | 
				
			||||||
 | 
					#####
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					#.#..
 | 
				
			||||||
 | 
					###..
 | 
				
			||||||
 | 
					###.#
 | 
				
			||||||
 | 
					###.#
 | 
				
			||||||
 | 
					#####
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					.....
 | 
				
			||||||
 | 
					#....
 | 
				
			||||||
 | 
					#.#..
 | 
				
			||||||
 | 
					#.#.#
 | 
				
			||||||
 | 
					#####
 | 
				
			||||||
							
								
								
									
										1
									
								
								res/examples/day3_1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								res/examples/day3_1.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))
 | 
				
			||||||
							
								
								
									
										1
									
								
								res/examples/day3_2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								res/examples/day3_2.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5))
 | 
				
			||||||
							
								
								
									
										10
									
								
								res/examples/day4.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								res/examples/day4.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					MMMSXXMASM
 | 
				
			||||||
 | 
					MSAMXMSMSA
 | 
				
			||||||
 | 
					AMXSXMAAMM
 | 
				
			||||||
 | 
					MSAMASMSMX
 | 
				
			||||||
 | 
					XMASAMXAMM
 | 
				
			||||||
 | 
					XXAMMXXAMA
 | 
				
			||||||
 | 
					SMSMSASXSS
 | 
				
			||||||
 | 
					SAXAMASAAA
 | 
				
			||||||
 | 
					MAMMMXMMMM
 | 
				
			||||||
 | 
					MXMXAXMASX
 | 
				
			||||||
							
								
								
									
										28
									
								
								res/examples/day5.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								res/examples/day5.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
				
			|||||||
 | 
					47|53
 | 
				
			||||||
 | 
					97|13
 | 
				
			||||||
 | 
					97|61
 | 
				
			||||||
 | 
					97|47
 | 
				
			||||||
 | 
					75|29
 | 
				
			||||||
 | 
					61|13
 | 
				
			||||||
 | 
					75|53
 | 
				
			||||||
 | 
					29|13
 | 
				
			||||||
 | 
					97|29
 | 
				
			||||||
 | 
					53|29
 | 
				
			||||||
 | 
					61|53
 | 
				
			||||||
 | 
					97|53
 | 
				
			||||||
 | 
					61|29
 | 
				
			||||||
 | 
					47|13
 | 
				
			||||||
 | 
					75|47
 | 
				
			||||||
 | 
					97|75
 | 
				
			||||||
 | 
					47|61
 | 
				
			||||||
 | 
					75|61
 | 
				
			||||||
 | 
					47|29
 | 
				
			||||||
 | 
					75|13
 | 
				
			||||||
 | 
					53|13
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					75,47,61,53,29
 | 
				
			||||||
 | 
					97,61,53,29,13
 | 
				
			||||||
 | 
					75,29,13
 | 
				
			||||||
 | 
					75,97,47,61,53
 | 
				
			||||||
 | 
					61,13,29
 | 
				
			||||||
 | 
					97,13,75,29,47
 | 
				
			||||||
							
								
								
									
										10
									
								
								res/examples/day6.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								res/examples/day6.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					....#.....
 | 
				
			||||||
 | 
					.........#
 | 
				
			||||||
 | 
					..........
 | 
				
			||||||
 | 
					..#.......
 | 
				
			||||||
 | 
					.......#..
 | 
				
			||||||
 | 
					..........
 | 
				
			||||||
 | 
					.#..^.....
 | 
				
			||||||
 | 
					........#.
 | 
				
			||||||
 | 
					#.........
 | 
				
			||||||
 | 
					......#...
 | 
				
			||||||
							
								
								
									
										9
									
								
								res/examples/day7.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								res/examples/day7.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,9 @@
 | 
				
			|||||||
 | 
					190: 10 19
 | 
				
			||||||
 | 
					3267: 81 40 27
 | 
				
			||||||
 | 
					83: 17 5
 | 
				
			||||||
 | 
					156: 15 6
 | 
				
			||||||
 | 
					7290: 6 8 6 15
 | 
				
			||||||
 | 
					161011: 16 10 13
 | 
				
			||||||
 | 
					192: 17 8 14
 | 
				
			||||||
 | 
					21037: 9 7 18 13
 | 
				
			||||||
 | 
					292: 11 6 16 20
 | 
				
			||||||
							
								
								
									
										12
									
								
								res/examples/day8.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								res/examples/day8.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					............
 | 
				
			||||||
 | 
					........0...
 | 
				
			||||||
 | 
					.....0......
 | 
				
			||||||
 | 
					.......0....
 | 
				
			||||||
 | 
					....0.......
 | 
				
			||||||
 | 
					......A.....
 | 
				
			||||||
 | 
					............
 | 
				
			||||||
 | 
					............
 | 
				
			||||||
 | 
					........A...
 | 
				
			||||||
 | 
					.........A..
 | 
				
			||||||
 | 
					............
 | 
				
			||||||
 | 
					............
 | 
				
			||||||
							
								
								
									
										1
									
								
								res/examples/day9_1.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								res/examples/day9_1.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					12345
 | 
				
			||||||
							
								
								
									
										1
									
								
								res/examples/day9_2.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								res/examples/day9_2.txt
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
				
			|||||||
 | 
					2333133121414131402
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								res/me.jpg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								res/me.jpg
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 535 KiB  | 
							
								
								
									
										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)
 | 
				
			||||||
@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let is-safe(levels) = {
 | 
				
			||||||
 | 
					  let increasing
 | 
				
			||||||
 | 
					  let safe = true
 | 
				
			||||||
 | 
					  for i in range(levels.len() - 1) {
 | 
				
			||||||
 | 
					    let d = levels.at(i + 1) - levels.at(i)
 | 
				
			||||||
 | 
					    let abs-d = calc.abs(d)
 | 
				
			||||||
 | 
					    if abs-d < 1 or abs-d > 3 {
 | 
				
			||||||
 | 
					      safe = false
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if i == 0 {
 | 
				
			||||||
 | 
					      increasing = d > 0
 | 
				
			||||||
 | 
					    } else if (d > 0) != increasing {
 | 
				
			||||||
 | 
					      safe = false
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return safe
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve-bruteforce(input) = {
 | 
				
			||||||
 | 
					  let safe-cnt = 0
 | 
				
			||||||
 | 
					  for line in input.split("\n") {
 | 
				
			||||||
 | 
					    let nums = line.split(" ").map(n => int(n))
 | 
				
			||||||
 | 
					    if is-safe(nums) {
 | 
				
			||||||
 | 
					      safe-cnt += 1
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      for i in range(nums.len()) {
 | 
				
			||||||
 | 
					        if is-safe(nums.slice(0, i) + nums.slice(i+1)) {
 | 
				
			||||||
 | 
					          safe-cnt += 1
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return safe-cnt
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let safe-cnt = 0
 | 
				
			||||||
 | 
					  for line in input.split("\n") {
 | 
				
			||||||
 | 
					    let nums = line.split(" ").map(n => int(n))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let remove-i = none
 | 
				
			||||||
 | 
					    if not is-safe(nums) {
 | 
				
			||||||
 | 
					      for i in range(nums.len()) {
 | 
				
			||||||
 | 
					        if is-safe(nums.slice(0, i) + nums.slice(i+1)) {
 | 
				
			||||||
 | 
					          remove-i = i
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let cells = ()
 | 
				
			||||||
 | 
					    cells += nums.enumerate().map(((i, n)) => grid.cell(
 | 
				
			||||||
 | 
					      colspan: 3,
 | 
				
			||||||
 | 
					      fill: if i == remove-i {gray.transparentize(40%)},
 | 
				
			||||||
 | 
					      str(n)
 | 
				
			||||||
 | 
					    ))
 | 
				
			||||||
 | 
					    cells += (none,none,)
 | 
				
			||||||
 | 
					    cells += range(nums.len() - 1).map(i => nums.at(i + 1) - nums.at(i))
 | 
				
			||||||
 | 
					                                  .map(n => {
 | 
				
			||||||
 | 
					                                    let col = if calc.abs(n) in (1,2,3) {green} else {red}
 | 
				
			||||||
 | 
					                                    grid.cell(
 | 
				
			||||||
 | 
					                                      colspan: 2,
 | 
				
			||||||
 | 
					                                      fill: gradient.linear(
 | 
				
			||||||
 | 
					                                        angle: if n >= 0 {0deg} else {180deg},
 | 
				
			||||||
 | 
					                                        (white, 0%),
 | 
				
			||||||
 | 
					                                        (white, 50%),
 | 
				
			||||||
 | 
					                                        (col, 50%),
 | 
				
			||||||
 | 
					                                        (col, 100%)
 | 
				
			||||||
 | 
					                                      ),
 | 
				
			||||||
 | 
					                                      str(n)
 | 
				
			||||||
 | 
					                                    )
 | 
				
			||||||
 | 
					                                  })
 | 
				
			||||||
 | 
					                                  .intersperse(none)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    grid(
 | 
				
			||||||
 | 
					      columns: (1fr,) * 3 * nums.len(),
 | 
				
			||||||
 | 
					      inset: (x: 0.2em, y: 0.4em),
 | 
				
			||||||
 | 
					      stroke: (x, y) => {
 | 
				
			||||||
 | 
					        if y == 0 or (calc.rem(x, 3) == 2 and x != nums.len() * 3 - 1) {
 | 
				
			||||||
 | 
					          black + .5pt
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					      ..cells
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  2, 2,
 | 
				
			||||||
 | 
					  solve-bruteforce,
 | 
				
			||||||
 | 
					  example: 4,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										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
									
								
							
							
								
								
									
										33
									
								
								src/day3/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/day3/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let reg = regex("mul\((\d{1,3}),(\d{1,3})\)")
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let matches = input.matches(reg)
 | 
				
			||||||
 | 
					  let total = matches.map(m => {
 | 
				
			||||||
 | 
					    m.captures.map(int)
 | 
				
			||||||
 | 
					              .product()
 | 
				
			||||||
 | 
					  }).sum()
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  [
 | 
				
			||||||
 | 
					    #set text(size: 1.2em)
 | 
				
			||||||
 | 
					    #show reg: it => {
 | 
				
			||||||
 | 
					      let m = it.text.match(reg)
 | 
				
			||||||
 | 
					      let v = m.captures.map(int).product()
 | 
				
			||||||
 | 
					      math.underbrace(
 | 
				
			||||||
 | 
					        highlight(fill: red, raw(it.text)),
 | 
				
			||||||
 | 
					        text(size: 1.5em, str(v))
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    #raw(input)
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  3, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: ("1": 161),
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										31
									
								
								src/day3/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								src/day3/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let dos = input.matches(regex("do\(\)"))
 | 
				
			||||||
 | 
					  let donts = input.matches(regex("don't\(\)"))
 | 
				
			||||||
 | 
					  let toggles = dos.map(m => (m.end, true))
 | 
				
			||||||
 | 
					  toggles += donts.map(m => (m.end, false))
 | 
				
			||||||
 | 
					  toggles = toggles.sorted(key: t => t.first())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let is-enabled(i) = {
 | 
				
			||||||
 | 
					    let t = toggles.rev().find(t => t.first() <= i)
 | 
				
			||||||
 | 
					    return t == none or t.last()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let matches = input.matches(regex("mul\((\d{1,3}),(\d{1,3})\)"))
 | 
				
			||||||
 | 
					  let total = matches.map(m => {
 | 
				
			||||||
 | 
					    if is-enabled(m.start) {
 | 
				
			||||||
 | 
					      m.captures.map(int)
 | 
				
			||||||
 | 
					                .product()
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      0
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }).sum()
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  3, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: ("2": 48)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										91
									
								
								src/day4/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										91
									
								
								src/day4/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,91 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let check-xmas(lines, ox, oy) = {
 | 
				
			||||||
 | 
					  let w = lines.first().len()
 | 
				
			||||||
 | 
					  let h = lines.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let dirs = ()
 | 
				
			||||||
 | 
					  for dy in (-1, 0, 1) {
 | 
				
			||||||
 | 
					    for dx in (-1, 0, 1) {
 | 
				
			||||||
 | 
					      if dx == 0 and dy == 0 {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let buffer = ""
 | 
				
			||||||
 | 
					      let x = ox
 | 
				
			||||||
 | 
					      let y = oy
 | 
				
			||||||
 | 
					      for i in range(4) {
 | 
				
			||||||
 | 
					        buffer += lines.at(y).at(x)
 | 
				
			||||||
 | 
					        x += dx
 | 
				
			||||||
 | 
					        y += dy
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					          not "XMAS".starts-with(buffer) or
 | 
				
			||||||
 | 
					          x < 0 or x >= w or
 | 
				
			||||||
 | 
					          y < 0 or y >= h
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if buffer == "XMAS" {
 | 
				
			||||||
 | 
					        dirs.push((dx, dy))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return dirs
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let lines = input.split("\n")
 | 
				
			||||||
 | 
					  let w = lines.first().len()
 | 
				
			||||||
 | 
					  let h = lines.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      if lines.at(y).at(x) == "X" {
 | 
				
			||||||
 | 
					        total += check-xmas(lines, x, y).len()
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let lines = input.split("\n")
 | 
				
			||||||
 | 
					  let w = lines.first().len()
 | 
				
			||||||
 | 
					  let h = lines.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  canvas(length: 2em, {
 | 
				
			||||||
 | 
					    for y in range(h) {
 | 
				
			||||||
 | 
					      for x in range(w) {
 | 
				
			||||||
 | 
					        if lines.at(y).at(x) == "X" {
 | 
				
			||||||
 | 
					          let key = str(x) + "-" + str(y)
 | 
				
			||||||
 | 
					          let dirs = check-xmas(lines, x, y)
 | 
				
			||||||
 | 
					          draw.on-layer(2, {
 | 
				
			||||||
 | 
					            for (dx, dy) in dirs {
 | 
				
			||||||
 | 
					              draw.line(
 | 
				
			||||||
 | 
					                (x + dx * 0.2, y + dy * 0.2),
 | 
				
			||||||
 | 
					                (x + dx * 2.8, y + dy * 2.8),
 | 
				
			||||||
 | 
					                stroke: red,
 | 
				
			||||||
 | 
					                fill: red,
 | 
				
			||||||
 | 
					                mark: (end: ">")
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          })
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        draw.content(
 | 
				
			||||||
 | 
					          (x, y),
 | 
				
			||||||
 | 
					          lines.at(y).at(x)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  4, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 18,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										88
									
								
								src/day4/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								src/day4/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,88 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let lines = input.split("\n")
 | 
				
			||||||
 | 
					  let w = lines.first().len()
 | 
				
			||||||
 | 
					  let h = lines.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let perms = (("M", "S"), ("S", "M"))
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for y in range(1, h - 1) {
 | 
				
			||||||
 | 
					    for x in range(1, w - 1) {
 | 
				
			||||||
 | 
					      if lines.at(y).at(x) == "A" {
 | 
				
			||||||
 | 
					        let tl = lines.at(y - 1).at(x - 1)
 | 
				
			||||||
 | 
					        let tr = lines.at(y - 1).at(x + 1)
 | 
				
			||||||
 | 
					        let bl = lines.at(y + 1).at(x - 1)
 | 
				
			||||||
 | 
					        let br = lines.at(y + 1).at(x + 1)
 | 
				
			||||||
 | 
					        let tlbr = (tl, br)
 | 
				
			||||||
 | 
					        let bltr = (bl, tr)
 | 
				
			||||||
 | 
					        if tlbr in perms and bltr in perms {
 | 
				
			||||||
 | 
					          total += 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let lines = input.split("\n")
 | 
				
			||||||
 | 
					  let w = lines.first().len()
 | 
				
			||||||
 | 
					  let h = lines.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let perms = (("M", "S"), ("S", "M"))
 | 
				
			||||||
 | 
					  let positions = ()
 | 
				
			||||||
 | 
					  for y in range(1, h - 1) {
 | 
				
			||||||
 | 
					    for x in range(1, w - 1) {
 | 
				
			||||||
 | 
					      if lines.at(y).at(x) == "A" {
 | 
				
			||||||
 | 
					        let tl = lines.at(y - 1).at(x - 1)
 | 
				
			||||||
 | 
					        let tr = lines.at(y - 1).at(x + 1)
 | 
				
			||||||
 | 
					        let bl = lines.at(y + 1).at(x - 1)
 | 
				
			||||||
 | 
					        let br = lines.at(y + 1).at(x + 1)
 | 
				
			||||||
 | 
					        let tlbr = (tl, br)
 | 
				
			||||||
 | 
					        let bltr = (bl, tr)
 | 
				
			||||||
 | 
					        if tlbr in perms and bltr in perms {
 | 
				
			||||||
 | 
					          positions.push((x, y))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  canvas(length: 2em, {
 | 
				
			||||||
 | 
					    for y in range(h) {
 | 
				
			||||||
 | 
					      for x in range(w) {
 | 
				
			||||||
 | 
					        let valid = (x, y) in positions
 | 
				
			||||||
 | 
					        if valid {
 | 
				
			||||||
 | 
					          draw.circle(
 | 
				
			||||||
 | 
					            (x, y),
 | 
				
			||||||
 | 
					            radius: 0.3,
 | 
				
			||||||
 | 
					            stroke: red,
 | 
				
			||||||
 | 
					            name: str(x) + "-" + str(y)
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					          for dy in (-1, 1) {
 | 
				
			||||||
 | 
					            for dx in (-1, 1) {
 | 
				
			||||||
 | 
					              draw.line(
 | 
				
			||||||
 | 
					                str(x) + "-" + str(y),
 | 
				
			||||||
 | 
					                (x + dx * 0.75, y + dy * 0.75),
 | 
				
			||||||
 | 
					                stroke: red
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        draw.content(
 | 
				
			||||||
 | 
					          (x, y),
 | 
				
			||||||
 | 
					          lines.at(y).at(x)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  4, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 9,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										127
									
								
								src/day5/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										127
									
								
								src/day5/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,127 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let make-rules-dict(rules) = {
 | 
				
			||||||
 | 
					  let dict = (:)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for rule in rules {
 | 
				
			||||||
 | 
					    let (a, b) = rule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if a not in dict {
 | 
				
			||||||
 | 
					      dict.insert(a, ())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    dict.at(a).push(b)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return dict
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let is-update-valid(dict, update) = {
 | 
				
			||||||
 | 
					  for i in range(update.len() - 1) {
 | 
				
			||||||
 | 
					    let a = str(update.at(i))
 | 
				
			||||||
 | 
					    for j in range(i + 1, update.len()) {
 | 
				
			||||||
 | 
					      let b = str(update.at(j))
 | 
				
			||||||
 | 
					      if a in dict.at(b, default: ()) {
 | 
				
			||||||
 | 
					        return false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let (rules, updates) = input.split("\n\n")
 | 
				
			||||||
 | 
					  rules = rules.split("\n").map(l => l.split("|"))
 | 
				
			||||||
 | 
					  updates = updates.split("\n").map(l => l.split(",").map(int))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  let rules-dict = make-rules-dict(rules)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for update in updates {
 | 
				
			||||||
 | 
					    if is-update-valid(rules-dict, update) {
 | 
				
			||||||
 | 
					      total += update.at(calc.div-euclid(update.len(), 2))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let (rules, updates) = input.split("\n\n")
 | 
				
			||||||
 | 
					  rules = rules.split("\n").map(l => l.split("|").map(int))
 | 
				
			||||||
 | 
					  updates = updates.split("\n").map(l => l.split(",").map(int))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  //let rules-dict = make-rules-dict(rules)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let diags = ()
 | 
				
			||||||
 | 
					  for update in updates {
 | 
				
			||||||
 | 
					    let diag = canvas(length: 3em, {
 | 
				
			||||||
 | 
					      for (x, n) in update.enumerate() {
 | 
				
			||||||
 | 
					        draw.circle(
 | 
				
			||||||
 | 
					          (x, 0),
 | 
				
			||||||
 | 
					          radius: 0.4,
 | 
				
			||||||
 | 
					          name: str(x)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        draw.content(
 | 
				
			||||||
 | 
					          (x, 0),
 | 
				
			||||||
 | 
					          str(n)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let flip = false
 | 
				
			||||||
 | 
					      let c = 1
 | 
				
			||||||
 | 
					      for (a, b) in rules {
 | 
				
			||||||
 | 
					        let i = update.position(n => n == a)
 | 
				
			||||||
 | 
					        let j = update.position(n => n == b)
 | 
				
			||||||
 | 
					        if i == none or j == none {
 | 
				
			||||||
 | 
					          continue
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        let anchor = if flip {".south"} else {".north"}
 | 
				
			||||||
 | 
					        let pt-i = str(i) + anchor
 | 
				
			||||||
 | 
					        let pt-j = str(j) + anchor
 | 
				
			||||||
 | 
					        let col = if j < i {red} else {green}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        draw.arc-through(
 | 
				
			||||||
 | 
					          pt-i,
 | 
				
			||||||
 | 
					          (
 | 
				
			||||||
 | 
					            rel: (0, if flip {-c / 10} else {c / 10}),
 | 
				
			||||||
 | 
					            to: (pt-i, 50%, pt-j)
 | 
				
			||||||
 | 
					          ),
 | 
				
			||||||
 | 
					          pt-j,
 | 
				
			||||||
 | 
					          mark: (end: ">", fill: col),
 | 
				
			||||||
 | 
					          stroke: col
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        flip = not flip
 | 
				
			||||||
 | 
					        c += 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    diags.push(diag)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /*if is-update-valid(rules-dict, update) {
 | 
				
			||||||
 | 
					      total += update.at(calc.div-euclid(update.len(), 2))
 | 
				
			||||||
 | 
					    }*/
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  diags.last() = grid.cell(
 | 
				
			||||||
 | 
					    colspan: 2 - calc.rem(diags.len() - 1, 2),
 | 
				
			||||||
 | 
					    diags.last()
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  grid(
 | 
				
			||||||
 | 
					    columns: 2,
 | 
				
			||||||
 | 
					    stroke: (paint: black, dash: "dashed"),
 | 
				
			||||||
 | 
					    align: center + horizon,
 | 
				
			||||||
 | 
					    inset: 0.4em,
 | 
				
			||||||
 | 
					    ..diags
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  5, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 143,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										63
									
								
								src/day5/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/day5/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let make-rules-dict(rules) = {
 | 
				
			||||||
 | 
					  let dict = (:)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for rule in rules {
 | 
				
			||||||
 | 
					    let (a, b) = rule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if a not in dict {
 | 
				
			||||||
 | 
					      dict.insert(a, ())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    dict.at(a).push(b)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return dict
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/// Bubble sort
 | 
				
			||||||
 | 
					#let fix-update(dict, update) = {
 | 
				
			||||||
 | 
					  let update = update
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let has-changed = false
 | 
				
			||||||
 | 
					  let changed = true
 | 
				
			||||||
 | 
					  while changed {
 | 
				
			||||||
 | 
					    changed = false
 | 
				
			||||||
 | 
					    for i in range(update.len() - 1) {
 | 
				
			||||||
 | 
					      let a = str(update.at(i))
 | 
				
			||||||
 | 
					      let b = str(update.at(i + 1))
 | 
				
			||||||
 | 
					      
 | 
				
			||||||
 | 
					      if a in dict.at(b, default: ()) {
 | 
				
			||||||
 | 
					        update.at(i) = int(b)
 | 
				
			||||||
 | 
					        update.at(i + 1) = int(a)
 | 
				
			||||||
 | 
					        changed = true
 | 
				
			||||||
 | 
					        has-changed = true
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return (has-changed, update)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let (rules, updates) = input.split("\n\n")
 | 
				
			||||||
 | 
					  rules = rules.split("\n").map(l => l.split("|"))
 | 
				
			||||||
 | 
					  updates = updates.split("\n").map(l => l.split(",").map(int))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  let rules-dict = make-rules-dict(rules)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for update in updates {
 | 
				
			||||||
 | 
					    let (has-changed, update) = fix-update(rules-dict, update)
 | 
				
			||||||
 | 
					    if has-changed {
 | 
				
			||||||
 | 
					      total += update.at(calc.div-euclid(update.len(), 2))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  5, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 123
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										152
									
								
								src/day6/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/day6/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let offsets = (
 | 
				
			||||||
 | 
					  (0, -1),
 | 
				
			||||||
 | 
					  (1, 0),
 | 
				
			||||||
 | 
					  (0, 1),
 | 
				
			||||||
 | 
					  (-1, 0)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-grid(x, y, w, h) = {
 | 
				
			||||||
 | 
					  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 ox = 0
 | 
				
			||||||
 | 
					  let oy = 0
 | 
				
			||||||
 | 
					  let found-start = false
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      if grid.at(y).at(x) == "^" {
 | 
				
			||||||
 | 
					        ox = x
 | 
				
			||||||
 | 
					        oy = y
 | 
				
			||||||
 | 
					        found-start = true
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if found-start {
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  grid.at(oy).at(ox) = "v"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let x = ox
 | 
				
			||||||
 | 
					  let y = oy
 | 
				
			||||||
 | 
					  let dir = 0
 | 
				
			||||||
 | 
					  let count = 1
 | 
				
			||||||
 | 
					  let (dx, dy) = offsets.at(dir)
 | 
				
			||||||
 | 
					  while in-grid(x, y, w, h) {
 | 
				
			||||||
 | 
					    if grid.at(y).at(x) != "v" {
 | 
				
			||||||
 | 
					      grid.at(y).at(x) = "v"
 | 
				
			||||||
 | 
					      count += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let x2 = x + dx
 | 
				
			||||||
 | 
					    let y2 = y + dy
 | 
				
			||||||
 | 
					    if not in-grid(x2, y2, w, h) {
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for _ in range(4) {
 | 
				
			||||||
 | 
					      let next = grid.at(y2).at(x2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if next == "#" {
 | 
				
			||||||
 | 
					        dir = calc.rem(dir + 1, 4)
 | 
				
			||||||
 | 
					        (dx, dy) = offsets.at(dir)
 | 
				
			||||||
 | 
					        x2 = x + dx
 | 
				
			||||||
 | 
					        y2 = y + dy
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    x = x2
 | 
				
			||||||
 | 
					    y = y2
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return count
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let grid = input.split("\n").map(l => l.clusters())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  canvas(length: 2em, {
 | 
				
			||||||
 | 
					    let (ox, oy) = (0, 0)
 | 
				
			||||||
 | 
					    for y in range(h) {
 | 
				
			||||||
 | 
					      for x in range(w) {
 | 
				
			||||||
 | 
					        let c = grid.at(y).at(x)
 | 
				
			||||||
 | 
					        draw.circle(
 | 
				
			||||||
 | 
					          (x, -y),
 | 
				
			||||||
 | 
					          radius: if c == "#" {0.4} else {0.2},
 | 
				
			||||||
 | 
					          fill: if c == "#" {
 | 
				
			||||||
 | 
					            red
 | 
				
			||||||
 | 
					          } else if c == "^" {
 | 
				
			||||||
 | 
					            green
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            gray.lighten(40%)
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if c == "^" {
 | 
				
			||||||
 | 
					          (ox, oy) = (x, y)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let x = ox
 | 
				
			||||||
 | 
					    let y = oy
 | 
				
			||||||
 | 
					    let dir = 0
 | 
				
			||||||
 | 
					    let path = ()
 | 
				
			||||||
 | 
					    let (dx, dy) = offsets.at(dir)
 | 
				
			||||||
 | 
					    while in-grid(x, y, w, h) {
 | 
				
			||||||
 | 
					      path.push((x, y))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let x2 = x + dx
 | 
				
			||||||
 | 
					      let y2 = y + dy
 | 
				
			||||||
 | 
					      if not in-grid(x2, y2, w, h) {
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for _ in range(4) {
 | 
				
			||||||
 | 
					        let next = grid.at(y2).at(x2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if next == "#" {
 | 
				
			||||||
 | 
					          dir = calc.rem(dir + 1, 4)
 | 
				
			||||||
 | 
					          (dx, dy) = offsets.at(dir)
 | 
				
			||||||
 | 
					          x2 = x + dx
 | 
				
			||||||
 | 
					          y2 = y + dy
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let col = if path.len() == 1 {green} else {blue}
 | 
				
			||||||
 | 
					      draw.line(
 | 
				
			||||||
 | 
					        (x, -y),
 | 
				
			||||||
 | 
					        (x2, -y2),
 | 
				
			||||||
 | 
					        mark: (end: ">"),
 | 
				
			||||||
 | 
					        fill: col,
 | 
				
			||||||
 | 
					        stroke: col
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      x = x2
 | 
				
			||||||
 | 
					      y = y2
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    //draw.line(..path, stroke: blue)
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  6, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 41,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										210
									
								
								src/day6/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										210
									
								
								src/day6/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,210 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let empty = 0
 | 
				
			||||||
 | 
					#let up = 1
 | 
				
			||||||
 | 
					#let right = 2
 | 
				
			||||||
 | 
					#let down = 4
 | 
				
			||||||
 | 
					#let left = 8
 | 
				
			||||||
 | 
					#let obstacle = 16
 | 
				
			||||||
 | 
					#let possible-obstacle = 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let offsets = (
 | 
				
			||||||
 | 
					  str(up): (0, -1),
 | 
				
			||||||
 | 
					  str(right): (1, 0),
 | 
				
			||||||
 | 
					  str(down): (0, 1),
 | 
				
			||||||
 | 
					  str(left): (-1, 0)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let values = (
 | 
				
			||||||
 | 
					  ".": empty,
 | 
				
			||||||
 | 
					  "#": obstacle,
 | 
				
			||||||
 | 
					  "^": up,
 | 
				
			||||||
 | 
					  "O": possible-obstacle
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-grid(x, y, w, h) = {
 | 
				
			||||||
 | 
					  return 0 <= x and x < w and 0 <= y and y < h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let rotate(dir) = {
 | 
				
			||||||
 | 
					  return if dir == left {up} else {dir.bit-lshift(1)}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let puzzle1(grid, ox, oy, dir) = {
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					  let path = ((ox, oy, dir),)
 | 
				
			||||||
 | 
					  let (x, y) = (ox, oy)
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  let (dx, dy) = (0, 0)
 | 
				
			||||||
 | 
					  let (x2, y2) = (x, y)
 | 
				
			||||||
 | 
					  while true {
 | 
				
			||||||
 | 
					    (dx, dy) = offsets.at(str(dir))
 | 
				
			||||||
 | 
					    (x2, y2) = (x + dx, y + dy)
 | 
				
			||||||
 | 
					    grid.at(y).at(x) = grid.at(y).at(x).bit-or(dir)
 | 
				
			||||||
 | 
					    path.push((x2, y2, dir))
 | 
				
			||||||
 | 
					    
 | 
				
			||||||
 | 
					    if not in-grid(x2, y2, w, h) {
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Not relevant
 | 
				
			||||||
 | 
					    /*
 | 
				
			||||||
 | 
					    if grid.at(y2).at(x2).bit-and(dir) != 0 {
 | 
				
			||||||
 | 
					      loops = true
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if grid.at(y2).at(x2) == obstacle {
 | 
				
			||||||
 | 
					      dir = rotate(dir)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      (x, y) = (x2, y2)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return path
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let add-obstacle(rows, cols, x, y) = {
 | 
				
			||||||
 | 
					  let add-in-line(line, v) = {
 | 
				
			||||||
 | 
					    if line.len() == 0 {
 | 
				
			||||||
 | 
					      line.push(v)
 | 
				
			||||||
 | 
					      return line
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    for (i, v2) in line.enumerate() {
 | 
				
			||||||
 | 
					      if v < v2 {
 | 
				
			||||||
 | 
					        line.insert(i, v)
 | 
				
			||||||
 | 
					        return line
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    line.push(v)
 | 
				
			||||||
 | 
					    return line
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rows.at(y) = add-in-line(rows.at(y), x)
 | 
				
			||||||
 | 
					  cols.at(x) = add-in-line(cols.at(x), y)
 | 
				
			||||||
 | 
					  return (rows, cols)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let walk-loops(rows, cols, ox, oy, dir) = {
 | 
				
			||||||
 | 
					  let (x, y) = (ox, oy)
 | 
				
			||||||
 | 
					  let w = cols.len()
 | 
				
			||||||
 | 
					  let h = rows.len()
 | 
				
			||||||
 | 
					  let visited = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while true {
 | 
				
			||||||
 | 
					    let pos = (x, y, dir)
 | 
				
			||||||
 | 
					    if pos in visited {
 | 
				
			||||||
 | 
					      return true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    visited.push(pos)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let line = if dir in (up, down) {cols.at(x)} else {rows.at(y)}
 | 
				
			||||||
 | 
					    let v = if dir in (up, down) {y} else {x}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // No obstacle
 | 
				
			||||||
 | 
					    if line.len() == 0 {
 | 
				
			||||||
 | 
					      return false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Leave grid
 | 
				
			||||||
 | 
					    if dir in (up, left) {
 | 
				
			||||||
 | 
					      if v < line.first() {
 | 
				
			||||||
 | 
					        return false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    } else if v > line.last() {
 | 
				
			||||||
 | 
					      return false
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let i = line.len() - 1
 | 
				
			||||||
 | 
					    for (j, v2) in line.enumerate() {
 | 
				
			||||||
 | 
					      if v < v2 {
 | 
				
			||||||
 | 
					        i = j
 | 
				
			||||||
 | 
					        if dir in (left, up) {
 | 
				
			||||||
 | 
					          i -= 1
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let v2 = line.at(i)
 | 
				
			||||||
 | 
					    if dir == up {
 | 
				
			||||||
 | 
					      y = v2 + 1
 | 
				
			||||||
 | 
					    } else if dir == right {
 | 
				
			||||||
 | 
					      x = v2 - 1
 | 
				
			||||||
 | 
					    } else if dir == down {
 | 
				
			||||||
 | 
					      y = v2 - 1
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      x = v2 + 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    dir = rotate(dir)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return false
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let grid = input.split("\n").map(l => l.clusters())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let ox = 0
 | 
				
			||||||
 | 
					  let oy = 0
 | 
				
			||||||
 | 
					  for (y, line) in grid.enumerate() {
 | 
				
			||||||
 | 
					    for (x, cell) in line.enumerate() {
 | 
				
			||||||
 | 
					      let value = values.at(cell)
 | 
				
			||||||
 | 
					      if value == up {
 | 
				
			||||||
 | 
					        (ox, oy) = (x, y)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      grid.at(y).at(x) = value
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let cols = ()
 | 
				
			||||||
 | 
					  let rows = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    let row = ()
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      if grid.at(y).at(x) == obstacle {
 | 
				
			||||||
 | 
					        row.push(x)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    rows.push(row)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for x in range(w) {
 | 
				
			||||||
 | 
					    let col = ()
 | 
				
			||||||
 | 
					    for y in range(h) {
 | 
				
			||||||
 | 
					      if grid.at(y).at(x) == obstacle {
 | 
				
			||||||
 | 
					        col.push(y)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    cols.push(col)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let path = puzzle1(grid, ox, oy, up)
 | 
				
			||||||
 | 
					  let count = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (x, y, _) in path.slice(1, path.len() - 1) {
 | 
				
			||||||
 | 
					    if x == ox and y == oy {
 | 
				
			||||||
 | 
					      continue
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let cell = grid.at(y).at(x)
 | 
				
			||||||
 | 
					    if cell.bit-and(possible-obstacle) == 0 {
 | 
				
			||||||
 | 
					      let (rows2, cols2) = add-obstacle(rows, cols, x, y)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if walk-loops(rows2, cols2, ox, oy, up) {
 | 
				
			||||||
 | 
					        count += 1
 | 
				
			||||||
 | 
					        grid.at(y).at(x) = cell.bit-or(possible-obstacle)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return count
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  6, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 6
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										160
									
								
								src/day7/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								src/day7/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solvable(values, target) = {
 | 
				
			||||||
 | 
					  if values.len() == 1 {
 | 
				
			||||||
 | 
					    return values.last() == target
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let values = values
 | 
				
			||||||
 | 
					  let v = values.pop()
 | 
				
			||||||
 | 
					  if calc.rem(target, v) == 0 {
 | 
				
			||||||
 | 
					    if solvable(values, target / v) {
 | 
				
			||||||
 | 
					      return true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if v > target {
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return solvable(values, target - v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let equations = input.split("\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for equation in equations {
 | 
				
			||||||
 | 
					    let (target, values) = equation.split(": ")
 | 
				
			||||||
 | 
					    target = int(target)
 | 
				
			||||||
 | 
					    values = values.split(" ").map(int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if solvable(values, target) {
 | 
				
			||||||
 | 
					      total += target
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let get-solution(values, target) = {
 | 
				
			||||||
 | 
					    if values.len() == 1 {
 | 
				
			||||||
 | 
					      return (values.last() == target, (target,))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let values = values
 | 
				
			||||||
 | 
					    let v = values.pop()
 | 
				
			||||||
 | 
					    if calc.rem(target, v) == 0 {
 | 
				
			||||||
 | 
					      let r = get-solution(values, target / v)
 | 
				
			||||||
 | 
					      if r.first() {
 | 
				
			||||||
 | 
					        return (true, r.last() + ("*", v))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if v > target {
 | 
				
			||||||
 | 
					      return (false, ())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let r = get-solution(values, target - v)
 | 
				
			||||||
 | 
					    if r.first() {
 | 
				
			||||||
 | 
					      return (true, r.last() + ("+", v))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return (false, ())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let num(v, x, y, name) = {
 | 
				
			||||||
 | 
					    draw.circle(
 | 
				
			||||||
 | 
					      (x, y),
 | 
				
			||||||
 | 
					      radius: 0.4,
 | 
				
			||||||
 | 
					      fill: gray.lighten(60%),
 | 
				
			||||||
 | 
					      name: name
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    draw.content((x, y), str(v))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let ope(o, x, y, name) = {
 | 
				
			||||||
 | 
					    let s = (
 | 
				
			||||||
 | 
					      "+": sym.plus,
 | 
				
			||||||
 | 
					      "*": sym.times
 | 
				
			||||||
 | 
					    ).at(o)
 | 
				
			||||||
 | 
					    draw.circle(
 | 
				
			||||||
 | 
					      (x, y),
 | 
				
			||||||
 | 
					      radius: 0.3,
 | 
				
			||||||
 | 
					      fill: orange.lighten(60%),
 | 
				
			||||||
 | 
					      name: name
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    draw.content((x, y), s)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let equations = input.split("\n")
 | 
				
			||||||
 | 
					  let diags = ()
 | 
				
			||||||
 | 
					  for equation in equations {
 | 
				
			||||||
 | 
					    let (target, values) = equation.split(": ")
 | 
				
			||||||
 | 
					    target = int(target)
 | 
				
			||||||
 | 
					    values = values.split(" ").map(int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let r = get-solution(values, target)
 | 
				
			||||||
 | 
					    if not r.first() {
 | 
				
			||||||
 | 
					      continue
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let diag = canvas({
 | 
				
			||||||
 | 
					      let lvl = 0
 | 
				
			||||||
 | 
					      let steps = r.last()
 | 
				
			||||||
 | 
					      let prev = none
 | 
				
			||||||
 | 
					      let v = steps.remove(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while true {
 | 
				
			||||||
 | 
					        num(v, lvl, -lvl, str(lvl) + "-0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if lvl != 0 {
 | 
				
			||||||
 | 
					          draw.line(
 | 
				
			||||||
 | 
					            str(lvl - 1) + "-1",
 | 
				
			||||||
 | 
					            str(lvl) + "-0"
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if steps.len() == 0 {
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let op = steps.remove(0)
 | 
				
			||||||
 | 
					        let v2 = steps.remove(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ope(op, lvl + 1, -lvl, str(lvl) + "-1")
 | 
				
			||||||
 | 
					        num(v2, lvl + 2, -lvl, str(lvl) + "-2")
 | 
				
			||||||
 | 
					        draw.line(
 | 
				
			||||||
 | 
					          str(lvl) + "-0",
 | 
				
			||||||
 | 
					          str(lvl) + "-1"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        draw.line(
 | 
				
			||||||
 | 
					          str(lvl) + "-2",
 | 
				
			||||||
 | 
					          str(lvl) + "-1"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if op == "+" {
 | 
				
			||||||
 | 
					          v += v2
 | 
				
			||||||
 | 
					        } else if op == "*" {
 | 
				
			||||||
 | 
					          v *= v2
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lvl += 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    diags.push(diag)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if calc.rem(diags.len(), 2) == 1 {
 | 
				
			||||||
 | 
					    diags.last() = grid.cell(colspan: 2, diags.last())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  grid(
 | 
				
			||||||
 | 
					    columns: 2,
 | 
				
			||||||
 | 
					    stroke: (paint: black, dash: "dashed"),
 | 
				
			||||||
 | 
					    align: center + horizon,
 | 
				
			||||||
 | 
					    inset: 0.4em,
 | 
				
			||||||
 | 
					    ..diags
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  7, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 3749,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										197
									
								
								src/day7/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								src/day7/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,197 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let concat(a, b) = {
 | 
				
			||||||
 | 
					  return int(str(a) + str(b))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solvable(values, target) = {
 | 
				
			||||||
 | 
					  if values.len() == 1 {
 | 
				
			||||||
 | 
					    return values.last() == target
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let values = values
 | 
				
			||||||
 | 
					  let v = values.pop()
 | 
				
			||||||
 | 
					  if calc.rem(target, v) == 0 {
 | 
				
			||||||
 | 
					    if solvable(values, target / v) {
 | 
				
			||||||
 | 
					      return true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let str-target = str(target)
 | 
				
			||||||
 | 
					  let str-v = str(v)
 | 
				
			||||||
 | 
					  if str-target == str-v {
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if str-target.ends-with(str-v) {
 | 
				
			||||||
 | 
					    let target2 = str-target.slice(
 | 
				
			||||||
 | 
					      0,
 | 
				
			||||||
 | 
					      str-target.len() - str-v.len()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    if solvable(values, int(target2)) {
 | 
				
			||||||
 | 
					      return true
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  if v > target {
 | 
				
			||||||
 | 
					    return false
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return solvable(values, target - v)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let equations = input.split("\n")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for equation in equations {
 | 
				
			||||||
 | 
					    let (target, values) = equation.split(": ")
 | 
				
			||||||
 | 
					    target = int(target)
 | 
				
			||||||
 | 
					    values = values.split(" ").map(int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if solvable(values, target) {
 | 
				
			||||||
 | 
					      total += target
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let get-solution(values, target) = {
 | 
				
			||||||
 | 
					    if values.len() == 1 {
 | 
				
			||||||
 | 
					      return (values.last() == target, (target,))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let values = values
 | 
				
			||||||
 | 
					    let v = values.pop()
 | 
				
			||||||
 | 
					    if calc.rem(target, v) == 0 {
 | 
				
			||||||
 | 
					      let r = get-solution(values, target / v)
 | 
				
			||||||
 | 
					      if r.first() {
 | 
				
			||||||
 | 
					        return (true, r.last() + ("*", v))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let str-target = str(target)
 | 
				
			||||||
 | 
					    let str-v = str(v)
 | 
				
			||||||
 | 
					    if str-target == str-v {
 | 
				
			||||||
 | 
					      return (false, ())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if str-target.ends-with(str-v) {
 | 
				
			||||||
 | 
					      let target2 = str-target.slice(
 | 
				
			||||||
 | 
					        0,
 | 
				
			||||||
 | 
					        str-target.len() - str-v.len()
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      let r = get-solution(values, int(target2))
 | 
				
			||||||
 | 
					      if r.first() {
 | 
				
			||||||
 | 
					        return (true, r.last() + ("||", v))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if v > target {
 | 
				
			||||||
 | 
					      return (false, ())
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let r = get-solution(values, target - v)
 | 
				
			||||||
 | 
					    if r.first() {
 | 
				
			||||||
 | 
					      return (true, r.last() + ("+", v))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return (false, ())
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let num(v, x, y, name) = {
 | 
				
			||||||
 | 
					    draw.circle(
 | 
				
			||||||
 | 
					      (x, y),
 | 
				
			||||||
 | 
					      radius: 0.4,
 | 
				
			||||||
 | 
					      fill: gray.lighten(60%),
 | 
				
			||||||
 | 
					      name: name
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    draw.content((x, y), str(v))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  let ope(o, x, y, name) = {
 | 
				
			||||||
 | 
					    let s = (
 | 
				
			||||||
 | 
					      "+": sym.plus,
 | 
				
			||||||
 | 
					      "*": sym.times,
 | 
				
			||||||
 | 
					      "||": sym.bar + sym.bar
 | 
				
			||||||
 | 
					    ).at(o)
 | 
				
			||||||
 | 
					    draw.circle(
 | 
				
			||||||
 | 
					      (x, y),
 | 
				
			||||||
 | 
					      radius: 0.3,
 | 
				
			||||||
 | 
					      fill: orange.lighten(60%),
 | 
				
			||||||
 | 
					      name: name
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    draw.content((x, y), s)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let equations = input.split("\n")
 | 
				
			||||||
 | 
					  let diags = ()
 | 
				
			||||||
 | 
					  for equation in equations {
 | 
				
			||||||
 | 
					    let (target, values) = equation.split(": ")
 | 
				
			||||||
 | 
					    target = int(target)
 | 
				
			||||||
 | 
					    values = values.split(" ").map(int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    let r = get-solution(values, target)
 | 
				
			||||||
 | 
					    if not r.first() {
 | 
				
			||||||
 | 
					      continue
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    let diag = canvas({
 | 
				
			||||||
 | 
					      let lvl = 0
 | 
				
			||||||
 | 
					      let steps = r.last()
 | 
				
			||||||
 | 
					      let prev = none
 | 
				
			||||||
 | 
					      let v = steps.remove(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while true {
 | 
				
			||||||
 | 
					        num(v, lvl, -lvl, str(lvl) + "-0")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if lvl != 0 {
 | 
				
			||||||
 | 
					          draw.line(
 | 
				
			||||||
 | 
					            str(lvl - 1) + "-1",
 | 
				
			||||||
 | 
					            str(lvl) + "-0"
 | 
				
			||||||
 | 
					          )
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if steps.len() == 0 {
 | 
				
			||||||
 | 
					          break
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        let op = steps.remove(0)
 | 
				
			||||||
 | 
					        let v2 = steps.remove(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        ope(op, lvl + 1, -lvl, str(lvl) + "-1")
 | 
				
			||||||
 | 
					        num(v2, lvl + 2, -lvl, str(lvl) + "-2")
 | 
				
			||||||
 | 
					        draw.line(
 | 
				
			||||||
 | 
					          str(lvl) + "-0",
 | 
				
			||||||
 | 
					          str(lvl) + "-1"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        draw.line(
 | 
				
			||||||
 | 
					          str(lvl) + "-2",
 | 
				
			||||||
 | 
					          str(lvl) + "-1"
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        if op == "+" {
 | 
				
			||||||
 | 
					          v += v2
 | 
				
			||||||
 | 
					        } else if op == "*" {
 | 
				
			||||||
 | 
					          v *= v2
 | 
				
			||||||
 | 
					        } else if op == "||" {
 | 
				
			||||||
 | 
					          v = int(str(v) + str(v2))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        lvl += 1
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    })
 | 
				
			||||||
 | 
					    diags.push(diag)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  diags.last() = grid.cell(
 | 
				
			||||||
 | 
					    colspan: 3 - calc.rem(diags.len() - 1, 3),
 | 
				
			||||||
 | 
					    diags.last()
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  grid(
 | 
				
			||||||
 | 
					    columns: 3,
 | 
				
			||||||
 | 
					    stroke: (paint: black, dash: "dashed"),
 | 
				
			||||||
 | 
					    align: center + horizon,
 | 
				
			||||||
 | 
					    inset: 0.4em,
 | 
				
			||||||
 | 
					    ..diags
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  7, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 11387,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										98
									
								
								src/day8/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								src/day8/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "@preview/cetz:0.3.1": canvas, draw
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-grid(w, h, x, y) = {
 | 
				
			||||||
 | 
					  return 0 <= x and x < w and 0 <= y and y < h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input, return-data: false) = {
 | 
				
			||||||
 | 
					  let by-freq = (:)
 | 
				
			||||||
 | 
					  let antinodes = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      let c = grid.at(y).at(x)
 | 
				
			||||||
 | 
					      if c == "." {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if c not in by-freq {
 | 
				
			||||||
 | 
					        by-freq.insert(c, ())
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (x2, y2) in by-freq.at(c) {
 | 
				
			||||||
 | 
					        let (dx, dy) = (x2 - x, y2 - y)
 | 
				
			||||||
 | 
					        let node1 = (x - dx, y - dy)
 | 
				
			||||||
 | 
					        let node2 = (x2 + dx, y2 + dy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if in-grid(..node1) and node1 not in antinodes {
 | 
				
			||||||
 | 
					          antinodes.push(node1)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if in-grid(..node2) and node2 not in antinodes {
 | 
				
			||||||
 | 
					          antinodes.push(node2)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      by-freq.at(c).push((x, y))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return if return-data {
 | 
				
			||||||
 | 
					    (grid, by-freq, antinodes)
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    antinodes.len()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(solve, input) = {
 | 
				
			||||||
 | 
					  let (grid, by-freq, antinodes) = solve(input, return-data: true)
 | 
				
			||||||
 | 
					  let w = grid.first().len()
 | 
				
			||||||
 | 
					  let h = grid.len()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let freqs = by-freq.keys()
 | 
				
			||||||
 | 
					  let n-freqs = freqs.len()
 | 
				
			||||||
 | 
					  let colors = gradient.linear(red, orange, yellow, green, aqua, blue, purple)
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  canvas(length: 1.65em, {
 | 
				
			||||||
 | 
					    for y in range(h) {
 | 
				
			||||||
 | 
					      for x in range(w) {
 | 
				
			||||||
 | 
					        draw.circle(
 | 
				
			||||||
 | 
					          (x, y),
 | 
				
			||||||
 | 
					          radius: 0.1,
 | 
				
			||||||
 | 
					          fill: gray,
 | 
				
			||||||
 | 
					          stroke: none
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for (i, freq) in freqs.enumerate() {
 | 
				
			||||||
 | 
					          let col = colors.sample(i * 100% / n-freqs)
 | 
				
			||||||
 | 
					          for (ax, ay) in by-freq.at(freq) {
 | 
				
			||||||
 | 
					            draw.circle(
 | 
				
			||||||
 | 
					              (ax, ay),
 | 
				
			||||||
 | 
					              radius: 0.4,
 | 
				
			||||||
 | 
					              fill: col
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (anx, any) in antinodes {
 | 
				
			||||||
 | 
					      draw.rect(
 | 
				
			||||||
 | 
					        (anx - 0.4, any - 0.4),
 | 
				
			||||||
 | 
					        (anx + 0.4, any + 0.4),
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  })
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  8, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 14,
 | 
				
			||||||
 | 
					  visualize: visualize.with(solve)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										72
									
								
								src/day8/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								src/day8/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "puzzle1.typ": visualize
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let in-grid(w, h, x, y) = {
 | 
				
			||||||
 | 
					  return 0 <= x and x < w and 0 <= y and y < h
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let get-antinodes(in-grid, x1, y1, x2, y2) = {
 | 
				
			||||||
 | 
					  let (dx, dy) = (x2 - x1, y2 - y1)
 | 
				
			||||||
 | 
					  let f = calc.gcd(dx, dy)
 | 
				
			||||||
 | 
					  dx /= f
 | 
				
			||||||
 | 
					  dy /= f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let walk(ox, oy, dx, dy) = {
 | 
				
			||||||
 | 
					    let x = ox
 | 
				
			||||||
 | 
					    let y = oy
 | 
				
			||||||
 | 
					    let pos = ()
 | 
				
			||||||
 | 
					    while in-grid(x, y) {
 | 
				
			||||||
 | 
					      pos.push((x, y))
 | 
				
			||||||
 | 
					      x += dx
 | 
				
			||||||
 | 
					      y += dy
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    return pos
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let antinodes = walk(x1, y1, dx, dy) + walk(x1, y1, -dx, -dy)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return antinodes
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input, return-data: false) = {
 | 
				
			||||||
 | 
					  let by-freq = (:)
 | 
				
			||||||
 | 
					  let antinodes = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  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 get-antinodes = get-antinodes.with(in-grid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for y in range(h) {
 | 
				
			||||||
 | 
					    for x in range(w) {
 | 
				
			||||||
 | 
					      let c = grid.at(y).at(x)
 | 
				
			||||||
 | 
					      if c == "." {
 | 
				
			||||||
 | 
					        continue
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if c not in by-freq {
 | 
				
			||||||
 | 
					        by-freq.insert(c, ())
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      for (x2, y2) in by-freq.at(c) {
 | 
				
			||||||
 | 
					        antinodes += get-antinodes(x, y, x2, y2)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      by-freq.at(c).push((x, y))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  antinodes = antinodes.dedup()
 | 
				
			||||||
 | 
					  return if return-data {
 | 
				
			||||||
 | 
					    (grid, by-freq, antinodes)
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    antinodes.len()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  8, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: 34,
 | 
				
			||||||
 | 
					  visualize: visualize.with(solve)
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
							
								
								
									
										152
									
								
								src/day9/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										152
									
								
								src/day9/puzzle1.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,152 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let parse-input(input) = {
 | 
				
			||||||
 | 
					  let blocks = ()
 | 
				
			||||||
 | 
					  let holes = ()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let block-i = 0
 | 
				
			||||||
 | 
					  let is-block = true
 | 
				
			||||||
 | 
					  let pos = 0
 | 
				
			||||||
 | 
					  for c in input {
 | 
				
			||||||
 | 
					    let block = (pos, int(c))
 | 
				
			||||||
 | 
					    if is-block {
 | 
				
			||||||
 | 
					      block.push(block-i)
 | 
				
			||||||
 | 
					      block-i += 1
 | 
				
			||||||
 | 
					      blocks.push(block)
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      holes.push(block)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    pos += int(c)
 | 
				
			||||||
 | 
					    is-block = not is-block
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return (blocks, holes)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let compute-checksum(blocks) = {
 | 
				
			||||||
 | 
					  let total = 0
 | 
				
			||||||
 | 
					  for (i0, l, id) in blocks {
 | 
				
			||||||
 | 
					    total += id * range(i0, i0 + l).sum()
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return total
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let insert(list, elmt) = {
 | 
				
			||||||
 | 
					  for (i, elmt2) in list.enumerate() {
 | 
				
			||||||
 | 
					    if elmt.first() < elmt2.first() {
 | 
				
			||||||
 | 
					      list.insert(i, elmt)
 | 
				
			||||||
 | 
					      return list
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  list.push(elmt)
 | 
				
			||||||
 | 
					  return list
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let (blocks, holes) = parse-input(input)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (hi, hl) in holes {
 | 
				
			||||||
 | 
					    while hl > 0 {
 | 
				
			||||||
 | 
					      if blocks.last().first() < hi + hl {
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let (bi, bl, bid) = blocks.pop()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      let len = calc.min(hl, bl)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      blocks.insert(0, (hi, len, bid))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if len < bl {
 | 
				
			||||||
 | 
					        blocks = insert(blocks, (bi, bl - len, bid))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      hl -= len
 | 
				
			||||||
 | 
					      hi += len
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if hl > 0 {
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return compute-checksum(blocks)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let col-gradient = gradient.linear(red, orange, yellow, green, aqua, blue, purple)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let show-fs(size, max-id, blocks) = {
 | 
				
			||||||
 | 
					  let cells = ()
 | 
				
			||||||
 | 
					  for (bi, bl, bid) in blocks {
 | 
				
			||||||
 | 
					    cells.push(
 | 
				
			||||||
 | 
					      grid.cell(
 | 
				
			||||||
 | 
					        x: bi,
 | 
				
			||||||
 | 
					        colspan: bl,
 | 
				
			||||||
 | 
					        fill: col-gradient.sample(bid * 100% / max-id),
 | 
				
			||||||
 | 
					        str(bid)
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  grid(
 | 
				
			||||||
 | 
					    columns: (1fr,) * size,
 | 
				
			||||||
 | 
					    align: center + horizon,
 | 
				
			||||||
 | 
					    stroke: black,
 | 
				
			||||||
 | 
					    inset: 0.3em,
 | 
				
			||||||
 | 
					    ..cells
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let (blocks, holes) = parse-input(input)
 | 
				
			||||||
 | 
					  let max-id = blocks.last().last()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let last-block = blocks.last()
 | 
				
			||||||
 | 
					  let last-holes = holes.last()
 | 
				
			||||||
 | 
					  let show-fs = show-fs.with(
 | 
				
			||||||
 | 
					    calc.max(
 | 
				
			||||||
 | 
					      last-block.first() + last-block.at(1),
 | 
				
			||||||
 | 
					      last-holes.first() + last-holes.last()
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    max-id
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  let steps = ()
 | 
				
			||||||
 | 
					  steps.push(show-fs(blocks))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (hi, hl) in holes {
 | 
				
			||||||
 | 
					    while hl > 0 {
 | 
				
			||||||
 | 
					      if blocks.last().first() < hi + hl {
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      let (bi, bl, bid) = blocks.pop()
 | 
				
			||||||
 | 
					      let len = calc.min(hl, bl)
 | 
				
			||||||
 | 
					      blocks.insert(0, (hi, len, bid))
 | 
				
			||||||
 | 
					      if len < bl {
 | 
				
			||||||
 | 
					        blocks = insert(blocks, (bi, bl - len, bid))
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      hl -= len
 | 
				
			||||||
 | 
					      hi += len
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    if hl > 0 {
 | 
				
			||||||
 | 
					      break
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    steps.push(show-fs(blocks))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  stack(
 | 
				
			||||||
 | 
					    spacing: 0.5em,
 | 
				
			||||||
 | 
					    ..steps
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  9, 1,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: (
 | 
				
			||||||
 | 
					    "1": 60,
 | 
				
			||||||
 | 
					    "2": 1928
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  only-example: true,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Too long to recompile everytime
 | 
				
			||||||
 | 
					#show-result(6390180901651)
 | 
				
			||||||
							
								
								
									
										86
									
								
								src/day9/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/day9/puzzle2.typ
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,86 @@
 | 
				
			|||||||
 | 
					#import "/src/utils.typ": *
 | 
				
			||||||
 | 
					#import "puzzle1.typ": parse-input, compute-checksum, insert, show-fs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let solve(input) = {
 | 
				
			||||||
 | 
					  let (blocks, holes) = parse-input(input)
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  let blocks2 = ()
 | 
				
			||||||
 | 
					  for (bi, bl, bid) in blocks.rev() {
 | 
				
			||||||
 | 
					    for (i, (hi, hl)) in holes.enumerate() {
 | 
				
			||||||
 | 
					      if hi < bi and bl <= hl {
 | 
				
			||||||
 | 
					        bi = hi
 | 
				
			||||||
 | 
					        holes.at(i).first() += bl
 | 
				
			||||||
 | 
					        holes.at(i).last() -= bl
 | 
				
			||||||
 | 
					        if bl == hl {
 | 
				
			||||||
 | 
					          holes.remove(i)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    blocks2.push((bi, bl, bid))
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  return compute-checksum(blocks2)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let visualize(input) = {
 | 
				
			||||||
 | 
					  let (blocks, holes) = parse-input(input)
 | 
				
			||||||
 | 
					  let max-id = blocks.last().last()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let last-block = blocks.last()
 | 
				
			||||||
 | 
					  let last-holes = holes.last()
 | 
				
			||||||
 | 
					  let show-fs = show-fs.with(
 | 
				
			||||||
 | 
					    calc.max(
 | 
				
			||||||
 | 
					      last-block.first() + last-block.at(1),
 | 
				
			||||||
 | 
					      last-holes.first() + last-holes.last()
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    max-id
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					  let steps = ()
 | 
				
			||||||
 | 
					  steps.push(show-fs(blocks))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let ids = ()
 | 
				
			||||||
 | 
					  let blocks2 = ()
 | 
				
			||||||
 | 
					  for (bi, bl, bid) in blocks.rev() {
 | 
				
			||||||
 | 
					    let moved = false
 | 
				
			||||||
 | 
					    for (i, (hi, hl)) in holes.enumerate() {
 | 
				
			||||||
 | 
					      if hi < bi and bl <= hl {
 | 
				
			||||||
 | 
					        bi = hi
 | 
				
			||||||
 | 
					        holes.at(i).first() += bl
 | 
				
			||||||
 | 
					        holes.at(i).last() -= bl
 | 
				
			||||||
 | 
					        if bl == hl {
 | 
				
			||||||
 | 
					          _ = holes.remove(i)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        moved = true
 | 
				
			||||||
 | 
					        break
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    ids.push(bid)
 | 
				
			||||||
 | 
					    blocks2.push((bi, bl, bid))
 | 
				
			||||||
 | 
					    if moved {
 | 
				
			||||||
 | 
					      steps.push(show-fs(
 | 
				
			||||||
 | 
					        blocks.filter(b => b.last() not in ids) +
 | 
				
			||||||
 | 
					        blocks2
 | 
				
			||||||
 | 
					      ))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  stack(
 | 
				
			||||||
 | 
					    spacing: 0.5em,
 | 
				
			||||||
 | 
					    ..steps
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#show-puzzle(
 | 
				
			||||||
 | 
					  9, 2,
 | 
				
			||||||
 | 
					  solve,
 | 
				
			||||||
 | 
					  example: (
 | 
				
			||||||
 | 
					    "1": 132,
 | 
				
			||||||
 | 
					    "2": 2858
 | 
				
			||||||
 | 
					  ),
 | 
				
			||||||
 | 
					  only-example: true,
 | 
				
			||||||
 | 
					  visualize: visualize
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Too long to recompile everytime
 | 
				
			||||||
 | 
					#show-result(6412390114238)
 | 
				
			||||||
							
								
								
									
										
											BIN
										
									
								
								src/main.pdf
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								src/main.pdf
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							@@ -17,7 +17,6 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#v(2cm)
 | 
					#v(2cm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
#align(center, canvas({
 | 
					#align(center, canvas({
 | 
				
			||||||
  draw.merge-path(
 | 
					  draw.merge-path(
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@@ -50,7 +49,7 @@
 | 
				
			|||||||
    fill: rgb("#63584B"),
 | 
					    fill: rgb("#63584B"),
 | 
				
			||||||
    stroke: none
 | 
					    stroke: none
 | 
				
			||||||
  )
 | 
					  )
 | 
				
			||||||
}))*/
 | 
					}))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#v(1fr)
 | 
					#v(1fr)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -67,22 +67,37 @@
 | 
				
			|||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let show-puzzle(day, puzzle, func, example: none, visualize: none) = {
 | 
					#let show-puzzle(
 | 
				
			||||||
 | 
					  day,
 | 
				
			||||||
 | 
					  puzzle,
 | 
				
			||||||
 | 
					  func,
 | 
				
			||||||
 | 
					  example: none,
 | 
				
			||||||
 | 
					  visualize: none,
 | 
				
			||||||
 | 
					  only-example: false
 | 
				
			||||||
 | 
					) = {
 | 
				
			||||||
  let check-example = check-example.with(visualize: visualize)
 | 
					  let check-example = check-example.with(visualize: visualize)
 | 
				
			||||||
  if example != none {
 | 
					  if example != none {
 | 
				
			||||||
    if type(example) == dictionary {
 | 
					    if type(example) == dictionary {
 | 
				
			||||||
      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)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    linebreak()
 | 
					    linebreak()
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  let input = get-input(day)
 | 
					  if not only-example {
 | 
				
			||||||
  let result = (func)(input)
 | 
					    let input = get-input(day)
 | 
				
			||||||
  show-result(result)
 | 
					    let result = (func)(input)
 | 
				
			||||||
 | 
					    show-result(result)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let day-template(day, puzzle1, puzzle2, stars: 0) = {
 | 
					#let day-template(day, puzzle1, puzzle2, stars: 0) = {
 | 
				
			||||||
@@ -113,7 +128,30 @@
 | 
				
			|||||||
  )
 | 
					  )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#let make-progress(links: true) = context {
 | 
					#let make-badge(name, img) = {
 | 
				
			||||||
 | 
					  let img = scale(3em, img, reflow: true)
 | 
				
			||||||
 | 
					  box(
 | 
				
			||||||
 | 
					    grid(
 | 
				
			||||||
 | 
					      columns: 2,
 | 
				
			||||||
 | 
					      align: center + horizon,
 | 
				
			||||||
 | 
					      column-gutter: 0.4em,
 | 
				
			||||||
 | 
					      [*#name*],
 | 
				
			||||||
 | 
					      box(
 | 
				
			||||||
 | 
					        img,
 | 
				
			||||||
 | 
					        radius: 1.5em,
 | 
				
			||||||
 | 
					        clip: true
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					    ),
 | 
				
			||||||
 | 
					    fill: green.lighten(50%),
 | 
				
			||||||
 | 
					    inset: (left: 0.8em),
 | 
				
			||||||
 | 
					    radius: 1.5em
 | 
				
			||||||
 | 
					  )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#let make-progress(
 | 
				
			||||||
 | 
					  links: true,
 | 
				
			||||||
 | 
					  badge: none
 | 
				
			||||||
 | 
					) = context {
 | 
				
			||||||
  let stars = star-state.final()
 | 
					  let stars = star-state.final()
 | 
				
			||||||
  let star-cnt = stars.values().sum(default: 0)
 | 
					  let star-cnt = stars.values().sum(default: 0)
 | 
				
			||||||
  let first-weekday = datetime(
 | 
					  let first-weekday = datetime(
 | 
				
			||||||
@@ -140,7 +178,11 @@
 | 
				
			|||||||
    cells.push(cell)
 | 
					    cells.push(cell)
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  [*Stars: #star-cnt / 50*]
 | 
					  let badge = if badge != none {
 | 
				
			||||||
 | 
					    make-badge(badge.name, badge.img)
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [*Stars: #star-cnt / 50*#h(1fr)#badge]
 | 
				
			||||||
  table(
 | 
					  table(
 | 
				
			||||||
    columns: (1fr,)*7,
 | 
					    columns: (1fr,)*7,
 | 
				
			||||||
    inset: 0.8em,
 | 
					    inset: 0.8em,
 | 
				
			||||||
@@ -153,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