Solution: Farmer Crosses River Puzzle
Go over the implementation of the farmer crosses the river puzzle solution.
We'll cover the following...
Solution
Press + to interact
def is_everyone_across?@item_positions.values.all? { |x| x == :crossed }end# farmer move by himself or take another one (only one)def move(item)if @item_positions[item] == :crossed@item_positions[:farmer] = @item_positions[item] = :not_crossedelse@item_positions[:farmer] = @item_positions[item] = :crossedend@direction = @direction == :forward ? :backward : :forwardenddef undo_move(item)if @item_positions[item] == :crossed@item_positions[:farmer] = @item_positions[item] = :not_crossedelse@item_positions[:farmer] = @item_positions[item] = :crossedend@direction = @direction == :forward ? :backward : :forwardenddef is_safe?# OK if farmer is thereitems_not_crossed = @item_positions.collect{ |x, y| x if y.to_s == "not_crossed" }.compactitems_crossed = @item_positions.collect{ |x, y| x if y.to_s == "crossed" }.compact[items_crossed, items_not_crossed].each do |items_at_one_side|# false if one side wolf and sheep# false if one side sheep and cabbagereturn false if items_at_one_side.include?(:sheep) && items_at_one_side.include?(:cabbage) && !items_at_one_side.include?(:farmer)return false if items_at_one_side.include?(:sheep) && items_at_one_side.include?(:wolf) && !items_at_one_side.include?(:farmer)endreturn trueenddef was_done_before?@moving_log.values.any?{ |x|x[:farmer] == @item_positions[:farmer] &&x[:sheep] == @item_positions[:sheep] &&x[:wolf] == @item_positions[:wolf] &&x[:cabbage] == @item_positions[:cabbage]}enddef is_item_with_farmer?(item)@item_positions[item] == @item_positions[:farmer]enddef print_moving_log@moving_log.keys.sort.each do |key|action_str = "Step #{key}"item_statuses = @moving_log[key]items_not_crossed = item_statuses.collect{ |x, y| x if y.to_s == "not_crossed" }.compactitems_crossed = item_statuses.collect{ |x, y| x if y.to_s == "crossed" }.compactif key > 0prev_statuses = @moving_log[key - 1]prev_not_crossed = prev_statuses.collect{ |x, y| x if y.to_s == "not_crossed" }.compactprev_crossed = prev_statuses.collect{ |x, y| x if y.to_s == "crossed" }.compactdiff_crossed = items_crossed - prev_crosseddiff_not_crossed = items_not_crossed - prev_not_crossedif diff_not_crossed.empty?action_str += " <= #{diff_crossed}"elseaction_str += " #{diff_not_crossed} =>"endendputs action_strformat_str = " #{items_crossed.inspect} |~~~| #{items_not_crossed.inspect}"puts format_strendend@item_positions = { :farmer => :not_crossed, :wolf => :not_crossed, :sheep => :not_crossed, :cabbage => :not_crossed }@items = [:farmer, :wolf, :sheep, :cabbage]@step = 1@direction = :forward@moving_log = { 0 => @item_positions.dup } # moving log starts with all in one sidedef crossif is_everyone_across?print_moving_logputs "Done!"return # exit out of recursionend@items.each do |item|next unless is_item_with_farmer?(item)# next if item == :farmer && @direction == :forwardmove(item)if is_safe? && !was_done_before?@moving_log[@step] = @item_positions.dup@step += 1cross(); # next step, recursiveelseundo_move(item); # "not safe, revert"endendendcross()
Explanation
Lines 8, 10, 18, and 20: The array locations are changed to reflect updated positions.
Line 30: The
collect
method returns the subarray of all keys in the hash that have ...