diff --git a/examples/17_b.txt b/examples/17_b.txt new file mode 100644 index 0000000..e456899 --- /dev/null +++ b/examples/17_b.txt @@ -0,0 +1,5 @@ +111111111111 +999999999991 +999999999991 +999999999991 +999999999991 diff --git a/lib/days/17.rb b/lib/days/17.rb index b1cc192..80fc77b 100644 --- a/lib/days/17.rb +++ b/lib/days/17.rb @@ -2,6 +2,11 @@ class Day17 require 'fc' include Util + RIGHT = :right + LEFT = :left + DOWN = :down + UP = :up + Crucible = Struct.new( :x, :y, :cost, :dir, :moves_in_dir ) do @@ -16,7 +21,7 @@ def part1(input) goal = [grid.length - 1, grid.first.length - 1] queue = FastContainers::PriorityQueue.new(:min) - queue.push(Crucible.new(0, 0, 0, :down, 0), 0) + queue.push(Crucible.new(0, 0, 0, nil, 0), 0) visited = Set.new loop do @@ -39,11 +44,6 @@ def part1(input) end end - LEFT_TO_RIGHT = :right - RIGHT_TO_LEFT = :left - DOWN = :down - UP = :up - def available_moves(grid, position, current_cost, current_direction, num_direction) moves = [] @@ -72,34 +72,117 @@ def available_moves(grid, position, current_cost, current_direction, num_directi end if position[0] != grid.length - 1 && - current_direction != RIGHT_TO_LEFT && - !(current_direction == LEFT_TO_RIGHT && num_direction >= 3) + current_direction != LEFT && + !(current_direction == RIGHT && num_direction >= 3) moves << Crucible.new( position[0] + 1, position[1], current_cost + grid[position[0] + 1][position[1]], - LEFT_TO_RIGHT, - current_direction == LEFT_TO_RIGHT ? num_direction + 1 : 1) + RIGHT, + current_direction == RIGHT ? num_direction + 1 : 1) end if !position[0].zero? && - current_direction != LEFT_TO_RIGHT && - !(current_direction == RIGHT_TO_LEFT && num_direction >= 3) + current_direction != RIGHT && + !(current_direction == LEFT && num_direction >= 3) moves << Crucible.new( position[0] - 1, position[1], current_cost + grid[position[0] - 1][position[1]], - RIGHT_TO_LEFT, - current_direction == RIGHT_TO_LEFT ? num_direction + 1 : 1) + LEFT, + current_direction == LEFT ? num_direction + 1 : 1) end moves end - def part2(_input) - 'TODO' + def part2(input) + grid = parse_grid(input, :to_i) + goal = [grid.length - 1, grid.first.length - 1] + + queue = FastContainers::PriorityQueue.new(:min) + queue.push(Crucible.new(0, 0, 0, nil, 0), 0) + + visited = Set.new + + loop do + crucible = queue.top + queue.pop + + return crucible.cost if crucible.pos == goal && crucible.moves_in_dir >= 4 + + next_steps = available_moves_v2(grid, crucible.pos, crucible.cost, crucible.dir, crucible.moves_in_dir) + + next_steps.each do |next_step| + hash = [next_step.pos, next_step.dir, next_step.moves_in_dir] + next if visited.include?(hash) + + visited.add(hash) + queue.push(next_step, next_step.cost) + end + + break if queue.none? + end + end + + def available_moves_v2(grid, position, current_cost, current_direction, num_direction) + moves = [] + + if position[1] != grid.first.length - 1 && + current_direction != UP && + !([RIGHT, LEFT].include?(current_direction) && num_direction < 4) && + !(current_direction == DOWN && num_direction >= 10) + + moves << Crucible.new( + position[0], + position[1] + 1, + current_cost + grid[position[0]][position[1] + 1], + DOWN, + current_direction == DOWN ? num_direction + 1 : 1) + end + + if !position[1].zero? && + current_direction != DOWN && + !([RIGHT, LEFT].include?(current_direction) && num_direction < 4) && + !(current_direction == UP && num_direction >= 10) + + moves << Crucible.new( + position[0], + position[1] - 1, + current_cost + grid[position[0]][position[1] - 1], + UP, + current_direction == UP ? num_direction + 1 : 1) + end + + if position[0] != grid.length - 1 && + current_direction != LEFT && + !([UP, DOWN].include?(current_direction) && num_direction < 4) && + !(current_direction == RIGHT && num_direction >= 10) + + moves << Crucible.new( + position[0] + 1, + position[1], + current_cost + grid[position[0] + 1][position[1]], + RIGHT, + current_direction == RIGHT ? num_direction + 1 : 1) + end + + if !position[0].zero? && + current_direction != RIGHT && + !([UP, DOWN].include?(current_direction) && num_direction < 4) && + !(current_direction == LEFT && num_direction >= 10) + + moves << Crucible.new( + position[0] - 1, + position[1], + current_cost + grid[position[0] - 1][position[1]], + LEFT, + current_direction == LEFT ? num_direction + 1 : 1) + end + + moves end end diff --git a/test/17_test.rb b/test/17_test.rb index 489ca65..aa1c2cb 100644 --- a/test/17_test.rb +++ b/test/17_test.rb @@ -5,6 +5,7 @@ class TestDay17 < Minitest::Test def setup @data = File.read(File.join(APP_ROOT, 'examples', '17.txt')).rstrip + @data_b = File.read(File.join(APP_ROOT, 'examples', '17_b.txt')).rstrip @day = Day17.new end @@ -13,6 +14,7 @@ def test_part1 end def test_part2 - assert_equal @day.part2(@data), '' + assert_equal @day.part2(@data_b), 71 + assert_equal @day.part2(@data), 94 end end