changeset 32:a1b748f2c416

Implement day 23 "longest route finding" Allows for "downhill only" and "can climb" approaches. But climbing still brute-forces the map and takes too long on the final input.
author IBBoard <dev@ibboard.co.uk>
date Thu, 04 Jan 2024 11:18:56 +0000
parents 47dc75915e91
children 676461cc3a70
files day23.rb day23.txt
diffstat 2 files changed, 81 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/day23.rb	Thu Jan 04 11:18:56 2024 +0000
@@ -0,0 +1,76 @@
+#! /usr/bin/env ruby
+
+if ARGV.length != 1 and ARGV.length != 2
+    abort("Incorrect arguments - needs input file and optional second arg")
+elsif not File.exist? (ARGV[0])
+abort("File #{ARGV[0]} did not exist")
+end
+
+file = ARGV[0]
+
+lines = File.open(file, "r").each_line(chomp: true).to_a
+start_pos = [lines[0].index("."), 0]
+end_pos = [lines[-1].index("."), lines.length - 1]
+maze = lines.map {|line| line.each_char.to_a}
+
+$maze_width = 0...maze[0].length
+$maze_height = 0...maze.length
+$can_climb_hills = ARGV.length == 2
+
+open_routes = [[start_pos]]
+
+distances = Array.new(maze.length) {Array.new(maze[0].length) {Hash.new}}
+
+$moves = [[0,1], [0,-1], [1,0], [-1,0]]
+
+def valid_move(move, space, maze)
+    if $maze_width.cover?(space[0]) && $maze_height.cover?(space[1])
+        case maze[space[1]][space[0]]
+        when "#" then false
+        when "." then true
+        when "^" then $can_climb_hills || move == [0,-1]
+        when "v" then $can_climb_hills || move == [0, 1]
+        when ">" then $can_climb_hills || move == [1, 0]
+        when "<" then $can_climb_hills || move == [-1,0]
+        end
+    else
+        false
+    end
+end
+
+def next_steps(route, maze)
+    last_space = route[-1]
+    $moves.map do |move|
+        new_space = [move[0] + last_space[0], move[1] + last_space[1]]
+        [move, new_space]
+    end.filter do |move, new_space|
+        valid_move(move, new_space, maze) && !route.include?(new_space)
+    end
+end
+
+while open_routes != []
+    num_routes = open_routes.length
+    open_routes = open_routes.flat_map do |route|
+        next_steps(route, maze).map do |move, next_step|
+            new_route = route + [next_step]
+            # Simple "distance to this point" doesn't work when if can approach from different
+            # directions, because the other route may be shorter to here but longer to the end
+            # so we use the movement direction to track different directionality
+            current_distances = distances[next_step[1]][next_step[0]]
+            if !current_distances.key?(move) || new_route.length > current_distances[move]
+                distances[next_step[1]][next_step[0]][move] = new_route.length
+                new_route
+            else
+                # We have a longer route to this space in this direction, so prune
+                nil
+            end
+        end.filter {|route| !route.nil?}
+    end
+    # if open_routes.length != num_routes
+    #     puts "Now have:"
+    #     open_routes.each {|route| puts "  * #{route.length} to #{route[-1]}"}
+    # end
+end
+
+# Subtract 1 because we don't count the first step
+puts "#{distances[end_pos[1]][end_pos[0]].values.max - 1}"
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/day23.txt	Thu Jan 04 11:18:56 2024 +0000
@@ -0,0 +1,5 @@
+--- Day 23: A Long Walk ---
+
+You have a map made of blocked spaces (#), open spaces (.) and slopes (^, >, v, and <). You can't go back up slopes.
+
+What is the LONGEST route from the start (open space in the top row) to the end (open space in the bottom row).
\ No newline at end of file