I am new to programming and have just begun learning Ruby. I wrote the program below to practice about loops, variables etc. Essentially, the program mimics a laundry card machine where the user can get a new card or load value into an existing card. How it works is that it randomly selects a value for the card as either needing to buy a new card or have an existing card with an X amount of $s.
It seems to work, but where I'm having a problem is when the user tries to go back to the welcome display and then types in a value, regardless of what the user enters it just goes directly to the menu display. Ideally, it should display "INVALID INPUT" if the user enters anything but "enter". My hunch is I've written my loop incorrectly.
What am I doing wrong?
Thank you in advance for taking the time to read and help out :)
# Method that randomly generates either need for a card or existing card with balance between $1-25
def card_value
# "NO CARD"
arr = [nil, (1..25)]
value = arr.sample
if value == nil
"NO CARD"
else
rand(value)
end
end
# Variable assigned to return value of method card_value
balance = card_value
# Method that outputs a string when user input is incorrect
def invalid
puts "INVALID RESPONSE"
end
# Method for main screen, prompts user to start
def welcome
puts ""
puts "*******************************"
puts " HERCULES ADD VALUE STATION"
puts " WELCOME"
puts ""
puts " | Enter? |"
puts "*******************************"
puts " Type Enter to continue:"
puts "-------------------------------"
puts ""
start = gets.chomp
end
# Method that displays the need of a card or the card balance
def card_balance(balance)
puts ""
puts "-------------------------------"
puts ""
if balance == "NO CARD"
puts " | #{balance} |"
else
puts " | BALANCE IS $#{balance} |"
end
puts ""
puts "-------------------------------"
balance
end
# Method that displays the option screen, prompts user to get a card or add balance to card
def menu(balance)
loop do
card_balance(balance)
puts ""
puts "*******************************"
puts "| NEW CARD? | or | ADD VALUE? |"
puts "*******************************"
puts " Type '1' to buy new card"
puts " Type '2' to add value"
puts " Type 'X' to exit"
puts "-------------------------------"
puts ""
buy_add = gets.chomp
if buy_add == "1" && balance == "NO CARD"
buy_card(balance)
elsif buy_add == "2" && (balance.is_a? Integer)
add_value(balance)
elsif buy_add.upcase == "X"
welcome
else
invalid
end
end
end
# Method that displays buy new card, prompts user to add money to buy card
def buy_card(balance)
loop do
puts ""
puts "*******************************"
puts " |NEW CARD - $5|"
puts "*******************************"
puts " Type '5' to purchase card"
puts " Type 'X' to exit"
puts "-------------------------------"
puts ""
new_card = gets.chomp
if new_card == "5" && balance == "NO CARD"
puts ""
puts "*******************************"
puts " |NEW CARD PURCHASED!|"
puts "*******************************"
balance = 0
puts "BALANCE IS $#{balance}"
balance
elsif new_card == "5" && balance == 0
puts "YOU ALREADY HAVE A CARD"
elsif new_card.upcase == "X"
menu(balance)
else
invalid
end
end
end
# Method that displays the options for loading card value, prompts user for amount and displays
# the updated balance
def add_value(balance)
loop do
puts ""
puts "*******************************"
puts " |ADD VALUE|"
puts " |$15| |$35| |$50| |$75|"
puts "*******************************"
puts " Type '15' to add $15"
puts " Type '35' to add $35"
puts " Type '50' to add $50"
puts " Type '75' to add $75"
puts " Type 'X' to exit"
puts "-------------------------------"
puts ""
puts "BALANCE IS $#{balance}"
add_more = gets.chomp
if add_more == "15"
new_balance = balance += 15
elsif add_more == "35"
new_balance = balance += 35
elsif add_more == "50"
new_balance = balance += 50
elsif add_more == "75"
new_balance = balance += 75
elsif add_more.upcase == "X"
menu(balance)
else
invalid
end
puts "NEW BALANCE = #{balance}"
end
end
loop do
if welcome.downcase == "enter"
menu(balance)
else
invalid
end
end
I think where the problem lies is in the :menu(balance).. but can't seem to figure out what to do.
After entering the menu
loop, it'll stay there forever since you never break out of it.
One quick way of fixing it would be to use break
after calling the welcome
screen, that way it returns to the main loop at the end of the file:
elsif buy_add.upcase == "X"
welcome
break
For that matter, whenever you need to return to the parent/outer loop, just use break
again. I can see another two other cases in your code in buy_card
and add_value
. If you add a break
in the "X"
condition in those screens, it should fix all your stuck loops.
And if you analyze the code a bit more, you'll also find out that calling a "parent" screen and breaking after it, doesn't fix another hidden bug, which is, funnily enough, a stack overflow. If you keep entering and exiting screens for a long time, after a while it'll overflow the stack because you keep entering loops and calling new functions that create new loops and never actually finish the previous loops. To fix this once and for all, you should be able to do something like:
elsif buy_add.upcase == "X"
break
Which should also be replicated in the other "X"
conditions for other functions as well. This way, after the user inputs "X", it'll just end the function and return to the parent/caller function.
In the cases where you call menu(balance)
, of course replacing that with only a break
wouldn't update the user balance, so you need to take advantage of how functions work to actually update the balance correctly. I would suggest you always return balance
at the end of any submenu instead of using break
in those cases, and update the balance in the menu
function like balance = buy_card(balance)
. I won't give out an entire fixed piece of code for you but this should be enough hints to get you started in debugging and fixing some of the problems you're facing.