Variables not matching in if/else statement - ruby

I am creating a method that makes you solve random math problems. Code is below:
def subtraction()
puts "Your goal is to solve the math problem."
# Asks if user is ready
ready()
a = rand(0..5)
b = rand(0..5)
c = a - b
puts "what is #{a} - #{b}?"
prompt; next_move = gets.chomp
if next_move == c
puts "Lucky guess!"
water()
elsif next_move != c
puts "The answer was: #{c}"
dead("You suck at life")
else
dead("You didn't type anything")
end
end
I keep trying to run this and I keep getting the elsif option. Even though my variables match when I check with puts statements. I am not moving in the direction I want to. What am I doing wrong?

Change
next_move = gets.chomp
to
next_move = gets.chomp.to_i # gets.to_i will work also.
Kernel#gets will give you string, and you need to convert it to appropriate object as per your need, if your work is not with string object like this example. As per the line c = a - b, I am very much sure, you need to change your string object, that you are getting from stdin to an integer object. So you have to use String#to_i.

Related

assigning a method result to a variable in ruby

I'm sure it would be hard to find an easier question, but I'm a complete newbie. I have searched extensively and for some reason can't find the answer to this. Here's my code:
puts "Enter F for Fahrenheit and C for Celsius."
x = gets.chomp.downcase
def ftoc(fahrenheit)
(fahrenheit.to_f - 32.0) * (5.0 / 9.0)
end
if x == "f"
puts "Enter your temp:"
temp = gets.chomp.to_i
ftoc temp
elsif x == "c"
puts "Enter your temp:"
temp = gets.chomp.to_i
ctof temp
else
puts "That does not compute."
end
I'm just trying to get the returned result of the method into a variable so I can use it elsewhere....
Remember that calls like ctof temp just initiate a method and then, as you're not putting the result anywhere, discard it immediately.
To clean up this code let's organize it better:
# Temperature conversion method
def ftoc(fahrenheit)
(fahrenheit.to_f - 32.0) * (5.0 / 9.0)
end
# User input method
def temperature_prompt!
puts "Enter F for Fahrenheit and C for Celsius."
x = gets.chomp.downcase
case (x)
when "f"
puts "Enter your temp:"
temp = gets.chomp.to_i
ftoc temp
when "c"
puts "Enter your temp:"
temp = gets.chomp.to_i
ctof temp
else
puts "That does not compute."
end
end
Now you can make use of the fact that in Ruby things like if and case actually return values. In this case it's the value of the last thing to execute in each block, so that result isn't discarded, it's just passed along:
temp = temperature_prompt!
If you enter an invalid value you get the result of puts which is conveniently nil.
Here's something to consider: Ruby is very good at parsing arbitrary text if you can describe the patterns. Here's a simple input routine:
def temperature_prompt!
puts "Enter degrees (e.g. 8F, 2C)"
case (input = gets.chomp.downcase)
when /(\d+)f/
ftoc $1
when /(\d+)c/
ctof $1
else
puts "That does not compute."
end
end
You could add to those patterns to allow things like -2C and 3.4°F if you wanted.

I'm trying to design a simple Ruby calculator and I'm getting an error

So I've been messing around with Ruby for the first time after finishing the codecademy course up to "Object Oriented Programming, Part I" and I decided to start making a calculator. For some reason though, I get this error:
calc.rb:13:in `addition': undefined local variable or method `user_input' for main:Object (NameError)
from calc.rb:21:in `<main>'
I'm confused why it doesn't see my "user_input" array. Is it out of the scope of the method? Did I initialize it wrong?
Here's the code so you can see for yourself, it's obviously nothing sophisticated and it's not finished. I'm just trying to test for addition right now.
#!/usr/bin/env ruby
user_input = Array.new
puts "Would you like to [a]dd, [s]ubtract, [m]ultiply, or [d]ivide? "
type_of_math = gets.chomp
def addition
operator = :+
puts "Please enter the numbers you want to add (enter \"=\" to stop adding numbers): "
until gets.chomp == "="
user_input << gets.chomp.to_i
end
sum = user_input.inject(operator)
return sum
end
case type_of_math
when "a"
addition
when "s"
puts "Test for subtraction"
when "m"
puts "Test for multiplication"
when "d"
puts "Test for division"
else
puts "Wrong"
end
Consider this untested variation on your code. It's more idiomatic:
def addition
user_input = []
puts 'Please enter the numbers you want to add (enter "=" to stop adding numbers): '
loop do
input = gets.chomp
break if input == '='
user_input << input
end
user_input.map(&:to_i).inject(:+)
end
Notice that it puts user_input into the method. It also uses the normal [] direct assignment of an empty array to initialize it. Rather than chomp.to_i each value as it's entered it waits to do that until after the loop exits.
Instead of while loops, consider using loop do. They tend to be more easily seen when scanning code.
Also notice there's no return at the end of the method. Ruby automatically returns the last value seen.

Ruby Character Creation for text RPG

First I just wanted to state I am very new to Ruby. I am a hug fan of Dnd and I wanted to create a text adventure game based off of Dnd rules. The issue I am having (I don't even know if it is possible) is that I am creating a character class and I want the variables assigned outside the class. The reason for this is I don't want the player to have to type:
character.new("Havatr", "elf", "tall and lean", "etc")
This is just an experiment before actually creating the file. This is what i have so far:
class Character
attr_reader :name, :race, :description
def initalize (name, race, description)
#name = name
#race = race
#description = description
end
end
def prompt
print "Enter Command >"
end
puts "What is your name?"
prompt; next_move = gets.chomp.downcase
puts "what is your race?"
prompt; next_move = gets.chomp.downcase
puts "What do you look like?"
prompt; next_move = gets.chomp.downcase
player_one = Character.new("#{a}","#{b}","#{c}")
print player_one
If there is a way to do this can I get assistance with it and if there is a better method then what I am trying please let me know. The idea behind this is to try and dump the class into a yaml file to create a character save.
When I run the code this is what it looks like:
What is your name?
Enter Command > Havatr
What is your race?
Enter Command > Elf
What do you look like?
Enter Command > I look like me
C://core_rules0.0.1/Characters.rb:27:in '': undefined local variable or method 'a' for main:Object (NameError)
There are two problems here. The first is that you misspelled initialize. The second is that instead of saving the values entered by the user in a, b, and c you save each in next_move. That's an easy fix:
puts "What is your name?"
prompt; name = gets.chomp.downcase
puts "What is your race?"
prompt; race = gets.chomp.downcase
puts "What do you look like?"
prompt; desc = gets.chomp.downcase
player_one = Character.new(name, race, desc)
print player_one
You'll notice that I did Character.new(name, race, desc) instead of Character.new("#{a}", "#{b}", "#{c}"). First, I used more descriptive names (one-character variable names are almost always a poor choice, except for well-known conventions like i to represent the iteration number in a loop). Second, I did name instead of "#{name}" because the latter doesn't actually do anything. #{...} is string interpolation in Ruby. It's a way to put values into a string, e.g. "Hello #{name}". But when you don't have anything else in the string, as in "#{name}", it doesn't do anything except convert a to a string—a task for which name.to_s is a better solution, and which is unnecessary anyway because we know name is already a string. "#{whatever}" is always unnecessary; whatever.to_s is sometimes necessary.
The other thing you'll notice is that print player_one prints something like:
#<Character:0x007fc23b88bf08>
...which maybe isn't what you expected. That's because Ruby doesn't know how to print your Character object in a human-readable way. You can tell it how by defining a to_s method. For example:
class Character
# ...
def to_s
"#{name} (#{race} - #{desc})"
end
end
For the inputs in your question, this would yield the following:
puts player_one
# => havatr (elf - i look like me)
(It's all lower-case because you called downcase on each input, which may or may not be the behavior you actually want.)
It looks like there is a simple bug here:
class Character
attr_reader :name, :race, :description
def initalize (name, race, description)
#name = name
#race = race
#description = description
end
end
def prompt
print "Enter Command >"
end
puts "What is your name?"
prompt; a = gets.chomp.downcase
puts "what is your race?"
prompt; b = gets.chomp.downcase
puts "What do you look like?"
prompt; c = gets.chomp.downcase
player_one = Character.new("#{a}","#{b}","#{c}")
print player_one
You were setting the results of the prompt to the variable next_move

Catch and throw not working in ruby

I am trying to make a number guessing game in Ruby but the program exits after I type in yes when I want to play again. I tried using the catch and throw but it would not work. Could I please get some help.
Here is my code.
class Game
def Play
catch (:start) do
$a=rand(11)
puts ($a)
until $g==$a
puts "Guess the number between 0-10."
$g=gets.to_i
if $g>$a
puts "The number you guessed is too high."
elsif $g==$a
puts "Correct you won!!!"
puts "Would you like to play again?"
$s=gets()
if $s=="yes"
$c=true
end
if $c==true
throw (:start)
end
elsif $g<$a
puts "The number you guessed is too low."
end
end
end
end
end
Game.new.Play
Edit: Here's my new code after trying suggestions:
class Game
def Play
catch (:start) do
$a=rand(11)
puts ($a)
while $s=="yes"
until $g==$a
puts "Guess the number between 0-10."
$g=gets.chomp.to_i
if $g>$a
puts "The number you guessed is too high."
elsif $g==$a
puts "Correct you won!!!"
puts "Would you like to play again?"
$s=gets.chomp
if $s=="yes"
throw (:start)
end
elsif $g<$a
puts "The number you guessed is too low."
end
end
end
end
end
end
Game.new.Play
Your first problem is here:
$s=gets()
if $s=="yes"
$c=true
end
The gets method will read the next line including the new line character '\n', and you compare it to only "yes":
> gets
=> "yes\n"
The idiomatic way to fix this in Ruby is the chomp method:
> gets.chomp
=> "yes"
That said, your code has two other deficiencies.
You may come from a language such as PHP, Perl, or even just Bash scripting, but Ruby doesn't require the dollar sign before variables. Using a $ gives a variable global scope, which is likely not what you want. In fact, you almost never want a variable to have global scope.
Ruby uses three types of symbol prefixes to indicate scope - # for instance, ## for class, and $ for global. However the most common type of variable is just local which doesn't need any prefix, and what I would suggest for your code.
I have always been told that it is very bad practice to use exceptions for control structure. Your code would be better served with a while/break structure.
When you do gets(), it retrieves the full line with a '\n' in the end. You need to trim the new line character by using:
$g=gets.chomp.to_i
Same for other gets
Based on your updated code (where you fixed the newline problem shown by others), your new problem is that you have wrapped all your game inside while $s=="true". The very first time your code is run, $s is nil (it has never been set), and so you never get to play. If you used local variables instead of global variables (s instead of $s) this would have become more obvious, because the code would not even have run.
Here's one working way that I would re-write your game.
class Game
def play
keep_playing = true
while keep_playing
answer = rand(11) # Make a new answer each time
puts answer if $DEBUG # we don't normally let the user cheat
loop do # keep going until I break from the loop
puts "Guess the number between 0-10."
guess = gets.to_i # no need for chomp here
if guess>answer
puts "The number you guessed is too high."
elsif guess<answer
puts "The number you guessed is too low."
else
puts "Correct you won!!!",
"Would you like to play again?"
keep_playing = gets.chomp.downcase=="yes"
break
end
end
end
end
end
Game.new.play
I know this doesn't really answer your question about why your code isn't working, but after seeing the code you posted I just had to refactor it. Here you go:
class Game
def initialize
#answer = rand(11)
end
def play
loop do
guess = get_guess
display_feedback guess
break if guess == #answer
end
end
def self.play_loop
loop do
Game.new.play
break unless play_again?
end
end
private
def get_guess
puts "Guess the number between 0-10."
return gets.chomp.to_i
end
def display_feedback(guess)
if guess > #answer
puts "The number you guessed is too high."
elsif guess < #answer
puts "The number you guessed is too low."
elsif guess == #answer
puts "Correct you won!!!"
end
end
def self.play_again?
puts "Would you like to play again?"
return gets.chomp == "yes"
end
end
Game.play_loop

Naming a new file with timestamp?

I'm trying to write a simple command line program that lets me keep track of how many times I get distracted during a study session.
I'm getting an argument error when I run this code saying my file.open has invalid arguments. If I want to name each new_session file with a timestamp, what would be a simple solution?
def first_question
puts "Would you like to start a new session?"
answer = gets.chomp
answer = answer.downcase
if answer == "yes"
new_session
else
puts "Ok. Would you like to review a previous session?"
prev_session = gets.chomp
prev_session.downcase
if prev_session == "yes"
#GET AND REVIEW PREVIOUS SESSIONS
elsif prev_session == "no"
puts "Well if you don't want a new session, and you don't want to review your old sessions, then you're SOL."
else
"That's not an acceptable response."
first_question
end
end
end
def new_session
distractions = 0
d = File.open("Session"+Time.now.to_s , 'w'){|f| f.write(distractions) }
puts "What would you like to do (add track(s) or review tracks)?"
request = gets.chomp
request.downcase
if request == "add track"
distractions = distractions.to_i + 1
puts "You have #{distractions} tracks in this session."
elsif request == "add tracks"
puts "How many times have you been distracted since we last met?"
answer = gets.chomp
distractions = distractions.to_i + answer.to_i
puts "You have #{distractions} tracks."
elsif request == "review tracks"
puts distractions
end
File.open( d , 'w') {|f| f.write(distractions) }
end
first_question
Most of your code is messy and redundant. The problem you are referring to, though, comes from here:
d = File.open("Session"+Time.now.to_s , 'w'){|f| f.write(distractions) }
d will be the number of bytes written to the file and thus a Fixnum. You can't open a Fixnum which you're trying to do in the last line of the function.
Further,
request = gets.chomp
request.downcase
The second line here does nothing.
You have two File.open statements:
d = File.open("Session"+Time.now.to_s , 'w'){|f| f.write(distractions) }
and:
File.open( d , 'w') {|f| f.write(distractions) }
Your error code will tell you which one is wrong, but, from looking at them, I'd say it's the second one.
d will be assigned the result of the block for the first File.open, which is going to be the result of f.write(distractions):
The File.open docs say:
The value of the block will be returned from File.open.
The File.write docs say:
Returns the number of bytes written.
As a result, you are assigning d a number of bytes, then trying to create a file with an integer for a filename, which is an error because a filename MUST be a string.
That leads to a bigger problem, which is, your code makes no sense.
d = File.open("Session"+Time.now.to_s , 'w'){|f| f.write(distractions) } writes a 0 to the file created by "Session"+Time.now.to_s.
request.downcase converts the contents of request to lowercase and immediately throws it away. Perhaps you meant request.downcase!, but it'd be better to write:
request = gets.chomp
request.downcase
As:
request = gets.chomp.downcase
distractions = distractions.to_i + 1? distractions is already 0 which is a Fixnum. You're converting a Fixnum to an integer using to_i then adding 1 to it. Simply do:
distractions += 1
distractions = distractions.to_i + answer.to_i should be:
distractions += answer.to_i
File.open( d , 'w') {|f| f.write(distractions) }, because it's trying to write to a file with the integer name, won't update your original file. If it succeeded, it'd write to an entirely new file, which would end up overwriting the previously created file, which was the result of writing a single 0 to disk. Instead, d should be the name of the file previously created.
Consider this:
def new_session
distractions = 0
puts "What would you like to do (add track(s) or review tracks)?"
request = gets.chomp.downcase
case request
when "add track"
distractions += 1
puts "You have #{distractions} tracks in this session."
when "add tracks"
puts "How many times have you been distracted since we last met?"
distractions += gets.chomp.to_i
puts "You have #{distractions} tracks."
when "review tracks"
puts distractions
end
File.write( "Session" + Time.now.to_s, distractions)
end
This code is cleaner, and makes more sense now.

Resources