HiveBrain v1.2.0
Get Started
← Back to all entries
patternrubyMinor

Towers of Hanoi

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
towershanoistackoverflow

Problem

I am just learning how to code in Ruby now with no prior experience. This is 3 towers of Hanoi where the user selects where to choose from and where to go with each disc, and not to have a bigger disc on top of a smaller one. It works (I've played it), but I repeat my code A LOT! Is there a better way to write this without all the repeating but still making sense for a beginner?

```
tower1 = []
tower2 = []
tower3 = []

puts "How many discs would you like to start with?"
discs = gets.chomp.to_i

(1..discs).each do |disc|
tower1 << disc
end
final = tower1.reverse
tower1.reverse!
moves = 0

while true
puts "Here is how the game board looks right now:"
puts "Tower 1: #{tower1}"
puts "Tower 2: #{tower2}"
puts "Tower 3: #{tower3}"

puts "Please select what tower you want to chose from:"
select = gets.chomp.to_i
puts "Please select what tower you want to place disc:"
destination = gets.chomp.to_i

if select == 1 && destination == 1
puts "You can't put the same disc where you got it from!"
puts ""
end
if select == 1 && destination == 2
if tower2.length == 0 || tower1.last < tower2.last
tower2 << tower1.pop
moves += 1
else
puts "Invalid move. You can't place a bigger tile on top of a smaller one."
puts "Try again!"
puts ""
end
end
if select == 1 && destination == 3
if tower3.length == 0 || tower1.last < tower3.last
tower3 << tower1.pop
moves += 1
else
puts "Invalid move. You can't place a bigger tile on top of a smaller one."
puts "Try again!"
puts ""
end
end

if select == 2 && destination == 2
puts "You can't put the same disc where you got it from!"
puts ""
end
if select == 2 && destination == 1
if tower1.length == 0 || tower2.last < tower1.last
tower1 << tower2.pop
moves += 1
else
puts "Invalid move. You can't place a bigger tile on top of a smaller one."
puts "Try again!"
puts ""
end
end
if

Solution

When you represent three towers using three variables, it's hard to generalize the code. What you want is an array of three elements:

puts "How many discs would you like to start with?"
discs = gets.to_i
final = discs.downto(1).to_a
towers = [nil, final.dup, [], []]


This allows you to write a function to handle moves in general. (Note that you neglected to check whether the source tower actually contains any discs.)

def move(towers, from, to)
  if from  towers[to].last
    puts "Invalid move. You can't place a bigger tile on top of a smaller one."
    puts "Try again!\n"
  else
    towers[to] << towers[from].pop
    return true
  end
end


The main loop then becomes much simpler. Also, you should structure the loop with the proper termination condition rather than while true.

moves = 0
while towers[2] != final && towers[3] != final
  puts "Here is how the game board looks right now:"
  1.upto(3) { |t| puts "Tower #{t}: #{towers[t]}" }

  puts "Please select what tower you want to chose from:"
  select = gets.to_i
  puts "Please select what tower you want to place disc:"
  destination = gets.to_i

  if move(towers, select, destination)
    moves += 1
  end
end
puts "You win the game! You did it in #{moves} moves!"

Code Snippets

puts "How many discs would you like to start with?"
discs = gets.to_i
final = discs.downto(1).to_a
towers = [nil, final.dup, [], []]
def move(towers, from, to)
  if from <= 0 || towers[from].nil?
    puts "No such tower: #{from}\n"
  elsif to <= 0 || towers[to].nil?
    puts "No such tower: #{to}\n"
  elsif towers[from].empty?
    puts "Tower #{from} has no discs to move!\n"
  elsif from == to
    puts "You can't put the same disc where you got it from!\n"
  elsif !towers[to].empty? && towers[from].last > towers[to].last
    puts "Invalid move. You can't place a bigger tile on top of a smaller one."
    puts "Try again!\n"
  else
    towers[to] << towers[from].pop
    return true
  end
end
moves = 0
while towers[2] != final && towers[3] != final
  puts "Here is how the game board looks right now:"
  1.upto(3) { |t| puts "Tower #{t}: #{towers[t]}" }

  puts "Please select what tower you want to chose from:"
  select = gets.to_i
  puts "Please select what tower you want to place disc:"
  destination = gets.to_i

  if move(towers, select, destination)
    moves += 1
  end
end
puts "You win the game! You did it in #{moves} moves!"

Context

StackExchange Code Review Q#73400, answer score: 7

Revisions (0)

No revisions yet.