Advent of Code solutions
at main 525 lines 9.9 kB view raw
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)