Ruby - Secret number game, how to get the game to restart? - ruby

im trying to get my secret number game to work properly.
I'm having issues with getting the game to restart when:
you have no more guesses left
you win the game
I used a while loop, but have troubles accessing it via a function/method..
edit:
so basically players are given 3 attempts to guess the secret number. if they win, they will be asked if they want to restart the game. if they lose, they will also be asked if they want to retry.
here is the code:
thanks in advance guys
def get_input
gets.chomp
end
puts "Welcome to the Secret Number Game! Please tell me your name"
player_name = get_input
puts "Welcome #{player_name}!"
puts "Guess a number between 1 - 10. You only have 3 attempts!"
secret_number = 1 + rand(10)
restart = true
while restart
def playAgain ( restart )
puts "Would you like to play again? (y/n)"
answer = get_input
if answer == "n"
restart = false
end
end
def guess_check ( player_input, secret_number )
if player_input > secret_number
puts "Wrong, sorry! Too high!"
elsif player_input < secret_number
puts "Wrong, sorry! Too low!"
else
puts "Congratulations, you've guessed the secret number! #{[secret_number]}"
playAgain ( restart )
end
end
############################## START GAME ###########################################
guesses = []
attempts = 3
while attempts
attempts = attempts - 1
if attempts == -1
puts "Sorry, you have no more tries"
playAgain ( restart )
else
puts "Guess the secret number (You have #{attempts} tries left):"
end
player_input = get_input.to_i
guesses.push ( player_input )
guess_check( player_input, secret_number )
puts "You've guessed - #{guesses}"
end
playAgain ( restart )
end

0 in ruby is truthy, not falsey, contradictory to most languages. To break a while loop one should explicitly check it’s greater than zero:
- while attempts
+ while attempts > 0
or, more rubyish:
3.downto(0) do |attempts|
...
end
UPD
The portion with restarts. You get local restart variable defined in playAgain. Google for ruby scopes, in short: local variables in functions are not visible out of scope of this function; parameters are passed to the function by value. That said, defining restart = false inside playAgain makes no sence at all, since that local variables dies within the function scope. Possible solution would be to declare an instance variable #restart. In this case you won’t need to pass it as parameter. But most reasonable way to get things done would be to return a boolean value from playAgain:
def playAgain
puts "Would you like to play again? (y/n)"
answer = get_input
answer != "n" # return a boolean from the function in last statement
end
And then the whole scope would be looking like:
def playAgain
puts "Would you like to play again? (y/n)"
answer = get_input
answer != "n" # return a boolean from the function in last statement
end
def guess_check ( player_input, secret_number )
if player_input > secret_number
puts "Wrong, sorry! Too high!"
elsif player_input < secret_number
puts "Wrong, sorry! Too low!"
else
puts "Congratulations! Result: #{secret_number}"
end
player_input == secret_number # return true on success
end
# let’s go!
loop do # main loop
guesses = []
3.downto(1) |attempts| # 3 attemts
puts "Guess the secret number (You have #{attempts} tries left):"
player_input = get_input.to_i
guesses.push(player_input)
# you have to return true/false from guess check
# and rerun the topmost loop if success
# see the trick with logical and below:
# on normal execution the loop will return it’s initial
# (3 in this particular case)
# on successful guess is will break, returning false
break false if guess_check(player_input, secret_number)
puts "You've guessed - #{guesses}"
end && puts "Sorry, you have no more tries"
break unless playAgain # will break a topmost loop unless `y` input
end

Related

Handling Ruby Case Statement

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

How to restart script from the beginning?

I decided to make a number-guessing game. Here is the code.
print "Guess a number from 1-20. You have 5 guesses!"
guess1=gets.chomp
guess1=guess1.to_i
random=1 + rand(20)
random=random.to_i
if guess1 == random
puts "Correct! You win!!!"
sleep(5)
Kernel.exit
elsif guess1 > random
puts "Wrong! Too high. Try again!"
else
puts "Wrong! Too low. Try again!"
end
guess2=gets.chomp
guess2=guess2.to_i
if guess2 == random
puts "Correct! You win!!!"
sleep(5)
Kernel.exit
elsif guess2 > random
puts "Wrong! Too high. Try again!"
else
puts "Wrong! Too low. Try again!"
end
guess3=gets.chomp
guess3=guess3.to_i
if guess3 == random
puts "Correct! You win!!!"
sleep(5)
Kernel.exit
elsif guess3 > random
puts "Wrong! Too high. Try again!"
else
puts "Wrong! Too low. Try again!"
end
guess4=gets.chomp
guess4=guess4.to_i
if guess4 == random
puts "Correct! You win!!!"
sleep(5)
Kernel.exit
elsif guess4 > random
puts "Wrong! Too high. Try again!"
else
puts "Wrong! Too low. Try again!"
end
guess5=gets.chomp
guess5=guess5.to_i
if guess5 == random
puts "Correct! You win!!!"
sleep(5)
Kernel.exit
elsif guess5 > random
puts "Wrong! Too high. Game over!"
sleep(5)
Kernel.exit
else
puts "Wrong! Too low. Game over!"
sleep(5)
Kernel.exit
end
How would I add a try-again option at the end that would restart the game?
As Sergio said, a very clean solution would be to learn about functions, and divide up your game logic, so that you can call the function(s) when needed.
Another solution would be to use loops, specifically a do...while loop.
Side note: The begin...while style of looping in the tutorial link I posted is not recommended by Matz, the creator of Ruby. He recommends the loop do...break end style, which is what I will demonstrate.
I'll let you read the tutorials to learn more about whats going on, but the gist is that this particular style of loop will run your code once, and it will either loop back and run it again, or exit the loop and the program will end, depending on the result of our "control" variable.
Unfortunately, because of the way you've written your code, wrapping the program in a simple do...while is very awkward because of the Kernel.exit lines, and the fact that you've hard coded 5 guesses which are run in sequence. Since my answer involves loops, I'll quickly show you a good way to refactor the code without too much pain. Please note the comments in the code to understand what's going on.
loop do # The start of the main game loop
random=1 + rand(20)
random=random.to_i
guess_count = 0 # Tracks the number of times the user has guessed
print "Guess a number from 1-20. You have 5 guesses!"
while guess_count < 5 # The guess loop; keep looping up to a max of 5 times
guess=gets.chomp
guess=guess.to_i
if guess == random
puts "Correct! You win!!!"
sleep(5) # 5 seconds is a very long time, I would reduce this to 1 at most
break # This causes the 'guess loop' to end early
elsif guess > random
puts "Wrong! Too high. Try again!"
else
puts "Wrong! Too low. Try again!"
end
guess_count+= 1 # increment the guess count if the user guessed incorrectly
end
puts "Would you like to play again? (y/n)"
play_again = gets.chomp
break if play_again != "y" # exit the main loop if the user typed anything except "y"
end # The end of the main loop, and thus the entire program
# No need for Kernal.exit. The program is done at this point.
Note: I removed your comments so they don't interfere with the explanation. Feel free to put them back in your version.
Take a look at loops in the tutorials for more details. Hope thats clear.
What you want to do is make the main thread of your game a loop:
loop do
# Prompt
end
This will execute endlessly whatever is inside it until an exception is raised or the program is interrupted.
Now, to make it effective, you'll want to start wrapping up your game logic in an Object.
For instance
class GuessingGame
def initialize
#secret = 1 + rand(20)
#guesses_remaining = 5
end
def is_it?(number)
#guesses_remaining -+ 1
#secret == number
end
end
Now, when you start the game look, you just make GuessingGame.new, and after the game is over, by running out of guesses, or getting it right, you can just prompt to retry and make a new guessing game
First, extract the repeated part into a method:
def correctly_guessed?(target)
guess = gets.to_i
if guess == target then puts "Correct! You win!!!"; sleep(5)
elsif guess > target then puts "Wrong! Too high. Try again!"
else puts "Wrong! Too low. Try again!"
end
end
Then, restructure your code:
loop do
print "Guess a number from 1-20. You have 5 guesses!"
target = 1 + rand(20)
5.times{break if correctly_guessed?(target)}
print "Restart the game? (Y to restart)"
break unless gets.chomp == "Y"
end

Ruby - loop not breaking

I'm trying to create a secret number game where:
A number between 1 - 10 is selected at random
Player has 3 tries to guess the secret number
If player guesses wrong in all tries, or guesses the right number, they will be prompted if they want to replay the game.
My issues are:
the "attempt" loop won't break when playerInput == secret_number. It will only break when all 3 tries are used.
Can't get to print the message "Sorry, but you have no more tries" when all 3 guesses are used up.
I think my math is off too... with the attempts
The code is below. Thanks in advance guys!
puts "Welcome to the Secret Number Game! Please tell me your name"
player_name = gets.strip
puts "Welcome #{player_name}!"
puts "Guess a number between 1 - 10. You only have 3 attempts!"
restart = true
def guess_check( playerInput, secret_number, attempts )
if playerInput > secret_number
puts "Too high! try again!"
elsif playerInput < secret_number
puts "Too low! try again!"
elsif
playerInput == secret_number
puts "Congratulations, you guessed the secret number! [#{secret_number}]"
elsif
attempts == 0
puts "Sorry, you're out of guesses, the secret number is [#{secret_number}]"
else
puts secret_number
end
end
while restart
guesses = []
attempts = 3
secret_number = 1 + rand(10)
while attempts
attempts = attempts - 1
puts "Guess the secret number, you have #{attempts} tries left"
playerInput = gets.to_i
guesses.push( playerInput )
guess_check( playerInput, secret_number, attempts )
puts "You've guessed #{guesses}"
break if playerInput == secret_number || break if attempts == 0
end
puts "Do you want to play again? (y/n)"
answer = gets.strip
restart = false if answer == "n"
end
This isn't working like you intend it to:
break if playerInput == secret_number || break if attempts == 0
If you break it out, it reads like this:
if attempts == 0
if playerInput == secret_number or break
break
end
end
It only gets to a break if attempts == 0, and only then either because the first conditional in the nested if statement failed OR it passed. This should make it work like you intend it to:
break if playerInput == secret_number || attempts == 0
It doesn't print your used up guesses message because the conditionals for either the right or wrong answer were executed first. To fix that, your conditional needs to be in the following order:
def guess_check( playerInput, secret_number, attempts )
if playerInput == secret_number
puts "Congratulations, you guessed the secret number! [#{secret_number}]"
if attempts == 0
puts "Sorry, you're out of guesses, the secret number is [#{secret_number}]"
elsif playerInput > secret_number
puts "Too high! try again!"
elsif playerInput < secret_number
puts "Too low! try again!"
#else <= this can actually be removed, the if statement will never get this far
#puts secret_number
end
end
I'm sure there's a better way to phrase it, but you want your least likely/highest priority if statement assessed first. In this case, the user's guess is guaranteed to at least be greater or less than the secret number, so those get assessed first, and it never really gets down to the end. You want to assess correct guess (1/10), then out of guesses (3/10), then greater than (1/2)/less than(1/2). The else will never trigger because all possibilities are covered by the above.
Just a simple change here:
break if playerInput == secret_number || attempts == 0
Your || condition was valid syntax but causing the unwanted behavior.
I refactor your code.
puts "Welcome to the Secret Number Game! Please tell me your name"
player_name = gets.strip
puts "Welcome #{player_name}!"
restart = true
def guess_check( playerInput, secret_number)
if playerInput > secret_number
puts "Too high! try again!"
elsif playerInput < secret_number
puts "Too low! try again!"
end
end
while restart
guesses = []
attempts = 3
secret_number = 1 + rand(10)
puts "Guess a number between 1 - 10. You only have 3 attempts!"
while attempts
playerInput = gets.to_i
guesses.push( playerInput )
# once guessed, should exit current loop.
if playerInput == secret_number
puts "Congratulations, you guessed the secret number! [#{secret_number}]"
break;
end
puts "You've guessed #{guesses}"
#if not guessed, then should check the number is low or high.
guess_check(playerInput, secret_number)
attempts = attempts - 1
#if out of guesses, exit from current loop
if attempts == 0
puts "Sorry, you're out of guesses, the secret number is [#{secret_number}]"
break;
else
puts "Guess the secret number, you have #{attempts} tries left"
end
end
puts "Do you want to play again? (y/n)"
answer = gets.strip
restart = false if answer == "n"
end

Ruby script need fix

I'm having a problem with my ruby script. If anyone could help, I'd really appreciate it. The problem is that the number is stuck between 1-2; where 2 is too high and 1 is too low. The guesses should be integers only.
#!/usr/bin/ruby
def highLow(max)
again = "yes"
while again == "yes"
puts "Welcome to the High Low game"
playGame(max)
print "Would you like to play again? (yes/no): "
again = STDIN.gets.chomp
if again == 'no'
puts "Have a nice day, Goodbye"
end
end
end
#This method contains the logic for a single game and call the feedback method.
def playGame(max)
puts "The game gets played now"
puts "I am thinking of a number between 1 and #{max}." #It show what chosen by user
randomNumber = rand(max)+ 1
print "Make your guess: "
guess = STDIN.gets.chomp
feedback(guess, randomNumber)
end
#Start while loop
#Logic for feedback method. It's ganna check the guess if it's high or low.
def feedback(guess, randomNumber)
count = 1
while guess.to_i != randomNumber
count = count + 1
if guess.to_i < randomNumber
print "That's too low. Guess again: "
else
print "That's too high. Guess again: "
end
guess = STDIN.gets.chomp
end
puts "Correct! You guessed the answer in #{count} tries!"
end
highLow(ARGV[0])
Change your last line to this:
highLow(ARGV[0].to_i)
The ARGV array contains all the passed in arguments as strings, so you have to cast it to integer.

Guessing Game Ruby

So i have been learning ruby as of late and i am working on this code for practice purposes but i cannot seems to be able to solve this problem any help would be appreciate it.
This is are the guidelines i am following:
clear the screen
greet the player
explain the rules of the game to the player
generate a random number between 0 and x (x being a variable that can be set to any integer)
allow the player n number of guesses (n being a variable)
keep track of the guess history
don't count repeated guesses against the player
congratulate the player when he/she guesses correctly
alert the player when there is only one guess remaining
print the guess history at the end of the game
count number of games won IN A ROW
count number of games won in total
congratulate the play on 3 games won IN A ROW
ask if the player wants to play again
thank the player for playing games if the number of games played is greater than 2
please keep in my that this is work in progress and i have not completed all the guideline, however my questions is with one particular part of it.
here is the code:
guess = Array.new
puts guess.class
def ask()
puts "Please answer in a 'y' or 'n' only!"
puts "Would like to play again?"
end
def guess_check_against()
g = guess.last
unless guess.include?(g) != guess
count+=1
else
puts "sorry you have guessed that number before, Guess Again: "
guess << gets.to_i
count+=1
end
end
puts "what is your name?"
user= gets.chomp
puts "Hello #{user}!!"
max_user_attempts = 4
#attempt_counter = 0
directions = "\nLets play a guessing game! You have
#{max_user_attempts.to_s} guesses before you lose."
print directions
g = guess.last
win = 0
count = 0
play = true
while play == true
puts "Please tell me the max value of the random number: "
max= gets.to_i
num= rand(max)
puts "Ok. The random number is generated between 1 and " + max.to_s + "."
puts "Make your guess: "
guess << gets.to_i
guess_check_against()
#attempt_counter+=1
while guess != num && play != false
if g > num && #attempt_counter < max_user_attempts
print "That's too high. Guess again: "
guess << gets.to_i
guess_check_against()
#attempt_counter+=1
elsif g < num && #attempt_counter < max_user_attempts
print "That's too low. Guess again: "
guess << gets.to_i
guess_check_against()
count+=1
#attempt_counter+=1
else
break
end
end
if #attempts_counter >= max_user_attemtps
puts "Sorry! you lost! Try Again"
break
else #attempts_counter <= max_user_attempts
puts "Correct! You guessed the answer in " + count.to_s + " tries!"
win+=1
end
if win >= 3
puts "Congratulation! you have #{win} number of games in a row"
ask()
answer = gets.chomp!
elsif win < 3
ask()
answer = gets.chomp!
else
break
end
if answer == 'n'
play = false
break
end
if answer == 'y'
play = true
count = 0
end
end
puts "Ok. Goodbye!!"
and here is the error i keep receiving when i try to run this program:
guessing_game.rb:12:in `guess_check_against': undefined local variable or method `guess' for main:Object (NameError)
from guessing_game.rb:45:in `<main>'
when i try to use irb to run the same scenario it works completely fine.
i can not figure out what i am doing wrong, please help!!
The method definition
def guess_check_against()
g = guess.last
...
end
has its own scope, and the local variable
guess = Array.new
that you defined outside of it, is not accessible inside the method definition. guess is not defined inside the method definition. You can change the code so that the method takes that as an argument, and it will become accessible.

Resources