Compare commits
58 Commits
95981a219a
...
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
|
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: 90 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)
|
||||||
|
)
|
||||||
|
)
|
@ -3,4 +3,48 @@
|
|||||||
2:
|
2:
|
||||||
stars: 2
|
stars: 2
|
||||||
3:
|
3:
|
||||||
stars: 2
|
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
|
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 @@
|
|||||||
|
#####
|
||||||
|
.####
|
||||||
|
.####
|
||||||
|
.####
|
||||||
|
.#.#.
|
||||||
|
.#...
|
||||||
|
.....
|
||||||
|
|
||||||
|
#####
|
||||||
|
##.##
|
||||||
|
.#.##
|
||||||
|
...##
|
||||||
|
...#.
|
||||||
|
...#.
|
||||||
|
.....
|
||||||
|
|
||||||
|
.....
|
||||||
|
#....
|
||||||
|
#....
|
||||||
|
#...#
|
||||||
|
#.#.#
|
||||||
|
#.###
|
||||||
|
#####
|
||||||
|
|
||||||
|
.....
|
||||||
|
.....
|
||||||
|
#.#..
|
||||||
|
###..
|
||||||
|
###.#
|
||||||
|
###.#
|
||||||
|
#####
|
||||||
|
|
||||||
|
.....
|
||||||
|
.....
|
||||||
|
.....
|
||||||
|
#....
|
||||||
|
#.#..
|
||||||
|
#.#.#
|
||||||
|
#####
|
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)
|
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
@ -1,7 +1,8 @@
|
|||||||
#import "/src/utils.typ": *
|
#import "/src/utils.typ": *
|
||||||
|
|
||||||
|
#let reg = regex("mul\((\d{1,3}),(\d{1,3})\)")
|
||||||
#let solve(input) = {
|
#let solve(input) = {
|
||||||
let matches = input.matches(regex("mul\((\d{1,3}),(\d{1,3})\)"))
|
let matches = input.matches(reg)
|
||||||
let total = matches.map(m => {
|
let total = matches.map(m => {
|
||||||
m.captures.map(int)
|
m.captures.map(int)
|
||||||
.product()
|
.product()
|
||||||
@ -9,8 +10,24 @@
|
|||||||
return total
|
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(
|
#show-puzzle(
|
||||||
3, 1,
|
3, 1,
|
||||||
solve,
|
solve,
|
||||||
example: ("1": 161)
|
example: ("1": 161),
|
||||||
|
visualize: visualize
|
||||||
)
|
)
|
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)
|
||||||
|
@ -81,6 +81,12 @@
|
|||||||
for (suffix, result) in example.pairs() {
|
for (suffix, result) in example.pairs() {
|
||||||
check-example(day, func, result, suffix: suffix)
|
check-example(day, func, result, suffix: suffix)
|
||||||
}
|
}
|
||||||
|
} else if type(example) == array {
|
||||||
|
for ex in example {
|
||||||
|
let suffix = ex.at("suffix", default: none)
|
||||||
|
let args = ex.at("args", default: (:))
|
||||||
|
check-example(day, func.with(..args), ex.result, suffix: suffix)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
check-example(day, func, example)
|
check-example(day, func, example)
|
||||||
}
|
}
|
||||||
@ -122,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(
|
||||||
@ -149,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,
|
||||||
@ -162,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