so I'm teaching myself Ruby, and I made a simple heads or tails game. The user types in 'h' to choose heads and 't' to select tails. Under normal use, everything works fine, but unfortunately if the user types in 'th' they can win every time. How do I only reward exact string matches?
puts "~~~~~ HEADS OR TAILS ~~~~~"
print "Choose: Heads or Tails? (h,t): "
choice = gets.to_s
flip = rand(0..1)
if !choice.match('h') && !choice.match('t')
puts "oops"
elsif flip === 0
puts "The coin flipped as heads!"
puts "You chose: " + choice.to_s
if choice.match('h')
puts "YOU WIN!"
elsif !choice.match('h')
puts "YOU LOSE."
end
elsif flip === 1
puts "The coin flipped as tails"
puts "You chose: " + choice.to_s
if choice.match('t')
puts "YOU WIN!"
elsif !choice.match('t')
puts "YOU LOSE."
end
end
choice.match('t') will be truthy for any string where there is t anywhere in it. Use choice == 't'. Or, if you really want to be using regular expressions, choice.match(/\At\Z/) (match beginning, t and end of the string).
To fix your issue, you can update your code with below changes:
1. Replace match with eql? in the above code. This will perform
case-sensitive string comparisons in the program. In order to
ensure, for case-insensitive comparisons, you can use 'casecmp'
method defined in ruby.
2. Also, you can enhance your code by replacing
to_s with chomp() method it will strip off \r,\n.
Updated code is as follows:
puts "~~~~~ HEADS OR TAILS ~~~~~"
print "Choose: Heads or Tails? (h,t): "
choice = gets.chomp
flip = rand(0..1)
if !choice.eql?('h') && !choice.eql?('t')
puts "oops"
elsif flip === 0
puts "The coin flipped as heads!"
puts "You chose: " + choice
if choice.match('h')
puts "YOU WIN!"
elsif !choice.match('h')
puts "YOU LOSE."
end
elsif flip === 1
puts "The coin flipped as tails"
puts "You chose: " + choice
if choice.match('t')
puts "YOU WIN!"
elsif !choice.match('t')
puts "YOU LOSE."
end
Also, you can refer to the document "http://ruby-doc.org/core-2.2.2/Object.html#method-i-eql-3F".
Related
I tried to rewrite the "if/else statement" in the following piece of code by replacing it with a "case" statement, and I am deadly stuck with it for a few hours - what am I missing?
puts "Welcome to 'Guess My Number!'"
print "What is your name?"
input = gets
name = input.chomp
puts "Welcome, #{name.upcase!}!"
puts "I've got a random number between 1 and 100!"
puts "Can you guess it?"
target = rand(100) + 1
num_guesses = 0
guessed_it = false
until num_guesses == 10 || guessed_it
remaining_guesses = 10 - num_guesses
puts "You've got #{remaining_guesses.to_s} guesses left!"
print "Make a guess, put down a number: "
guess = gets.chomp.to_i
num_guesses = num_guesses + 1
end
puts case verification
when guess < target
then "Ooops. Your guess was LOW."
when guess > target
then "Ooops. Your guess was HIGH."
when guess < -1
then puts "Oooops. You have entered a number lower that 1!"
when guess > 100
then puts "Oooops. You have entered a number higher than 100!"
when guess =~ /^([w])/
then puts "Ooops. Looks like you have entered a non numeric
value!"
when guess == String
then puts "Oooops! Looks like you have entered a non numeric
value"
when guess == target
then puts "Good job, #{name}!"
puts "You guessed my number in #{num_guesses} guesses!"
guessed_it = true
end
unless guessed_it
puts "Sorry, you didn't get my number. My number was #{target}."
end
The "case statement" was used to replace and enhance the logic of the following if else statement:
if guess < target
puts "Ooops. Your guess was LOW."
elsif guess > target
puts "Ooops. Your guess was HIGH."
elsif guess == target
puts "Good job, #{name}!"
puts "You guessed my number in #{num_guesses} guesses!"
guessed_it = true
end
Your problem is that you're using the form of case with the optional condition, but you're using when clauses as if you were using the condition-less case.
puts case
when guess < target
"Ooops. Your guess was LOW."
should work.
Further explanation:
using case without a condition, the earliest when branch with a truthy expression is executed. This is what you want here.
But you were using case with verification. In this case, all branches are compared to verification, and the first branch where verification === branch condition is true is executed.
Since in your example I'm guessing verification is always nil, and all your branches' conditions are always true or false, no branch will ever get executed.
You can use a case statement like so:
class String
def green;"\e[32m#{self}\e[0m";end
def yellow;"\e[33m#{self}\e[0m";end
def cyan;"\e[36m#{self}\e[0m";end
def bg_blue;"\e[44m#{self}\e[0m";end
def bold;"\e[1m#{self}\e[22m";end
def underline;"\e[4m#{self}\e[24m";end
def border(num);"\n#{'-' * num}\n#{self}\n#{'-' * num}\n";end
end
puts;puts "Welcome to 'Guess My Number!'".bold.bg_blue;puts
print 'What is your name? '.green
name = gets.chomp
puts "\nWelcome, #{name.upcase!}!\n".cyan.underline
puts "I've got a random number between 1 and 100!\nCan you guess it?".border(44)
target = rand(100) + 1
num_guesses = 0
guessed_it = false
until num_guesses == 10 || guessed_it
remaining_guesses = 10 - num_guesses
puts "\nYou've got #{remaining_guesses} guesses left!\n"
puts;print 'Make a guess, put down a number: '
guess = gets.chomp
case guess.to_i
when (1...target)
puts 'Ooops. Your guess was LOW'.yellow.border(26)
when (target + 1..100)
puts 'Ooops. Your guess was HIGH'.yellow.border(26)
when target
puts; puts; puts
puts "Good job, #{name}!".bold.green
puts 'You guessed my number in ' + "#{num_guesses} guesses!".cyan
puts; puts; puts
guessed_it = true
else
puts "Oooops. You didn't enter a number from 1 to 100".yellow.border(47); puts
end
num_guesses += 1
end
unless guessed_it
puts;puts;puts "Sorry, you didn't get my number. My number was #{target}.".yellow;puts
end
Thanks a lot to everybody! With your invaluable help I managed to regain patience in my soul and satisfaction from this small task :) My mistake is that I violated the rules of common sense by trying to run several pieces of code in a wrong sequence. I moved the case statement inside the until loop and now all I have to do is correct the mistakes in particular when/then statements. It works :)
until num_guesses == 10 || guessed_it
remaining_guesses = 10 - num_guesses
puts "You've got #{remaining_guesses.to_s} guesses left!"
print "Make a guess, put down a number: "
guess = gets.chomp.to_i
num_guesses = num_guesses + 1
puts case
when guess < target
then "Ooops. Your guess was LOW."
when guess > target
then "Ooops. Your guess was HIGH."
when guess < -1
then puts "Oooops. You have entered a number lower that 1!"
when guess > 100
then puts "Oooops. You have entered a number higher than 100!"
when guess =~ /^([w])/
then puts "Ooops. Looks like you have entered a non numeric value!"
when guess == String
then puts "Oooops! Looks like you have entered a non numeric value"
when guess == target
then puts "Good job, #{name}!"
puts "You guessed my number in #{num_guesses} guesses!"
guessed_it = true
end
end
unless guessed_it
puts "Sorry, you didn't get my number. My number was #{target}."
end
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 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!
I am following tutorial and I can't figure out what I am doing wrong. It's outputting everything up to if down
puts "we are going down the cave" I can't get it to output the else statement or anything afterwards. I am just learning and the answer is probably really simple.
puts("Would you like to go up or down?")
user_input = gets()
down = "cave"
up = "mountain"
if down
puts "we are going down the cave"
else up
puts "we are going up the mountain"
puts("Pick a number between 1 and 100")
LOCATION = "cave"
NUMBER = gets()
if NUMBER == 100
puts "You've achieved enlightment in the #{LOCATION}! Spread joy around the world!"
elsif NUMBER >= 50 > 100
puts "There are #{NUMBER} goblins in the #{LOCATION}. WE MUST FIGHT!"
elsif NUMBER > 20 > 50
puts "There is still hope that we will make it to the #{LOCATION}. before the #{NUMBER} Snufflebums get us!"
else NUMBER <= 20
puts "We have conquered the Goon Squad of the #{LOCATION}.. It only took us #{NUMBER} years!!!"
end
end
down is "cave" and is always 'truthy' so if down is always, always true. You want to be testing the user_input, not the variable down
What I think you want is...
user_input = gets.chomp
# you need the chomp to remove the return character
down = "cave"
up = "mountain"
if user_input == down
puts "we are going down the cave"
elsif user_input == up
puts "we are going up the mountain"
end
# you need the end statement, otherwise everything that follows is part of the "else"
And remove the last end
puts 'guess my favorite num'
x = gets.chomp
unless x.kind_of?(Fixnum)
puts "it's not a Numeric symbol"
if x=="2"
puts "Well done!"
if x!=2 || x.is_a?(Fixnum)
puts "Try more, dude"
end
end
end
Trying to learn ruby, but my code is not work :-( Need 3 DIFFERENT conditions for var. Where is a bug ?
Consider this:
#!/usr/bin/env ruby
puts "Guess my favorite num."
x = gets.chomp
begin
if Integer(x) == 2
puts "Well done!"
else
puts "Try more, dude."
end
rescue ArgumentError
puts "It's not an integer."
end
Semi-contrived example, but you're probably looking for elsif:
puts 'enter a favorite num'
x = gets.chomp.to_i
if x == 2
puts "you entered 2"
elsif x !=2
puts "you did not enter 2"
end
Also--as #Jan Dvorak points out--the gets method returns a string, which you would want to convert (to integer in this case).
Another solution would be to use a case statement:
print 'enter a favorite num'
x = gets.chomp.to_i
case x
when 2
puts "you entered 2"
else
puts "you did not enter 2"
end
You did probably mean something like that:
loop do
puts 'guess my favorite num'
x = gets.chomp
case x
when /\D/
puts "it's not a Numeric symbol"
when "2"
puts "Well done!"
break
else
puts "Try more, dude"
end
end