Advent of Code solutions
1#!/usr/bin/env ruby
2
3require "json"
4
5def which(cmd)
6 exts = ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""]
7 ENV["PATH"].split(File::PATH_SEPARATOR).each do |path|
8 exts.each do |ext|
9 exe = File.join(path, "#{cmd}#{ext}")
10 return exe if File.executable?(exe) && !File.directory?(exe)
11 end
12 end
13 nil
14end
15
16def measure
17 before = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
18 yield
19 after = Process.clock_gettime(Process::CLOCK_MONOTONIC, :nanosecond)
20 after - before
21end
22
23def report(label)
24 answer = nil
25 time = measure do
26 answer = yield
27 end
28 return :program_error unless $?.success?
29 answer = answer.chomp
30 puts "\t#{label}: #{answer} in #{(time / 10000).to_f / 100}ms"
31 return {
32 duration_ns: time,
33 answer: answer,
34 }
35end
36
37module Rust
38 def self.available?
39 which "rustc"
40 end
41
42 def self.implemented?(part)
43 File.exist? "p#{part}.rs"
44 end
45
46 def self.run(part)
47 `rustc p#{part}.rs 2> /dev/null`
48 return :rustc_error unless $?.success?
49 report "p#{part}.rs" do
50 `timeout 1m ./p#{part} < input`
51 end
52 end
53end
54
55module Haskell
56 def self.available?
57 which "ghc"
58 end
59
60 def self.implemented?(part)
61 File.exist? "p#{part}.hs" or File.exist? "solution.cabal"
62 end
63
64 def self.run(part)
65 if File.exist? "solution.cabal"
66 `cabal build p#{part} 2> /dev/null`
67 return :cabal_error unless $?.success?
68 report "solution.cabal (p#{part})" do
69 `timeout 1m cabal run p#{part} < input`
70 end
71 else
72 `ghc p#{part}.hs 2> /dev/null`
73 return :ghc_error unless $?.success?
74 report "p#{part}.hs" do
75 `timeout 1m ./p#{part} < input`
76 end
77 end
78 end
79end
80
81module Trilogy
82 def self.clang
83 return "clang" if which "clang"
84 return "clang-19" if which "clang-19"
85 nil
86 end
87
88 def self.available?
89 which "trilogy" and self.clang != nil
90 end
91
92 def self.implemented?(part)
93 File.exist? "p#{part}.tri"
94 end
95
96 def self.run(part)
97 `trilogy compile p#{part}.tri 2> /dev/null | #{self.clang} -O3 -o p#{part} -x ir -`
98 return :trilogy_error unless $?.success?
99 report "p#{part}.tri" do
100 `timeout 1m ./p#{part} < input`
101 end
102 end
103end
104
105module C
106 def self.clang
107 return "clang" if which "clang"
108 return "clang-19" if which "clang-19"
109 return "gcc" if which "gcc"
110 nil
111 end
112
113 def self.available?
114 self.clang != nil
115 end
116
117 def self.implemented?(part)
118 File.exist? "p#{part}.c"
119 end
120
121 def self.run(part)
122 `#{self.clang} -O3 -o p#{part} p#{part}.c`
123 return :clang_error unless $?.success?
124 report "p#{part}.c" do
125 `timeout 1m ./p#{part} < input`
126 end
127 end
128end
129
130module Cpp
131 def self.available?
132 which "g++"
133 end
134
135 def self.implemented?(part)
136 File.exist? "p#{part}.cpp"
137 end
138
139 def self.run(part)
140 `g++ -std=c++2c -O3 -o p#{part} p#{part}.cpp`
141 return :gcc_error unless $?.success?
142 report "p#{part}.cpp" do
143 `timeout 1m ./p#{part} < input`
144 end
145 end
146end
147
148module Swift
149 def self.available?
150 which "swiftc"
151 end
152
153 def self.implemented?(part)
154 File.exist? "p#{part}.swift"
155 end
156
157 def self.run(part)
158 `swiftc p#{part} p#{part}.swift`
159 return :swiftc_error unless $?.success?
160 report "p#{part}.swift" do
161 `timeout 1m ./p#{part} < input`
162 end
163 end
164end
165
166module Python
167 def self.python
168 return "python3" if which "python3"
169 return "python" if which "python"
170 return nil
171 end
172
173 def self.available?
174 self.python
175 end
176
177 def self.implemented?(part)
178 File.exist? "p#{part}.py"
179 end
180
181 def self.run(part)
182 report "p#{part}.py" do
183 `timeout 1m #{self.python} p#{part}.py < input`
184 end
185 end
186end
187
188module Ruby
189 def self.available?
190 which "ruby"
191 end
192
193 def self.implemented?(part)
194 File.exist? "p#{part}.rb"
195 end
196
197 def self.run(part)
198 report "p#{part}.rb" do
199 `timeout 1m ruby p#{part}.rb < input`
200 end
201 end
202end
203
204module TypeScript
205 def self.available?
206 which "deno"
207 end
208
209 def self.implemented?(part)
210 File.exist? "p#{part}.ts"
211 end
212
213 def self.run(part)
214 report "p#{part}.ts" do
215 `timeout 1m deno p#{part}.ts < input`
216 end
217 end
218end
219
220module Erlang
221 def self.available?
222 which "erl"
223 end
224
225 def self.implemented?(part)
226 File.exist? "p#{part}.erl"
227 end
228
229 def self.run(part)
230 `erl -compile p#{part}.erl`
231 return :erl_error unless $?.success?
232 report "p#{part}.erl" do
233 `timeout 1m erl -noshell -s p#{part} main -s init stop < input`
234 end
235 end
236end
237
238module Elixir
239 def self.available?
240 which "elixir"
241 end
242
243 def self.implemented?(part)
244 File.exist? "p#{part}.ex"
245 end
246
247 def self.run(part)
248 report "p#{part}.ex" do
249 `timeout 1m elixir p#{part}.ex < input`
250 end
251 end
252end
253
254module Gleam
255 def self.available?
256 which "gleam"
257 end
258
259 def self.implemented?(part)
260 File.exist? "p#{part}/gleam.toml"
261 end
262
263 def self.run(part)
264 pwd = Dir.pwd
265 Dir.chdir "p#{part}"
266 `gleam build --no-print-progress`
267 return :gleam_error unless $?.success?
268 report "p#{part}.gleam" do
269 `timeout 1m gleam run --no-print-progress < ../input`
270 end
271 Dir.chdir pwd
272 end
273end
274
275module Prolog
276 def self.available?
277 which "swipl"
278 end
279
280 def self.implemented?(part)
281 return false unless File.exist? "p#{part}.pl"
282 first_line = File.open "p#{part}.pl", &:gets
283 not first_line.include?("perl")
284 end
285
286 def self.run(part)
287 report "p#{part}.pl" do
288 `timeout 1m swipl -s p#{part}.pl -g main,halt < input`
289 end
290 end
291end
292
293module Perl
294 def self.available?
295 which "perl"
296 end
297
298 def self.implemented?(part)
299 return false unless File.exist? "p#{part}.pl"
300 first_line = File.open "p#{part}.pl", &:gets
301 first_line.include?("perl")
302 end
303
304 def self.run(part)
305 report "p#{part}.pl" do
306 `timeout 1m perl p#{part}.pl < input`
307 end
308 end
309end
310
311module Php
312 def self.available?
313 which "php"
314 end
315
316 def self.implemented?(part)
317 File.exist? "p#{part}.php"
318 end
319
320 def self.run(part)
321 report "p#{part}.php" do
322 `timeout 1m php p#{part}.php < input`
323 end
324 end
325end
326
327module Bash
328 def self.available?
329 which "bash"
330 end
331
332 def self.implemented?(part)
333 File.exist? "p#{part}.bash"
334 end
335
336 def self.run(part)
337 report "p#{part}.bash" do
338 `timeout 1m bash p#{part}.bash < input`
339 end
340 end
341end
342
343module Fish
344 def self.available?
345 which "fish"
346 end
347
348 def self.implemented?(part)
349 File.exist? "p#{part}.fish"
350 end
351
352 def self.run(part)
353 report "p#{part}.fish" do
354 `timeout 1m fish p#{part}.fish < input`
355 end
356 end
357end
358
359module Go
360 def self.available?
361 which "go"
362 end
363
364 def self.implemented?(part)
365 File.exist? "p#{part}.go"
366 end
367
368 def self.run(part)
369 `go build p#{part}.go`
370 return :go_error unless $?.success?
371 report "p#{part}.go" do
372 `timeout 1m ./p#{part} < input`
373 end
374 end
375end
376
377module Sql
378 def self.available?
379 which "psql" and `pg_isready -d postgres -U postgres -h localhost`
380 end
381
382 def self.implemented?(part)
383 File.exist? "p#{part}.sql"
384 end
385
386 def self.run(part)
387 report "p#{part}.sql" do
388 `timeout 1m psql -h localhost -U postgres -d postgres -f ./p#{part}.sql`
389 end
390 end
391end
392
393module Clojure
394 def self.available?
395 which "clojure"
396 end
397
398 def self.implemented?(part)
399 File.exist? "p#{part}.clj"
400 end
401
402 def self.run(part)
403 report "p#{part}.clj" do
404 `timeout 1m clojure -M ./p#{part}.clj < input`
405 end
406 end
407end
408
409module Kotlin
410 def self.available?
411 which "kotlinc"
412 end
413
414 def self.implemented?(part)
415 File.exist? "p#{part}.kt"
416 end
417
418 def self.run(part)
419 `kotlinc p#{part}.kt`
420 return :kotlinc_error unless $?.success?
421 report "p#{part}.kt" do
422 `timeout 1m kotlin ./P#{part}Kt.class < input`
423 end
424 end
425end
426
427module Scala
428 def self.available?
429 which "scala"
430 end
431
432 def self.implemented?(part)
433 File.exist? "p#{part}.scala"
434 end
435
436 def self.run(part)
437 `scala compile p#{part}.scala 2> /dev/null`
438 return :scala_error unless $?.success?
439 report "p#{part}.scala" do
440 `timeout 1m scala -M P#{part} p#{part}.scala < input 2> /dev/null`
441 end
442 end
443end
444
445module Crystal
446 def self.available?
447 which "crystal"
448 end
449
450 def self.implemented?(part)
451 File.exist? "p#{part}.cr"
452 end
453
454 def self.run(part)
455 `crystal build p#{part}.cr`
456 return :crystal_error unless $?.success?
457 report "p#{part}.cr" do
458 `timeout 1m ./p#{part} < input`
459 end
460 end
461end
462
463module Nim
464 def self.available?
465 which "nim"
466 end
467
468 def self.implemented?(part)
469 File.exist? "p#{part}.nim"
470 end
471
472 def self.run(part)
473 `nim compile -d:release p#{part}.nim 2> /dev/null`
474 return :nim_error unless $?.success?
475 report "p#{part}.nim" do
476 `timeout 1m ./p#{part} < input`
477 end
478 end
479end
480
481module Zig
482 def self.available?
483 which "zig"
484 end
485
486 def self.implemented?(part)
487 File.exist? "p#{part}.zig"
488 end
489
490 def self.run(part)
491 `zig build-exe p#{part}.zig`
492 return :zig_error unless $?.success?
493 report "p#{part}.zig" do
494 `timeout 1m ./p#{part} < input`
495 end
496 end
497end
498
499languages = [Haskell, Rust, Trilogy, C, Cpp, Swift, Python, Ruby, TypeScript, Erlang, Elixir, Gleam, Prolog, Php, Perl, Bash, Fish, Go, Sql, Clojure, Kotlin, Scala, Crystal, Nim, Zig]
500 .filter { |lang| lang.available? }
501
502root = Dir.pwd
503report = {}
504
505for year in `ls .`.split.filter { |x| /^\d+$/ =~ x }
506 report[year] = {}
507 for day in 1..25
508 next unless Dir.exist? "#{year}/#{day}"
509 report[year][day] = {}
510 puts "year #{year} day #{day}"
511 `just get #{day} #{year} 2> /dev/null`
512
513 Dir.chdir "#{year}/#{day}"
514 for part in ["1", "2"]
515 report[year][day]["p#{part}"] = {}
516 for lang in languages
517 next unless lang.implemented? part
518 report[year][day]["p#{part}"][lang] = lang.run part
519 end
520 end
521 Dir.chdir root
522 end
523end
524
525File.write "report.json", JSON.pretty_generate(report)