#!/usr/bin/env ruby require "debug" input = (ARGV.first.nil? ? DATA : ARGF) .readlines(chomp: true) .map(&:chars) @arrow_dirs = { "<" => [0, -1], "^" => [-1, 0], ">" => [0, 1], "v" => [1, 0], } @dir_arrows = @arrow_dirs.invert number_movements = { "0" => ["^", ">"], "A" => ["<", "^"], "1" => ["^", ">"], "2" => ["<", "^", ">", "v"], "3" => ["<", "^", "v"], "4" => ["^", ">", "v"], "5" => ["<", "^", ">", "v"], "6" => ["<", "^", "v"], "7" => [">", "v"], "8" => ["<", ">", "v"], "9" => ["<", "v"], } arrow_movements = { "^" => [">", "v"], "A" => ["v", "<"], "<" => [">"], "v" => ["^", ">", "<"], ">" => ["^", "<"], } number_grid = [ ["7", "8", "9"], ["4", "5", "6"], ["1", "2", "3"], ["#", "0", "A"], ] arrow_grid = [ ["#", "^", "A"], ["<", "v", ">"], ] class PriorityQueue < Array alias_method :extract_min, :shift def <<(v) super sort_by!(&:last) end end def discover(char, graph) (0...graph.size).each do |x| (0...graph.first.size).each do |y| return [x, y] if char == graph.dig(x, y) end end end def dijkstra_with_lookup(graph, movements, start_char, target_char) start = discover(start_char, graph) target = discover(target_char, graph) dijkstra(graph, movements, start, target) end def dijkstra(graph, movements, start, target) queue = PriorityQueue.new dest = Hash.new prev = Hash.new queue << [start, 0] dest[start] = 0 prev[start] = nil until queue.empty? node, moves = queue.extract_min movements[graph.dig(*node)] .map { @arrow_dirs[_1] } .map { |dx, dy| [node.first + dx, node.last + dy] } .each do |neighbor| if dest[neighbor].nil? || moves + 1 < dest[neighbor] dest[neighbor] = moves + 1 prev[neighbor] = node queue << [neighbor, moves + 1] end end end path = Array.new u = target while prev[u] path.unshift(u) u = prev[u] end return dest, path end # calculate most efficient number pad paths # actually AN efficient path; ties not included :\ number_cache = number_grid .flatten.reject { _1 == "#" || _2 == "#" } .permutation(2).to_a .map { [[_1, _2], dijkstra_with_lookup(number_grid, number_movements, _1, _2)] } .map do |(start, target), (dest, prev)| presses = ([discover(start, number_grid)] + prev) .each_cons(2).to_a .map { @dir_arrows[[_2.first - _1.first, _2.last - _1.last]] } [[start, target], presses] end .to_h # cache efficient direction movements arrow_cache = arrow_grid .flatten.reject { _1 == "#" || _2 == "#" } .permutation(2).to_a .map { [[_1, _2], dijkstra_with_lookup(arrow_grid, arrow_movements, _1, _2)] } .map do |(start, target), (dest, prev)| presses = ([discover(start, arrow_grid)] + prev) .each_cons(2).to_a .map { @dir_arrows[[_2.first - _1.first, _2.last - _1.last]] } [[start, target], presses] end .to_h arrow_cache[["<", "<"]] = ["A"] arrow_cache[["^", "^"]] = ["A"] arrow_cache[[">", ">"]] = ["A"] arrow_cache[["v", "v"]] = ["A"] arrow_cache[["A", "A"]] = ["A"] robot_2 = input .map { ["A"] + _1 } .map { _1.each_cons(2).to_a } .map { _1.map { |move| number_cache[move].join }.join("A") + "A" } robot_3 = robot_2 .map { _1.chars } .map { ["A"] + _1 } .map { _1.each_cons(2).to_a } .map { _1.map { |move| arrow_cache[move].join }.join("A") + "A" } me = robot_3 .map { _1.chars } .map { ["A"] + _1 } .map { _1.each_cons(2).to_a } .map { _1.map { |move| arrow_cache[move].join }.join("A") + "A" } pp " >^AvAA<^A>A>^AvA^A^A^A>AAvA^AA>^AAAvA<^A>A" pp me me.each_with_index do |entry, i| p "#{entry.length} * #{input[i].join.to_i} = #{entry.length * input[i].join.to_i}" end p me.map.with_index { |entry, i| entry.length * input[i].join.to_i }.sum #debugger __END__ 029A 980A 179A 456A 379A