Here is the code I'm working on.
def gold_room
puts "This room is full of gold. How much do you take?"
print "> "
choice = $stdin.gets.chomp
# this line has a bug, so fix it
if choice.include?("0") || choice.include?("1")
how_much = choice.to_i
else
dead("Man, learn to type a number.")
end
if how_much < 50
puts "Nice, you're not greedy, you win!"
exit(0)
else
dead("You greedy bastard!")
end
end
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?"
bear_moved = false
while true
print "> "
choice = $stdin.gets.chomp
if choice == "take honey"
dead("The bear looks at you then slaps your face off.")
elsif choice == "taunt bear" && !bear_moved
puts "The bear has moved from the door. You can go through it now."
bear_moved == true
elsif choice == "taunt bear" && bear_moved
dead("The bear gets pissed off and chews your leg off.")
elsif (choice == "open door") && bear_moved
gold_room
else
puts "I got no idea what that means."
end
end
end
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 = $stdin.gets.chomp
if choice.include? "flee"
start
elsif choice.include? "head"
dead("Well that was tasty!")
else
cthulhu_room
end
end
def dead(why)
puts why, "Good job!"
exit(0)
end
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 = $stdin.gets.chomp
if choice == "left"
bear_room
elsif choice == "right"
cthulhu_room
else
dead("You stumble around the room until you starve.")
end
end
start
When I try to run it in my terminal on OS X, I get this:
You are in a dark room.
There is a door to your right and left.
Which one do you take?
> left
There is a bear here.
The bear has a bunch of honey.
The fat bear is in front of another door.
How are you going to move the bear?
> taunt bear
The bear has moved from the door. You can go through it now.
> open door
I got no idea what that means. # should go to gold_room, new area
>
I expected the program to take me to the gold room after typing in "open door", but instead it goes to the barf messages and restarts the while loop.
I assume I have messed up something with scope, but I did research and still don't understand why the variable wasn't manipulated, or why I'm not getting the result I expect.
Your issue may be that you wrote bear_moved == true instead of bear_moved = true.
it's bear_moved = true not bear_moved == true
elsif choice == "taunt bear" && !bear_moved
puts "The bear has moved from the door. You can go through it now."
bear_moved = true
elsif choice == "taunt bear" && bear_moved
The bear_moved variable should receive the boolean, instead comparing it with true at the 14th line of method.
I think you want to ensure that when bear is taunted and it does not move it should move bear, not check again if bear moved.
The solution should be to remove the comparison operator on elsif choice == "taunt bear" && !bear_moved change the bear_moved to = not ==
while true
print "> "
choice = $stdin.gets.chomp
if choice == "take honey"
dead("The bear looks at you then slaps your face off.")
elsif choice == "taunt bear" && !bear_moved
puts "The bear has moved from the door. You can go through it now."
bear_moved = true
elsif choice == "taunt bear" && bear_moved
dead("The bear gets pissed off and chews your leg off.")
elsif (choice == "open door") && bear_moved
gold_room
else
puts "I got no idea what that means."
end
end
Related
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.
So I tried to follow this exercise to get it to work and well, see for yourself...
def gold_room
puts "This room is full of gold. How much do you take?"
print ">"
choice = $stdin.gets.chomp
#This line has a bug, so fix it
if choice.include? ("0") || if choice.include? ("1")
how_much = choice.to_i
else
dead("Man, learn how to type a number")
end
if how_much < 50
puts "Nice, you're not greedy. YOU WIN!!!"
exit(0)
else
dead("YOU GREEDY BASTARD!!")
end
end
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 the door."
puts "How are you going to move the bear?"
bear_moved = false
while true
print ">"
choice = $stdin.gets.chomp
if choice == "take honey"
dead("The bear looks at you, then slaps your face off.")
elsif choice == "taunt the bear" && !bear_moved
puts "The bear has moved from the door. You can go through now."
bear_moved = true
elsif choice == "taunt the bear" && bear_moved
dead("The bear gets pissed off and chews your legs off!")
elsif choice == "open door" && bear_moved
gold_room
else
puts "I got no idea what that means."
end
end
end
def cthulhu_room
puts "Here you see the great evil known as 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 = $stdin.gets.chomp
if choice.include? "flee"
start
elsif choice.include? "head"
dead("Well that was tasty!")
else
cthulhu_room
end
end
def dead(why)
puts why, "Good job!"
exit(0)
end
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 = $stdin.gets.chomp
if choice == "left"
bear_room
elsif choice == "right"
cthulhu_room
else
dead("You stumble around the room until you die of hunger.")
end
end
start
and got these errors:
“ex35.rb:90: syntax error, unexpected end-of-input, expecting `end’”
“ex35.rb:90:in <main>': undefined local variable or methodstart’ for main:Object (NameError)” (added extra end)
I'm not sure what happened.
There's one if too many in your code. You only need one.
replace
if choice.include?("0") || if choice.include?("1")
with
if choice.include?("0") || choice.include?("1")
or more pretty
if %w[0 1].include?(choice)
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
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.
How does the program continue beyond ''bear_moved = false''? If the while statement only runs while true how can the program continue to run? Or is the while true unrelated to the 'bear_moved'? If so what does it relate too?
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?"
bear_moved = false
while true
print "> "
choice = $stdin.gets.chomp
if choice == "take honey"
dead("The bear looks at you then slaps your face off.")
elsif choice == "taunt bear" && !bear_moved
puts "The bear has moved from the door. You can go through it now."
bear_moved = true
elsif choice == "taunt bear" && bear_moved
dead("The bear gets annoyed and chews your leg off.")
elsif choice == "open door" && bear_moved
gold_room
else
puts "I got no idea what that means."
end
end
end
It appears to me that you want something like the following:
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?"
bear_moved = false
until bear_moved
print "> "
case gets.chomp
when "take honey"
dead("The bear looks at you then slaps your face off.")
when "taunt bear"
puts "The bear has moved from the door. You can go through it now."
bear_moved = true
when "open door"
gold_room
else
puts "I have no idea what that means."
end
end
puts "I'm outta' here!"
end
def dead(str) puts(str) end
def gold_room; puts "So this is the famous gold room. Nice."; end
Some points to consider:
since you have while true, you need a break statement to exit the loop. Alternatively, you can use until bear_moved, as I have done. If you want to use while true (or more idiomatic, loop do), just replace until bear_moved with break. I've written it differently just to show you the choice available.
you don't need an if bear_moved...else...end under when "taunt bear", because bear_moved is always false at the point.
there is no method or local variable dead or gold_room, so you need to do something about that. I've added methods for those.
I've used a case statement instead of all the if-elsif's because it is easier to read. Also, you see you don't need the variable choice when using a case statement. I've also included only the outcomes of choice, leaving the secondary choices in if-then-else's for two of the case outcomes.
(I initially had while bear_moved == false. I changed that to until bear_moved after reading #SteveTurczyn's answer. Thanks, Steve.)
while true
is another way of saying
while true == true
which of course, is always and perpetually true. You can only come out of a while true loop via an explicit break command.
What you want to do (I think) is...
until bear_moved
Meaning you will continue to loop until you've executed the bear_moved == true
I think you're failing to grasp the concept of "state", the status that things are in. Initially the bear is (implicitly, according to text) in front of the door. The state of the bear is "unmoved from its initial position", and is captured by the boolean variable bear_moved which is set to the value false. The while true will continue to repeat the contained logic indefinitely until you give a command which results in the bear moving or the bear killing you. It looks like the correct sequence of actions is to taunt the bear - if it hasn't moved, as indicated by the bear_moved state variable, it will move, but it if has already moved and you taunt it again the bear will get annoyed and chew your leg off.
Basically, the chain of logic that gets followed depends on the state, i.e., in this case the interaction between the status of the bear (moved or not) and your action choice.