Mercurial > repos > other > adventofcode2023
changeset 37:455c825f5080
Add plotting of map in ANSI as we go
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Fri, 20 Sep 2024 20:06:21 +0100 |
parents | 7197f31970ff |
children | 8e92cb172e6b |
files | day17.rb |
diffstat | 1 files changed, 122 insertions(+), 26 deletions(-) [+] |
line wrap: on
line diff
--- a/day17.rb Thu Apr 18 19:56:23 2024 +0100 +++ b/day17.rb Fri Sep 20 20:06:21 2024 +0100 @@ -13,11 +13,15 @@ Coord = Struct.new(:x, :y) Route = Struct.new(:steps, :distance) Routes = Struct.new(:routes, :min_distance) +Colour = Struct.new(:r, :g, :b) city_map = File.open(file, "r").each_line(chomp: true).map{ |line| line.each_char.map(&:to_i)} +$height = city_map.length +$width = city_map[0].length + start_space = Coord.new(0,0) -end_space = Coord.new(city_map[0].length - 1, city_map.length - 1) +end_space = Coord.new($width - 1, $height - 1) visited = {} routes = { start_space => Routes.new([Route.new([start_space], 0)], 0)} @@ -31,10 +35,69 @@ $left = Coord.new(-1, 0) $right = Coord.new(1, 0) +# Our route is constrained to "four consecutive spaces". Zig-zag through the middle +# obeys that rule and would be a worst case score. +max_score = city_map.each_with_index.map {|row, i| i < row.length - 1 ? [row[i], row[i+1]] : [row[i]]}.flatten.reduce(:+) + +$colours = (0..max_score).map do |i| + # Use 300° because we don't want to be confused and loop back to red + angle = (i.to_f / max_score) * 300 + if angle <= 60 + r = 0xff + g = 0xff * angle / 60 + b = 0 + elsif angle <= 120 + r = 0xff * (120 - angle) / 60 + g = 0xff + b = 0 + elsif angle <= 180 + r = 0 + g = 0xff + b = 0xff * (angle - 120) / 60 + elsif angle <= 240 + r = 0 + g = 0xff * (240 - angle) / 60 + b = 0xff + else + r = 0xff * (240 - angle) / 60 + g = 0 + b = 0xff + end + Colour.new(r.to_i, g.to_i, b.to_i) +end + + def get_neighbours(city_map, x, y) [$up, $down, $left, $right].map {|coord| Coord.new(coord.x + x, coord.y + y)}.filter {|coord| coord.x >= 0 and coord.y >= 0 and coord.x < city_map[0].length and coord.y < city_map.length} end +def steps_since_turn(route) + if route.steps.length < 2 + return 1 + end +# puts "#{route}" + x_steps = 0 + y_steps = 0 + x_turned = false + y_turned = false + step_range = ..([6, route.length].max) + route.steps.reverse[step_range].each_cons(2) do |step_n, step_m| + x_diff = (step_n.x - step_m.x).abs + y_diff = (step_n.y - step_m.y).abs + if x_diff > 0 and ! x_turned + x_steps += 1 + else + x_turned = true + end + if y_diff > 0 and ! y_turned + y_steps += 1 + else + y_turned = true + end + end + (y_steps * 10) + x_steps +end + def allowed_routes(city_map, routes, cur_node, next_node) next_step_distance = city_map[next_node.y][next_node.x] candidate_routes = routes[cur_node].routes.map {|route| Route.new(route.steps + [next_node], route.distance + next_step_distance)} @@ -47,7 +110,8 @@ !(step_range.all?{|step| step.x == next_node.x} or step_range.all?{|step| step.y == next_node.y}) and !looped end end - valid_routes = valid_routes + routes[next_node].routes if !routes[next_node].nil? + valid_routes = valid_routes + routes[next_node].routes unless routes[next_node].nil? + valid_routes = valid_routes.group_by {|route| steps_since_turn(route)}.values.map {|routes| routes.sort {|a,b| a.distance <=> b.distance}[0]} Routes.new(valid_routes, valid_routes.map(&:distance).min) end @@ -59,22 +123,49 @@ end end +def update_map(new_node, map, routes) + routes_to_cell = routes[new_node] + up = $height - new_node.y + # We printed a new line, so we're already in column 0 + forward = new_node.x + if routes_to_cell + colour = $colours[routes_to_cell.min_distance] + cell = map[new_node.y][new_node.x] + #puts [cell, new_node, $height, $width, up, forward, colour] + print "\e[s" + if forward > 0 + print "\e[#{forward}C" + end + if up > 0 + print "\e[#{up}A" + end + print "\e[48;2;#{colour.r};#{colour.g};#{colour.b}m#{cell}\e[0m\e[u" + end +end + +puts "#{city_map.map {|row| row.join("")}.join("\n")}" +sleep(5) + until unvisited.empty? -# puts unvisited + #puts "Start loop: #{unvisited}" + #puts routes min_node = nil -# min_node_direct_distance = Float::INFINITY + min_node_direct_distance = Float::INFINITY unvisited.each do |node| -# x_dist = (end_space.x - node.x).abs -# y_dist = (end_space.y - node.y).abs -# direct_distance = x_dist * x_dist + y_dist * y_dist - if min_node.nil? || get_distance(routes, node) < get_distance(routes, min_node)# || direct_distance < min_node_direct_distance -# puts "#{get_distance(routes, node)} < #{get_distance(routes, min_node)} || #{direct_distance} < #{min_node_direct_distance}" + x_dist = (end_space.x - node.x).abs + y_dist = (end_space.y - node.y).abs + direct_distance = x_dist + y_dist + node_route_distance = get_distance(routes, node) + direct_distance + min_node_route_distance = get_distance(routes, min_node) + min_node_direct_distance + #puts "Node? #{min_node} - #{get_distance(routes, node)} < #{get_distance(routes, min_node)} || #{direct_distance} < #{min_node_direct_distance}" + if min_node.nil? || node_route_distance < min_node_route_distance #|| (node_route_distance == min_node_route_distance && direct_distance < min_node_direct_distance) min_node = node -# min_node_direct_distance = direct_distance + min_node_direct_distance = direct_distance end end -# puts unvisited.length - puts "Min node: #{min_node}" + #puts "Unvisited: #{unvisited.length}" + #puts "Min node: #{min_node} from #{unvisited.length}" + update_map(min_node, city_map, routes) break if get_distance(routes, min_node) == Float::INFINITY or min_node == end_space @@ -88,22 +179,27 @@ visited[min_node] = true unvisited = unvisited.delete(min_node) -# puts "Delete #{min_node}" -# a = $stdin.gets("\n") -end -=begin -city_map.length.times do |y| - city_map[0].length.times do |x| - print routes[Coord.new(x,y)].routes.map(&:distance).min.to_s.rjust(3) + " " + city_map[y][x].to_s + " |" - end - puts + #puts "Delete #{min_node}" + #a = $stdin.gets("\n") end -city_map.length.times do |y| - city_map[0].length.times do |x| - puts "#{x},#{y} ⇒ #{routes[Coord.new(x,y)].routes.length} - #{routes[Coord.new(x,y)].routes.map(&:distance).minmax}" + +best_route = routes[end_space].routes.sort {|a, b| a.distance <=> b.distance}[0] + +best_route.steps.each do |step| + cell = city_map[step.y][step.x] + up = $height - step.y + forward = step.x + print "\e[s" + if forward > 0 + print "\e[#{forward}C" end + if up > 0 + print "\e[#{up}A" + end + print "\e[1;30m\e[1;47m#{cell}\e[0m\e[u" end -=end + +#puts "#{city_map.map {|row| row.join("")}.join("\n")}" -puts routes[end_space].routes.sort {|a, b| a.distance <=> b.distance}[0] + #puts "#{routes[end_space]}" \ No newline at end of file