patternrubyMinor
Towers of Hanoi
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
```
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:
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.)
The main loop then becomes much simpler. Also, you should structure the loop with the proper termination condition rather than
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
endThe 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
endmoves = 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.