IF conditions not recognising in Ruby - ruby

I'm testing my basics of Ruby by writing a simple ChingChongCha program. One of my methods takes the choice entered and turns it into a number (for easy of use for future in the program) however this if statement keeps defaulting to the 'else' condition, even though I can see clearly that if rock is entered it matches the if condition perfectly with ROCK. Any ideas?
def user_choice(choice)
# 1 is rock
# 2 is paper
# 3 is scissors
userintchoice = 0
choice.upcase!
# turns user's choice into an integer
puts choice #debugging
if (choice == 'ROCK') then
userintchoice = 1
elsif (choice == 'PAPER') then
userintchoice = 2
elsif (choice == 'SCISSORS') then
userintchoice = 3
else
puts "Invalid Choice!"
end
return userintchoice
end
Code calling this method and getting input is:
puts "What would you like to choose (input Rock, Paper or Scissors and <ENTER>)?"
userstringchoice = gets()
userchoice = user_choice(userstringchoice)

It seems you have to call .strip on userchoice because otherwise the string will contain a trailing \n.

Related

stuck on a multiple choice game on ruby

I am just starting with ruby and tried to create a multiple choice game. I can't seem to see where i get something wrong which makes it so that it either repeats the generic line for a room instead of showing the resulting option.
just for info, the options in the hall are either "north", "look" or "quit"
then in the study, options are "look", "look at desk", "south", "quit", "enter combination 2451"
code below:
def hall_begin
#first line you see
puts "you can either look around or move north"
gets.chomp
end
def look_hall
# first option to look around in the hall
puts "You are standing in a hall with a marble floor. You see a door."
hall_begin
end
def onwards_study
# second option to go forwards into the next room from the hall
puts "You are in the study, you can either look around of move back south"
gets.chomp
end
def back_to_hall
# moving back into the hall from the study
puts "You are back in the hall, either look around or go north"
gets.chomp
end
def look_study
# looking around the study to find the desk and safe
puts "You are in a warm and cosy study. You see a safe. You see a desk."
onwards_study
end
def study_desk
# looking on the study desk to find the combination
puts "You see a piece of paper that reads, The combination is 2451."
onwards_study
end
def study_safe
# if you open the safe with combination
puts "You see some diamonds in the safe, pick them up and make your escape"
end
def first_choice
# all the choices whilst in the hall
while true
direction_1 = hall_begin
if direction_1 == "look"
look_hall
elsif direction_1 == "north"
onwards_study
elsif direction_1 == "quit"
break
end
end
end
while true
# start of the game
first_choice
while true
# all choices you face whilst in the study
direction_2 = onwards_study
if direction_2 == "look"
look_study
elsif direction_2 == "south"
back_to_hall
elsif direction_2 == "look at desk"
study_desk
elsif direction_2 == "enter combination 2451"
study_safe
break
elsif direction_2 == "quit"
break
end
break
end
end
This seems to be a beginner level program. In case if you haven't studied the functions yet. This simple code can help you.
won_condition = false
breaking_condition = false
puts "You are standing in the hall, either look around or go north"
while true
if won_condition == true
break
end
if breaking_condition == true
break
end
user_input = gets.chomp
if user_input == 'quit'
break
elsif user_input == "look"
puts "You are standing in a hall with a marble floor. You see a door."
elsif user_input == 'north'
puts "You are in the study, you can either look around or move back south"
while true
study_input = gets.chomp
if study_input == 'look'
puts "You are in a warm and cosy study. You see a safe. You see a desk."
elsif study_input == 'look at desk'
puts "You see a piece of paper that reads, The combination is 2451."
elsif study_input == 'south'
puts "You are standing in the hall, either look around or go north"
break
elsif study_input == '2451'
puts "You see some diamonds in the safe, pick them up and make your escape."
won_condition = true
break
elsif study_input == 'quit'
breaking_condition = true
break
end
end
end
end
As Chandan pointed out, having this many loops is unnecessary. I think what you are after can be achieved by having one main loop that gets the user input and then handles it.
I don't want to make too complicated suggestions to begin with since you are just starting out, but I think keeping track of a "current_room" variable is helpful (which can later transition into coordinates on a 2D room array or something).
Too give you a few examples, this is how something similar could be achieved.
def describe_room(current_room)
if current_room == "hall"
puts "You are standing in the hall, either look around or go north"
elsif current_room == "study"
puts "You are in the study, you can either look around of move back south"
end
end
def examine_room(current_room)
if current_room == "hall"
puts "You are standing in a hall with a marble floor. You see a door."
elsif current_room == "study"
puts "You are in a warm and cosy study. You see a safe. You see a desk."
end
end
def move(current_room, direction)
if current_room == "hall" and direction == "north"
"study"
elsif current_room == "study" and direction == "south"
"hall"
else
puts "You cannot go that way"
current_room
end
end
def hall_commands(command)
# No hall specific commands at this points
puts "Unknown command"
# Return "hall" since we are never moving anywhere else
"hall"
end
def study_commands(command)
if command == "look at desk"
puts "You see a piece of paper that reads, The combination is 2451."
elsif command == "enter combination 2451"
puts "You see some diamonds in the safe, pick them up and make your escape"
return nil # Use explicit return statement to avoid default return at the end of the method
else
puts "Unknown command"
end
# Return "study" as the default
"study"
end
# Starting position
current_room = "hall"
while true
break if current_room == nil
# Start each loop by a brief description of the current room.
describe_room(current_room)
# Get the user input in the main loop
command = gets.chomp
# Check for global commands (i.e. movements, look, etc.) first
# and then move on to check for room specific commands.
if command.in?(["north", "east", "south", "west"])
current_room = move(current_room, command)
elsif command == "look"
examine_room(current_room)
elsif command == "quit"
break
elsif current_room == "hall"
current_room = hall_commands(command)
elsif current_room == "study"
current_room = study_commands(command)
else
puts "Unknown command"
end
end
Basically I have simplified it into one loop as mentioned earlier, then split up the "global commands" that could be used regardless which room your are in, and the "room specific commands" that only apply in certain rooms.
I hope this helps you getting into Ruby. When you feel more comfortable, I would recommend looking into case/when statements as an alternative to if/elsif/else statements and also Arrays to keep track of rooms and positions.

gets.chomp three times in a row to exit

The task is taken from "Learn to Program" by Chrise Pine. The program is called 'Deaf Grandma'. Here's the task: "whatever you type, grandma (the program) should respond with this:
`HUH?! SPEAK UP, SONNY!`
unless you shout it (type in all capitals). In this case she responds with:
`NO, NOT SINCE 1938!`
Have Grandma shout a different year each time, maybe any year at random between 1930 and 1950. You have to shout BYE three times in a row. Make sure to test your program: if you shout
BYE three times but not in a row, you should still be talking to
Grandma."
Now, everything looks fine to me, except I didn't get where to put gets.chomp 3 times to exit a program. Eventually, I came up with this:
speak = gets.chomp
while speak != 'BYE'
puts 'HUH?! SPEAK UP, SONNY!'
if speak == speak.upcase
puts 'NO, NOT SINCE ' + (1930 + rand(20)).to_s + '!'
else repeat = gets.chomp
end
end
But in this case if I type BYE grandma still asks me:
`HUH?! SPEAK UP, SONNY!`
My question is: how can I properly make the program exit after I type BYE three times in a row?
Have a look at this, I've made some changes though. But should give you the expected output.
bye_count = 0
while true
speak = gets.chomp
if speak == 'BYE'
bye_count +=1
bye_count == 3 ? break : next
end
bye_count = 0 # Resets count
if speak == speak.upcase
puts 'NO, NOT SINCE ' + (1930 + rand(20)).to_s + '!'
else
puts 'HUH?! SPEAK UP, SONNY!'
end
end
You've not added anywhere in your code that using 'bye' 3 times will exit the program.
while bye < 3
Try looking at your code again and implementing the changes to exit after 3 byes.
Here's another way:
responses = ["", "", ""]
loop do
speak = gets.chomp
responses.shift
responses << speak
break if responses.all? { |r| r == "BYE" }
if speak == speak.upcase
puts 'NO, NOT SINCE ' + (1930 + rand(20)).to_s + '!'
else
puts 'HUH?! SPEAK UP, SONNY!'
end
end
Alternatively,
break if responses.uniq == ["BYE"]
puts "what can i do for your son?"
a=0
while a != 3
n= gets.chomp
if n.include? 'BYE'
puts "NO NOT SINCE #{rand(1897..1930)}".chomp
a = (a + 1)
end
if n != n.upcase
puts 'HUH?! SPEAK UP, SONNY!'.chomp
a=0
end
if n == n.upcase and n != 'BYE'
puts "NO NOT SINCE #{rand(1897..1930)}".chomp
a=0
end
end

Rock, paper, scissors game in Ruby

I am trying to get this program to run:
cpucount = 0
playercount = 0
tiecount = 0
playerchoice =
while playerchoice != "n"
puts "Chose your Weapon. Paper (0), Rock (1), Scissors (2)"
player1 = 0 #gets
cpuplayer = 2#rand(3)
puts player1
puts cpuplayer
if player1 == 0 and cpuplayer == 1
puts "You Win"
playercount +=1
elsif player1 == 1 and cpuplayer == 2
puts "You Win!"
playercount +=1
elsif player1 == 2 and cpuplayer == 0
puts "You Win!"
playercount +=1
elsif player1 == cpuplayer
puts "You tied!"
tiecount +=1
else
puts "You lose"
cpucount +=1
end
puts cpucount
puts playercount
puts tiecount
puts "Do you want to play again? y/n?"
playerchoice = gets
puts playerchoice
end
but there are a few issues.
First, regardless of whether I select "y" to continue to another round or "n" to quit, it still runs another round.
Second, the logic is fine when I manually input the values for player1 and cpuplayer, but when I use the rand method and the user input, the program takes those and then the logic doesn't work.
Any help would be appreciated.
In your input statement which is using gets you need to take into account the newline that is placed in the string, and the fact that it is a string. When the player is inputting it, it is coming in as text, not an integer. A simple way to do this is to make it an integer on input, via
player1 = gets.to_i
That will guarantee that the conditional logic you use to test against integers is not going to fail because you are comparing a string.
The newline that is coming in with the playerchoice input needs get chomped to make that happy for comparison. So, there is another method to get rid of newlines.
playerchoice = gets.chomp
Try assigning the playerchoice variable in the following way:
playerchoice = gets.chomp
The #gets method by itself will output any carriage returns that come with the user's input, which is why if you were to inspect the returned value for playerchoice, you'd see that instead of "n", the value returned is actually "n\n", causing your comparison to resume looping the game. Calling #chomp on that value strips out the carriage return characters (\n, \r, \r\n), which should allow the game to end if "n" is typed in by the user.
Hope it helps!

Mini-game. Ruby the hard way exercise 35

I'm currently learning ruby from the Learn Ruby the hard way tutorial. And in that exercise, the author ask us to add things to a simple game. However, I was trying this to improve the bear_room method by doing something like this:
while true
print "> "
choice = gets.chomp.downcase!
if choice.include? ("taunt")
dead("The bear looks at you then slaps your face off.")
elsif choice.include? "taunt" && !bear_moved
puts "The bear has moved from the door. You can go through it now."
bear_moved = true
elsif choice.include? "taunt" && bear_moved
dead("The bear gets pissed off and chews your leg off.")
elsif choice.include? "open" && bear_moved
However, when I write this:
choice = gets.chomp.downcase!
It gives me this error when executing:
ex35.rb:44:in `bear_room': undefined method `include?' for nil:NilClass (NoMethodError)
from ex35.rb:99:in `start'
from ex35.rb:109:in `<main>'
But if do something like this:
choice = gets.chomp.downcase
or this:
choice = gets.chomp
choice.downcase!
It works. Why is that? I would appreciate any kind of help. Also, how works the while true bit? That really gets me confused.
Here is the rest of the program in case that you need it. I'm going to leave "separated" the mentioned method to make it easier to read.
# Creates the 'gold_room' method, so it can be called later.
def gold_room
puts "This room is full of gold. How much do you take?"
print "> "
choice = gets.chomp
# Converts the 'choice' variable to integer type.
choice.to_i
# Checks if 'choice' is equals to 0, OR greater or equal to 1.
if choice == '0' || choice >= '1'
# Saves the integer 'choice' variable in the 'how_much' variable.
how_much = choice.to_i
else
dead("Man, learn to type a number.")
end
# Checks if the 'how_much' variable is lesser than 50, and executes the code below if so.
if how_much < 50
puts "Nice, you're not greedy, you win!"
exit(0)
elsif how_much >= 50 && how_much < 100
puts "Mmm, ok that's enough. Get out!"
else
dead("You greedy bastard!")
end
end
################### bear_room method ###################
# Creates the 'bear_room' method.
def bear_room
puts "There is a bear here."
puts "The bear has a bunch of honey."
puts "The fat bear is in front of another door."
puts "How are you going to move the bear?"
puts "1. Taunt the bear."
puts "2. Steal the bear's honey. "
# Declares the 'bear_moved' variable as a boolean, initialize it to false.
bear_moved = false
while true
print "> "
choice = gets.chomp.downcase
if choice.include? ("taunt")
dead("The bear looks at you then slaps your face off.")
elsif choice.include? "taunt" && !bear_moved
puts "The bear has moved from the door. You can go through it now."
bear_moved = true
elsif choice.include? "taunt" && bear_moved
dead("The bear gets pissed off and chews your leg off.")
elsif choice.include? "open" && bear_moved
gold_room
else
puts "I got no idea what that means."
end
end
end
############### end of method ###############
# Defines the 'cthulhu_room' method.
def cthulhu_room
puts "Here you see the great evil Cthulhu."
puts "He, it, whatever stares at you and you go insane."
puts "Do you flee for your life or eat your head?"
print "> "
choice = gets.chomp
# Checks if the user's input contains the word 'flee'. If so, executes the code below.
if choice.include? "flee"
start
# Checks if the user's input contains the word 'head'. If so, executes the code below instead.
elsif choice.include? "head"
dead("Well that was tasty!")
else
# Otherwise, calls the 'cthulhu_room' method again.
cthulhu_room
end
end
# Defines the 'dead' method. It takes one argument (why). Example: dead("Well that was tasty!")
def dead(why)
puts why, "Nice."
# Succesfully finish the program.
exit(0)
end
# Defines the 'start' method, wich is where the game begins. Duh.
def start
puts "You are in a dark room."
puts "There is a door to your right and left."
puts "Which one do you take?"
print "> "
choice = gets.chomp
# Start the branching. It checks the user's input, and saves that on the 'choice' variable, which is used along the whole program in the other methods.
if choice == "left"
# Calls the 'bear_room' method.
bear_room
elsif choice == "right"
# Calls the 'cthulhu_room' method.
cthulhu_room
else
dead("You stumble around the room until you starve.")
end
end
# Start the game.
start
---------------------------------------------------------------------------
TL;DR: What's the difference between choice = gets.chomp.downcase! and
choice = gets.chomp
choice.downcase!
PS: The comments are part of the exercise. Please, if you have any type of correction (about the comments, how I made the question, code in general, etc) please tell me so I can improve. Thanks and sorry for the length!
-------------------------------------------------------------------------
That is because if the string did not change upon calling downcase! it returns nil. Thus when you try and call include? it says that nil does not have such a method.
Thus, it is safest to use the "non-destructive" version of downcase. The downcase! method mutates the string in place if it can.
Check the docs for further reading.

Is there a way to 'cd ..' up a nested "if" statement tree?

I'm curious if there's a way to have the program go back up the if statement stack?
Ideally, the program would return to line 2 and prompt the user for the input variable, then continue to evaluate like it did the first time. Think of it like a cursor in a text editor, I just want to move it from either of those two comments back up to line 2. The two places of interest are commented out below:
while true
input = gets.chomp
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
elsif input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "GOOD BYE!";
break
else
# return to top-level if statement
end
else
# return to top-level if statement
end
else
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
end
end
In the code you show, you don't need to do anything to make the flow of execution go back to line 2. Just omit the else clauses in the two places you marked. The flow of execution will drop down to the bottom of the while loop, then loop back to the top, then go back to line 2.
You need to use a while statement to set a condition flag and check it, which will loop back to the while statement if you don't change the flag:
flag = 0
while flag1 == 0
if var = "string"
then ...statements...
flag1 = 1 ; this allows us to break out of this while loop
else ...statements...
end
end
If flag1 is not 0 at the end of the while statement, the while statement will loop back. For two such conditions, you need to nest the while loops. You might have to re-order your statements to make multiple while loops work this way.
You can avoid this level of neasted ifs with:
byecount = 0
while byecount < 3
input = gets.chomp
if input == "BYE"
byecount += 1
next
else
byecount = 0
end
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
else
puts "NO, NOT SINCE #{rand(1930..1950)}!"
end
end
puts "GOOD BYE!"
Or you can write a catch..throw flow structure. (Really.. if you need to use it, something is wrong with your design)
catch :exitloop do
while ...
if ...
if ...
if ...
throw :exitloop
end
end
end
end
end
Here's how I'd write a similar exercise:
BYE = 'BYE'
HUH = "HUH?! SPEAK UP, SONNY!"
loop do
input = gets.chomp
if input != input.upcase
puts HUH
next
end
if input != BYE
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
next
end
puts HUH
input = gets.chomp
if input == BYE
puts HUH
input = gets.chomp
if input == BYE
puts "GOOD BYE!";
break
end
end
end
I used loop instead of while. Matz, the main man for Ruby, recommends loop. See "Is there a “do … while” loop in Ruby?" for further discussion about it.

Resources