Code for the Advent of Code event
aoc advent-of-code

Add Ruby solution for 2024 day 16

+77
+77
src/2024/16/solve.rb
··· 1 + #!/usr/bin/env ruby 2 + # frozen_string_literal: true 3 + 4 + require 'bundler/setup' 5 + require 'aoc/point' 6 + 7 + Point = AoC::Point2 8 + 9 + MAZE = ARGF.readlines.flat_map.with_index { |line, y| 10 + line.chomp.chars.map.with_index { |cell, x| 11 + [Point[x, y], cell] 12 + } 13 + }.to_h 14 + 15 + START = MAZE.find { |p, c| c == ?S }[0] 16 + GOAL = MAZE.find { |p, c| c == ?E }[0] 17 + 18 + DIRS = [ 19 + Point[1, 0], # Right 20 + Point[0, 1], # Down 21 + Point[-1, 0], # Left 22 + Point[0, -1] # Up 23 + ].freeze 24 + 25 + def display(path) 26 + $width ||= MAZE.keys.map(&:x).max + 1 27 + $height ||= MAZE.keys.map(&:y).max + 1 28 + $height.times do |y| 29 + $width.times do |x| 30 + p = Point[x, y] 31 + c = MAZE[p] 32 + if c == ?. && path.include?(p) 33 + print 'O' 34 + else 35 + print c 36 + end 37 + end 38 + puts 39 + end 40 + end 41 + 42 + def dfs2 43 + stack = [[START, Point[1, 0], Set.new, 0, [START, GOAL]]] 44 + min_cost = Float::INFINITY 45 + pos_mins = Hash.new Float::INFINITY 46 + paths = Hash.new { |h, k| h[k] = [] } 47 + 48 + until stack.empty? 49 + pos, dir, vis, cost, path = stack.pop 50 + next if cost > min_cost 51 + next if cost > pos_mins[pos] + 1000 52 + if pos == GOAL 53 + paths[cost] << path 54 + min_cost = cost if cost < min_cost 55 + next 56 + end 57 + pos_mins[pos] = cost if cost < pos_mins[pos] 58 + vis.add pos 59 + path << pos 60 + DIRS.each do |new_dir| 61 + new_pos = pos + new_dir 62 + if !vis.include?(new_pos) && MAZE[new_pos] != ?# 63 + turn_cost = new_dir == dir ? 0 : 1000 64 + stack.push [new_pos, new_dir, vis.dup, cost + turn_cost + 1, path.dup] 65 + end 66 + end 67 + end 68 + 69 + [min_cost, Set.new(paths[min_cost].flatten)] 70 + end 71 + 72 + part1, paths = dfs2 73 + 74 + # display(paths) 75 + 76 + puts part1 77 + puts paths.size