1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
|
; Advent of Code 2025 - Day 11
bits 64
default rel
global _start
%define SYS_EXIT 60
%define SYS_READ 0
%define SYS_WRITE 1
%define SYS_OPEN 2
%define SYS_CLOSE 3
%define O_RDONLY 0
%define STDOUT 1
%define RAW_SIZE 262144
%define NCODES 17576 ; 26^3 possible 3-letter names
%define OUT_CODE 10003 ; encode("out")
%define YOU_CODE 16608 ; encode("you")
%define SVR_CODE 12731 ; encode("svr")
%define DAC_CODE 2030 ; encode("dac")
%define FFT_CODE 3529 ; encode("fft")
%define MAXEDGES 8192
section .data
input_path db "input/day11.txt", 0
newline db 10
label1 db "Part 1: ", 0
label2 db "Part 2: ", 0
section .bss
raw resb RAW_SIZE
adj_head resq NCODES ; first child index in `child`
adj_cnt resq NCODES ; number of children
child resq MAXEDGES ; flat child codes
memo resq NCODES ; path counts, -1 = not computed
blocked resb NCODES ; 1 = treat node as a dead end
raw_len resq 1
t1 resq 1
t2 resq 1
number_buffer resb 32
section .text
_start:
mov rax, SYS_OPEN
mov rdi, input_path
mov rsi, O_RDONLY
xor rdx, rdx
syscall
mov r12, rax
xor r13, r13
read_loop:
mov rax, SYS_READ
mov rdi, r12
lea rsi, [raw + r13]
mov rdx, RAW_SIZE
sub rdx, r13
syscall
cmp rax, 0
jle read_done
add r13, rax
jmp read_loop
read_done:
mov [raw_len], r13
mov rax, SYS_CLOSE
mov rdi, r12
syscall
; memo = -1 everywhere
mov rax, -1
lea rdi, [memo]
mov rcx, NCODES
rep stosq
; parse "name: a b c" adjacency lines
lea rsi, [raw]
mov rax, [raw_len]
lea r13, [raw + rax] ; end pointer
xor r12, r12 ; child write index
parse_line:
cmp rsi, r13
jae parse_done
mov al, [rsi]
cmp al, 10
je parse_skip
cmp al, 13
je parse_skip
call encode3 ; RAX = node code
mov r10, rax
mov [adj_head + r10*8], r12
skip_sep:
mov al, [rsi]
cmp al, ':'
je .adv
cmp al, ' '
je .adv
jmp children
.adv:
inc rsi
jmp skip_sep
children:
xor r11, r11 ; child count
child_loop:
cmp rsi, r13
jae child_done
mov al, [rsi]
cmp al, 10
je child_done
cmp al, 13
je child_done
cmp al, ' '
je child_space
call encode3
mov [child + r12*8], rax
inc r12
inc r11
jmp child_loop
child_space:
inc rsi
jmp child_loop
child_done:
mov [adj_cnt + r10*8], r11
skip_eol:
cmp rsi, r13
jae parse_done
mov al, [rsi]
inc rsi
cmp al, 10
jne skip_eol
jmp parse_line
parse_skip:
inc rsi
jmp parse_line
parse_done:
; part 1: paths you -> out
call clear_memo
mov rdi, YOU_CODE
call count
mov [t1], rax
; part 2: paths svr -> out visiting both dac and fft, via inclusion-exclusion
call clear_memo
mov rdi, SVR_CODE
call count
mov r12, rax ; N(all)
mov byte [blocked + DAC_CODE], 1
call clear_memo
mov rdi, SVR_CODE
call count
mov r13, rax ; N(avoid dac)
mov byte [blocked + DAC_CODE], 0
mov byte [blocked + FFT_CODE], 1
call clear_memo
mov rdi, SVR_CODE
call count
mov r14, rax ; N(avoid fft)
mov byte [blocked + FFT_CODE], 0
mov byte [blocked + DAC_CODE], 1
mov byte [blocked + FFT_CODE], 1
call clear_memo
mov rdi, SVR_CODE
call count ; N(avoid both) in rax
mov byte [blocked + DAC_CODE], 0
mov byte [blocked + FFT_CODE], 0
sub r12, r13
sub r12, r14
add r12, rax
mov [t2], r12
mov rcx, label1
call print_string
mov rax, [t1]
call print_int64
call print_newline
mov rcx, label2
call print_string
mov rax, [t2]
call print_int64
call print_newline
mov rax, SYS_EXIT
xor rdi, rdi
syscall
; clear_memo: reset all memo entries to -1. Clobbers RAX, RCX, RDI.
clear_memo:
mov rax, -1
lea rdi, [memo]
mov rcx, NCODES
rep stosq
ret
; encode3: read 3 lowercase letters at [RSI] into a base-26 code, advance RSI.
; Output: RAX = code. Clobbers RAX, RDX.
encode3:
movzx eax, byte [rsi]
sub eax, 'a'
imul eax, eax, 676
movzx edx, byte [rsi + 1]
sub edx, 'a'
imul edx, edx, 26
add eax, edx
movzx edx, byte [rsi + 2]
sub edx, 'a'
add eax, edx
add rsi, 3
ret
; count: number of paths from node RDI to out. Memoised DFS over the DAG.
; Output: RAX. Clobbers RAX; preserves everything else via the stack.
count:
cmp rdi, OUT_CODE
je .one
cmp byte [blocked + rdi], 0
jne .zero
mov rax, [memo + rdi*8]
cmp rax, -1
jne .ret
push r12
push r13
push r14
push r15
mov r12, rdi
mov r13, [adj_head + r12*8]
mov r14, r13
add r14, [adj_cnt + r12*8]
xor r15, r15
.loop:
cmp r13, r14
jge .done
mov rdi, [child + r13*8]
call count
add r15, rax
inc r13
jmp .loop
.done:
mov [memo + r12*8], r15
mov rax, r15
pop r15
pop r14
pop r13
pop r12
ret
.one:
mov rax, 1
ret
.zero:
xor eax, eax
.ret:
ret
print_newline:
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, newline
mov rdx, 1
syscall
ret
print_int64:
mov rdi, number_buffer+31
mov byte [rdi], 0
dec rdi
cmp rax, 0
jne .conv
mov byte [rdi], '0'
jmp .done
.conv:
mov rbx, 10
.next:
xor rdx, rdx
div rbx
add dl, '0'
mov [rdi], dl
dec rdi
test rax, rax
jnz .next
inc rdi
.done:
mov rax, SYS_WRITE
mov rsi, rdi
mov rdi, STDOUT
mov rdx, number_buffer+31
sub rdx, rsi
syscall
ret
print_string:
push rcx
push rdx
mov rdx, 0
.length:
cmp byte [rcx+rdx], 0
je .submit
inc rdx
jmp .length
.submit:
mov rax, SYS_WRITE
mov rdi, STDOUT
mov rsi, rcx
syscall
pop rdx
pop rcx
ret
|