What is provoking a strange 'break' after 'gets.chomp' in Ruby? - ruby

I am following this tutorial to create a text adventure but I am having trouble with the command prompt (a mere REPL): whenever I hit enter after the gets.chomp order that retrieves the user string, the program finishes unexpectedly returning no error.
This is the code for the console, a method for the Player class:
def play
loop do
do_look
print "> "
input = gets.chomp #The program seems to BREAK here.
puts "[DBG] input = #{input}"
command(input)
puts "[DBG] Command '#{input}' finished."
end
end
I used to think that it was something with the command method (the one that gets the first word as a verb and the rest as arguments so the player can say, f.e., go north); but after marking both methods with debug tags (puts "[DBG]...") I realized my program does not even print the line above command(input), so I guess something goes wrong after gets.chomp gets the string.
Do you have any idea about what is going on?
Note: the REPL is triggered this way:
$gamer = player
$gamer.play
puts "Program finished"

Related

How can I stop Ruby if/elsif statement from looping?

I am teaching myself Ruby by making a small game in order to test out how I feel about the language. My code was going rather smoothly until I encountered a problem in which the first decision of the game loops instead of progressing forwards.
This code is what I have been using for a short part in the Exposition of my game.
def getup_or_sleep?
puts"Cole";
puts"----";
puts"Will you get up or go back to sleep?";
decision = gets
if decision == "sleep";
puts"Cole";
puts"----";
puts"You decide to go back sleep. It is far too early.";
elsif decision == "get up";
Exposition.stretch
else;
Exposition.getup_or_sleep?
end
This is the expected result I was hoping to achieve:
Cole
Will you get up or go back to sleep?
If player chooses 'sleep'
1)Cole
You decide to go back to sleep, it is far too early.
*I plan to make a new method to direct the user to, but I first want to fix this issue.
**if player chooses 'get up'
->>> to stretch method which is inside of the same class.
I'm new to coding so I may be confused on a few things. Thanks in advance! =)
Your method calls itself recursively because it all conditions fail, and it always falls back to the else branch.
This happens because gets reads input from the user and returns the input including the invisible newline character that is added when the user hits the enter key. But your conditions do not include such new line characters.
A common Ruby idiom is to call gets.chomp to get user input, where the chomp removes the newline character from the input.
Just change this line decision = gets to
decision = gets.chomp
to fix your issue.
Apart from that, your code isn't following Ruby idioms, for example, Ruby does not require a ; at the end of a line, or you usually add a whitespace between a method name and its argument, like in puts "Cole". Therefore, I suggest formatting your code like this:
def getup_or_sleep?
puts 'Cole'
puts '----'
puts 'Will you get up or go back to sleep?'
decision = gets.chomp
if decision == 'sleep'
puts 'Cole'
puts '----'
puts 'You decide to go back sleep. It is far too early.'
elsif decision == 'get up'
Exposition.stretch
else
Exposition.getup_or_sleep?
end
end
Or with a case block and some duplication extracted into a method:
def greeting
puts 'Cole'
puts '----'
end
def getup_or_sleep?
greeting
puts 'Will you get up or go back to sleep?'
case gets.chomp
when 'sleep'
greeting
puts 'You decide to go back sleep. It is far too early.'
when 'get up'
Exposition.stretch
else
Exposition.getup_or_sleep?
end
end

Online Ruby editor outputs error “undefined method `chomp'” about gets.chomp

Usually when editing with Ruby I never would have to identify .chomp but now it needs me to define it and I don’t know how to define it properly to work the same way.
Code:
print "What's you're name?"
first_name= gets.chomp
print "What's you're last name?"
last_name= gets.chomp
print "What city are you from?"
city= gets.chomp
print "want to know you're name backwards?"
yes = true
no = false
if yes
puts #{first_name.reverse}
else
puts "Ok!"
print "you're first name is" #{first_name!}
print "you're last name is" #{last_name!}
print "you're city is" #{city!}
end
Editor:
https://www.jdoodle.com/execute-ruby-online/
I believe this may be an editor issue but please correct me if I’m wrong.
I was trying to make a questionnaire that takes the input then at the end spits it back out and asks if you would like to see your name backwards but instead I got an error I did not finish the other bit where it takes the information and spits it back out so ignore that. At first it just told me that chomp was not defined when using gets.chomp to take the input
but I never have to define .chomp before – this is a first. Please explain how to fix.
The error as seen in my code’s output:
What's you're name?
jdoodle.rb:2:in `<main>': undefined method `chomp' for nil:NilClass (NoMethodError)

(Ruby) while loop stops my code from running

I'm trying to run the following code (my goal was to make a "game" where i make two objects and have them "fight" each other):
#player definition
Player=Struct.new(:health, :dmg)
active_player=Player.new(10, 2)
puts "test"
#monster definition
class Monster
attr_accessor :health, :damage
def initialize(health, damage)
#health=health
#damage=damage
end
end
big_spider=Monster.new(4, 1)
player_win=false
monster_win=false
#the fight itself
while (!player_win) or (!monster_win)
big_spider.health-=active_player.dmg
active_player.health-=big_spider.damage
if big_spider.health<=0
player_win=true
elsif active_player.health<=0
monster_win=true
end
end
#prints out who wins
case monster_win
when true
puts "the monster wins"
when false
puts "the player wins"
else
puts "error"
end
gets.chomp #is there to ensure that the program doesn't exit immediately after execution(yes i have tried removing it)
When I try to run that code (from Windows 10 PowerShell) it prints out "test" and then stops as if it was asking for input, however when I try to type anything it doesn't appear on the screen and I can't react with PowerShell in any other way than closing/minimising it (I even try pressing Ctrl+D as when exiting irb). I tried running the code in many other ways (through Notepad++, from file explorer, and through cmd.exe), yet i still ran into the same problem. When I comment out the lines from the "the fight itself" comment to the end of the code the program prints out "test",then asks for input and then stops executing. I haven't really found anyone with a similar issue anywhere.
That loop spins until BOTH win. With your current code, this is impossible. You want to change it to something like
while !player_win && !monster_win
Personally, I prefer to write such loops like this:
loop do
break if player_win
break if monster_win
...
end

Making sense of the need for as_null_object in RSpec

While working my way through The RSpec Book, I came across the as_null_object method. I don't understand the explanation the book provides as to why it's needed:
... the simplest way is to tell the double output to only listen for the messages we tell it to expect and ignore any other messages.
But why does the example code fail? When we call double('output') in each example, are we not creating a new double object for each example and sending it a single message?
What I would like is an deeper explanation (than the book) of why the example code fails, and how as_null_object addresses the issue.
it "sends a welcome message" do
output = double('output')
game = Game.new(output)
output.should_receive(:puts).with('Welcome to Codebreaker!')
game.start
end
it "prompts for the first guess" do
output = double('output')
game = Game.new(output)
output.should_receive(:puts).with('Enter guess:')
game.start
end
The book tries to explain the reason for the error in an earlier section, but again I don't understand the explanation.
We've told the double in the first example to expect puts "Welcome to Codebreaker!" and we've satisfied that requirement, but we've only told it to expect "Welcome to Codebreaker!" It doesn't know anything about "Enter guess:"
Similarly, the double in the second example expects "Enter guess:" but the first message it gets is "Welcome to Codebreaker".
When you create a double with output = double('output') and then pass it to the new game in Game.new(output), that double will receive every message that it is passed in the codebreaker game code. You haven't included it, but the start method has the following code:
module Codebreaker
class Game
...
def start
#output.puts 'Welcome to Codebreaker!'
#output.puts 'Enter guess:'
end
end
end
Here, remember that the double output has been assigned to the instance variable #output in the game's initialize method, so in each spec it is called with two messages, first with 'Welcome to Codebreaker!', then with 'Enter guess:'.
Without as_null_object, the output double will fail when it receives anything other than what it expects, i.e. in the first spec anything other than 'Welcome to Codebreaker!' and in the second spec anything other than 'Enter guess:'. By using as_null_object on the double, you tell it to sit and wait and ignore anything other than what it expects. This avoids the problem above.
Hope that helps.
I agree the explanation is not crystal clear. Here is what I understand it to be, perhaps that in combination with shioyama's answer will make this click.
When you are creating specs, you are saying the output will include the expected phrases, but not that it will include ONLY the expected phrase. So lets say you got around the error by putting all of the expectations in one example, like this:
it "gets expected output" do
output = double('output')
game = Game.new(output)
output.should_receive(:puts).with('Welcome to Codebreaker!')
output.should_receive(:puts).with('Enter guess:')
game.start
end
This will pass. The problem is, if you decide later on to have the game start by saying "Hi Joe!", it will then fail. Then you will have to go back and fix your specification when in reality, it is meeting the specification already. You need a mechanism for the output to react to unexpected input with no behavior. That way, you can have specific examples of output that are expected without them failing when something unexpected appears. This seems like very basic programming and unit test assertion, but in RSpec they implement it in this manner that might seem unclear to you and I. The phrase "null object" carries a lot of baggage, and doesn't seem to describe what it is doing, but it is an implementation of the null object pattern (http://en.wikipedia.org/wiki/Null_Object_pattern).

return command from a newbie in ruby

I'm trying out the return function for the first time. The following lines of code show no output. I'm trying to figure out what's wrong with my code. I'd appreciate your input.
def favourite_drink name
if name == "tea"
return "I love tea too!"
end
if name == "lemonade"
return "Stuff's refreshing, isn't it?"
end
if name == "coffee"
return "Dude, don't have too much of that stuff!"
end
"So what exactly is it that you like? (scratches head)"
end
favourite_drink "tea"
There's no output because you don't output the result of your function.
puts favourite_drink("tea")
outputs:
"I love tea too!"
You've probably experimented with Ruby in irb, which is a REPL -- a read-eval-print loop. In irb, if you entered your code, you'd see:
=> "I love tea too!"
because irb automatically shows you the value of whatever you type. When actually running your program, you need to specifically ask to output whatever you want printed.
I'm no Ruby wizz by far, but I think you are missing a piece of code that will actually do the output for you. You have some strings, but they remain just that: string. To actually send them to the screen you need a command like puts or print.
see: http://en.wikibooks.org/wiki/Ruby_Programming/Strings
puts 'Hello world'
To target your method, in order to display out the string "I love tea too!" to the output screen(your terminal) you need to give accurate instructions to your method. i.e, you need to instruct your method 'favourite_drink' to take the argument "tea" and paly with it according to the structure described inside the method 'favourite_drink'
puts favourite_drink "tea"
the above will solve your issue.

Resources