|
|
@ -12,6 +12,10 @@ State = Struct.new(:floors, :elevator) do
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def ==(state)
|
|
|
|
def ==(state)
|
|
|
|
|
|
|
|
eql?(state)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def eql?(state)
|
|
|
|
elevator == state.elevator && floors.zip(state.floors).all? {|a,b| a == b }
|
|
|
|
elevator == state.elevator && floors.zip(state.floors).all? {|a,b| a == b }
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
@ -20,7 +24,8 @@ State = Struct.new(:floors, :elevator) do
|
|
|
|
next_floor = elevator + delta
|
|
|
|
next_floor = elevator + delta
|
|
|
|
next [] unless (0...floors.size).cover?(next_floor)
|
|
|
|
next [] unless (0...floors.size).cover?(next_floor)
|
|
|
|
|
|
|
|
|
|
|
|
current_floor.map {|item| move(item, elevator, next_floor) }
|
|
|
|
elevator_items = [1, 2].flat_map {|n| current_floor.to_a.combination(n).to_a }
|
|
|
|
|
|
|
|
elevator_items.map {|items| move(items, elevator, next_floor) }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
@ -28,15 +33,21 @@ State = Struct.new(:floors, :elevator) do
|
|
|
|
floors.any?(&:irradiated?)
|
|
|
|
floors.any?(&:irradiated?)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def to_s
|
|
|
|
|
|
|
|
floors.map.with_index {|floor, index|
|
|
|
|
|
|
|
|
"#{elevator == index ??E:?.} #{floor.inspect}"
|
|
|
|
|
|
|
|
}.reverse.join("\n")
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
private
|
|
|
|
private
|
|
|
|
|
|
|
|
|
|
|
|
def current_floor
|
|
|
|
def current_floor
|
|
|
|
floors[elevator]
|
|
|
|
floors[elevator]
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def move(item, from, to)
|
|
|
|
def move(items, from, to)
|
|
|
|
from_floor = floors[from] - [item]
|
|
|
|
from_floor = Floor.new(floors[from] - items)
|
|
|
|
to_floor = floors[to] + [item]
|
|
|
|
to_floor = Floor.new(floors[to] + items)
|
|
|
|
|
|
|
|
|
|
|
|
floors = self.floors.clone
|
|
|
|
floors = self.floors.clone
|
|
|
|
floors[from] = from_floor
|
|
|
|
floors[from] = from_floor
|
|
|
@ -50,7 +61,7 @@ class Floor < SimpleDelegator
|
|
|
|
attr_reader :source
|
|
|
|
attr_reader :source
|
|
|
|
|
|
|
|
|
|
|
|
def initialize(items)
|
|
|
|
def initialize(items)
|
|
|
|
@source = super(items)
|
|
|
|
@source = super(Set.new(items))
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def microchips
|
|
|
|
def microchips
|
|
|
@ -64,6 +75,44 @@ class Floor < SimpleDelegator
|
|
|
|
def irradiated?
|
|
|
|
def irradiated?
|
|
|
|
!(generators.empty? || (microchips - generators).empty?)
|
|
|
|
!(generators.empty? || (microchips - generators).empty?)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def ==(floor)
|
|
|
|
|
|
|
|
source == floor.source
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if __FILE__ == $0
|
|
|
|
|
|
|
|
INPUT = <<-INPUT
|
|
|
|
|
|
|
|
F4 . . . . .
|
|
|
|
|
|
|
|
F3 . . . LG .
|
|
|
|
|
|
|
|
F2 . HG . . .
|
|
|
|
|
|
|
|
F1 E . HM . LM
|
|
|
|
|
|
|
|
INPUT
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Step = Struct.new(:state, :count)
|
|
|
|
|
|
|
|
seen = Set.new
|
|
|
|
|
|
|
|
steps = [Step.new(State.from_s(INPUT), 0)]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
until steps.empty? do
|
|
|
|
|
|
|
|
step = steps.shift
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
puts
|
|
|
|
|
|
|
|
puts "Steps: #{step.count}"
|
|
|
|
|
|
|
|
puts step.state
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if step.state.floors[0..-2].all?(&:empty?)
|
|
|
|
|
|
|
|
exit
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
step.state.candidates.each do |candidate|
|
|
|
|
|
|
|
|
next if seen.include?(candidate)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
seen << candidate
|
|
|
|
|
|
|
|
next if candidate.irradiated?
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
steps << Step.new(candidate, step.count + 1)
|
|
|
|
|
|
|
|
end
|
|
|
|
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
require 'minitest/autorun'
|
|
|
|
require 'minitest/autorun'
|
|
|
@ -93,21 +142,28 @@ F1 E . HM . LM
|
|
|
|
def test_equality
|
|
|
|
def test_equality
|
|
|
|
state = State.from_s(INPUT)
|
|
|
|
state = State.from_s(INPUT)
|
|
|
|
assert_equal @state, state
|
|
|
|
assert_equal @state, state
|
|
|
|
|
|
|
|
assert_equal @state.hash, state.hash
|
|
|
|
|
|
|
|
assert @state.eql?(state)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def test_candidates
|
|
|
|
def test_candidates
|
|
|
|
candidates = @state.candidates
|
|
|
|
candidates = @state.candidates
|
|
|
|
assert_equal 2, candidates.size
|
|
|
|
assert_equal 3, candidates.size
|
|
|
|
|
|
|
|
|
|
|
|
candidate = candidates[0]
|
|
|
|
candidate = candidates[0]
|
|
|
|
assert_equal 1, candidate.elevator
|
|
|
|
assert_equal 1, candidate.elevator
|
|
|
|
assert_equal %W[ LM ], candidate.floors[0]
|
|
|
|
assert_equal %W[ LM ], candidate.floors[0].to_a
|
|
|
|
assert_equal %W[ HG HM ], candidate.floors[1]
|
|
|
|
assert_equal %W[ HG HM ], candidate.floors[1].to_a
|
|
|
|
|
|
|
|
|
|
|
|
candidate = candidates[1]
|
|
|
|
candidate = candidates[1]
|
|
|
|
assert_equal 1, candidate.elevator
|
|
|
|
assert_equal 1, candidate.elevator
|
|
|
|
assert_equal %W[ HM ], candidate.floors[0]
|
|
|
|
assert_equal %W[ HM ], candidate.floors[0].to_a
|
|
|
|
assert_equal %W[ HG LM ], candidate.floors[1]
|
|
|
|
assert_equal %W[ HG LM ], candidate.floors[1].to_a
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
candidate = candidates[2]
|
|
|
|
|
|
|
|
assert_equal 1, candidate.elevator
|
|
|
|
|
|
|
|
assert_empty candidate.floors[0]
|
|
|
|
|
|
|
|
assert_equal %W[ HG HM LM ], candidate.floors[1].to_a
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
def test_irradiated
|
|
|
|
def test_irradiated
|
|
|
|