From 3e6096cb9f0c14c62b7000ec6f141b56b177c08d Mon Sep 17 00:00:00 2001 From: Andrew Tomaka Date: Wed, 25 Dec 2024 10:52:41 -0500 Subject: [PATCH] Day twenty-one Part one --- 21/input | 5 + 21/main.rb | 337 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 342 insertions(+) create mode 100644 21/input create mode 100755 21/main.rb diff --git a/21/input b/21/input new file mode 100644 index 0000000..10276c8 --- /dev/null +++ b/21/input @@ -0,0 +1,5 @@ +140A +169A +170A +528A +340A \ No newline at end of file diff --git a/21/main.rb b/21/main.rb new file mode 100755 index 0000000..0f0aaa1 --- /dev/null +++ b/21/main.rb @@ -0,0 +1,337 @@ +#!/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 + +numeric_movements = { + "0" => ["^", ">"], + "A" => ["<", "^"], + "1" => ["^", ">"], + "2" => ["<", "^", ">", "v"], + "3" => ["<", "^", "v"], + "4" => ["^", ">", "v"], + "5" => ["<", "^", ">", "v"], + "6" => ["<", "^", "v"], + "7" => [">", "v"], + "8" => ["<", ">", "v"], + "9" => ["<", "v"], +} + +directional_movements = { + "^" => [">", "v"], + "A" => ["v", "<"], + "<" => [">"], + "v" => ["^", ">", "<"], + ">" => ["^", "<"], +} + +numeric_keypad = [ + ["7", "8", "9"], + ["4", "5", "6"], + ["1", "2", "3"], + [nil, "0", "A"], +] + +directional_keypad = [ + [nil, "^", "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_paths(prev, target, current_path = []) + return [current_path.reverse] if prev[target].nil? + + prev[target].flat_map do |parent| + dijkstra_paths(prev, parent, current_path + [target]) + 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| + new_moves = moves + 1 + if dest[neighbor].nil? || new_moves < dest[neighbor] + dest[neighbor] = new_moves + prev[neighbor] = [node] + queue << [neighbor, new_moves] + elsif new_moves == dest[neighbor] + prev[neighbor] << node + end + end + end + + paths = dijkstra_paths(prev, target) + + return dest, paths +end + +# calculate most efficient number pad paths +# actually AN efficient path; ties not included :\ +# @numeric_map = numeric_keypad +# .flatten.reject { _1.nil? } +# .permutation(2).to_a +# .map { [[_1, _2], dijkstra_with_lookup(numeric_keypad, numeric_movements, _1, _2)] } +# .map do |(start, target), (dest, paths)| +# presses = paths.map do |path| +# ([discover(start, numeric_keypad)] + path) +# .each_cons(2).to_a +# .map { @dir_arrows[[_2.first - _1.first, _2.last - _1.last]] }.join +# end +# [[start, target], presses] +# end +# .to_h + +@numeric_map = { + ["7", "8"]=>[">"], + ["7", "9"]=>[">>"], + ["7", "4"]=>["v"], + ["7", "5"]=>["v>"], + ["7", "6"]=>["v>>"], + ["7", "1"]=>["vv"], + ["7", "2"]=>["vv>"], + ["7", "3"]=>["vv>>"], + ["7", "0"]=>[">vvv", "v>vv", "vv>v"], + ["7", "A"]=>[">>vvv"], + ["8", "7"]=>["<"], + ["8", "9"]=>[">"], + ["8", "4"]=>["["v"], + ["8", "6"]=>["v>"], + ["8", "1"]=>["["vv"], + ["8", "3"]=>["vv>"], + ["8", "0"]=>["vvv"], + ["8", "A"]=>["vvv>"], + ["9", "7"]=>["<<"], + ["9", "8"]=>["<"], + ["9", "4"]=>["<["["v"], + ["9", "1"]=>["<["["vv"], + ["9", "0"]=>["["vvv"], + ["4", "7"]=>["^"], + ["4", "8"]=>["^>", ">^"], + ["4", "9"]=>["^>>", ">>^"], + ["4", "5"]=>[">"], + ["4", "6"]=>[">>"], + ["4", "1"]=>["v"], + ["4", "2"]=>["v>"], + ["4", "3"]=>["v>>"], + ["4", "0"]=>[">vv"], + ["4", "A"]=>[">>vv"], + ["5", "7"]=>["<^", "^<"], + ["5", "8"]=>["^"], + ["5", "9"]=>["^>", ">^"], + ["5", "4"]=>["<"], + ["5", "6"]=>[">"], + ["5", "1"]=>["["v"], + ["5", "3"]=>["v>"], + ["5", "0"]=>["vv"], + ["5", "A"]=>["vv>"], + ["6", "7"]=>["<<^", "^<<"], + ["6", "8"]=>["<^", "^<"], + ["6", "9"]=>["^"], + ["6", "4"]=>["<<"], + ["6", "5"]=>["<"], + ["6", "1"]=>["<["["v"], + ["6", "0"]=>["["vv"], + ["1", "7"]=>["^^"], + ["1", "8"]=>["^^>", ">^^"], + ["1", "9"]=>["^^>>", ">>^^"], + ["1", "4"]=>["^"], + ["1", "5"]=>["^>", ">^"], + ["1", "6"]=>["^>>", ">>^"], + ["1", "2"]=>[">"], + ["1", "3"]=>[">>"], + ["1", "0"]=>[">v"], + ["1", "A"]=>[">>v", ">v>"], + ["2", "7"]=>["<^^", "^^<"], + ["2", "8"]=>["^^"], + ["2", "9"]=>["^^>", ">^^"], + ["2", "4"]=>["<^", "^<"], + ["2", "5"]=>["^"], + ["2", "6"]=>["^>", ">^"], + ["2", "1"]=>["<"], + ["2", "3"]=>[">"], + ["2", "0"]=>["v"], + ["2", "A"]=>[">v", "v>"], + ["3", "7"]=>["<<^^", "^^<<"], + ["3", "8"]=>["<^^", "^^<"], + ["3", "9"]=>["^^"], + ["3", "4"]=>["<<^", "^<<"], + ["3", "5"]=>["<^", "^<"], + ["3", "6"]=>["^"], + ["3", "1"]=>["<<"], + ["3", "2"]=>["<"], + ["3", "0"]=>["["v"], + ["0", "7"]=>["^^^<"], + ["0", "8"]=>["^^^"], + ["0", "9"]=>["^^^>", ">^^^"], + ["0", "4"]=>["^^<"], + ["0", "5"]=>["^^"], + ["0", "6"]=>["^^>", ">^^"], + ["0", "1"]=>["^<"], + ["0", "2"]=>["^"], + ["0", "3"]=>["^>", ">^"], + ["0", "A"]=>[">"], + ["A", "7"]=>["^^^<<"], + ["A", "8"]=>["<^^^", "^^^<"], + ["A", "9"]=>["^^^"], + ["A", "4"]=>["^^<<"], + ["A", "5"]=>["<^^", "^^<"], + ["A", "6"]=>["^^"], + ["A", "1"]=>["^<<"], + ["A", "2"]=>["<^", "^<"], + ["A", "3"]=>["^"], + ["A", "0"]=>["<"] +} + +# cache efficient direction movements +# @directional_map = directional_keypad +# .flatten.reject { _1.nil? } +# .permutation(2).to_a +# .map { [[_1, _2], dijkstra_with_lookup(directional_keypad, directional_movements, _1, _2)] } +# .map do |(start, target), (dest, paths)| +# presses = paths.map do |path| +# ([discover(start, directional_keypad)] + path) +# .each_cons(2).to_a +# .map { @dir_arrows[[_2.first - _1.first, _2.last - _1.last]] }.join +# end +# [[start, target], presses] +# end +# .to_h + +@directional_map = { + ["^", "A"]=>[">"], + ["^", "<"]=>["v<"], + ["^", "v"]=>["v"], + ["^", ">"]=>[">v", "v>"], + ["A", "^"]=>["<"], + ["A", "<"]=>["v<<", "["v<", ""]=>["v"], + ["<", "^"]=>[">^"], + ["<", "A"]=>[">>^"], + ["<", "v"]=>[">"], + ["<", ">"]=>[">>"], + ["v", "^"]=>["^"], + ["v", "A"]=>["^>", ">^"], + ["v", "<"]=>["<"], + ["v", ">"]=>[">"], + [">", "^"]=>["^<", "<^"], + [">", "A"]=>["^"], + [">", "<"]=>["<<"], + [">", "v"]=>["<"] +} + +def solve(input, times = 3) + @robot_1 = input + @robot_2 = @robot_1 + .map { ["A"] + _1 } + .map { _1.each_cons(2).to_a } + .map { _1.map { |move| @numeric_map[move].first }.join("A") } + .map { _1 + "A" } + + (3..times).each do |num| + prev_robot = instance_variable_get(:"@robot_#{num - 1}") + + robot = prev_robot + .map { _1.chars } + .map { ["A"] + _1 } + .map { _1.each_cons(2).to_a } + .map do + _1.map do |move| + @directional_map[move].nil? ? "" : @directional_map[move].first + end.join("A") + end + .map { _1 + "A" } + instance_variable_set(:"@robot_#{num}", robot) + + @last_num = num + end + + last_robot = instance_variable_get(:"@robot_#{@last_num}") + me = last_robot + .map { _1.chars } + .map { ["A"] + _1 } + .map { _1.each_cons(2).to_a } + .map do + _1.map do |move| + @directional_map[move].nil? ? "" : @directional_map[move].first + end.join("A") + end + .map { _1 + "A" } + + me +end + +me = solve(input, 3) + +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 + +__END__ +029A +980A +179A +456A +379A