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
Related
I created a guessing game through Ruby and I believe the structure of my code is off. When entering 'Cheat', you are given the random number then asked to type it in again. When typed in again, it says the random number is not correct and always defaults to my 'elseif' in line 45.
puts "Hey! I'm Sam. What's your name?"
name = gets
puts "Welcome #{name}. Thanks for playing the guessing game.
I've chosen a number between 1-100.
You'll have 10 tries to guess the correct number.
You'll also recieve a hint when you're guess is wrong.
If you feel like being a big ol cheater, type 'Cheat'.
Let's get started..."
random_number = rand(1...100)
Cheat = random_number
counter = 10
loop do
break if counter == 0
divisor = rand(2...10)
guess = gets.chomp
break if guess.to_i == random_number
counter -= 1
if
guess == random_number
puts 'You guessed the right number! You win!'
end
if counter < 4
puts "You can go ahead and cheat by typing 'Cheat'..."
end
if guess.to_s.downcase.eql? "cheat"
puts "The random number is #{random_number} you CHEATER!! Go ahead and type it in..."
guess = gets.chomp
puts = "You win cheater!"
end
if
guess.to_i < random_number
puts 'Ah shucks, guess again!'
guess = gets.chomp
elsif
guess.to_i > random_number
puts 'Too high, guess again!'
guess = gets.chomp
end
if random_number % divisor == 0
puts "Thats not it.\n #{guess} is #{guess.to_i > random_number ? 'less' : 'greater'} than the random number.
The random number is divisible by #{divisor}.\nTry again: "
elsif
puts "That's not the random number.\n #{guess} is #{guess.to_i > random_number ? 'less' : 'greater'} than the random number.
The random number is NOT divisible by #{divisor}.\nTry again: "
end
end
if counter > 0
puts "The number is #{random_number}! You win!"
else
puts "You lose! Better luck another time."
end
this is the response i get in the terminal
Let's get started...
Cheat
The random number is 96 you CHEATER!! Go ahead and type it in...
96
Thats not it.
96 is greater than the random number.
The random number is divisible by 8.
Try again:
The problem is here:
puts = "You win cheater!"
You're assigning the string "You win cheater!" to a local variable named puts. Changing it to this fixes the problem:
puts "You win cheater!"
You'll probably also want to put a break after that line.
As an aside, this pattern:
loop do
break if counter == 0
# ...
end
...would be better expressed as:
while counter > 0
# ...
end
...or:
until counter == 0
# ...
end
Also, you should always put the condition for an if/elsif/whathaveyou on the same line as if et al. Why? Because if you don't you get bugs like this:
if random_number % divisor == 0
# ...
elsif
puts "..."
end
Can you spot the bug? You forgot to put a condition after elsif, or used elsif when you meant to use else, which means that the return value of puts (which is always nil) is being used as the condition, just as if you had written elsif puts "...".
If you make a habit of always putting the condition on the same line as if/elsif, your eye will get used to it and errors like this will jump out at you.
I'm using a Ruby interpreter to run the code I created(a simple guess a number 1-100 code), but every time you guess the number correctly or incorrectly after x number of times it automatically closes itself after it prints out "You Win!" or "You Lose!". Here's the code:
srand
random_number = rand 1..100
guesses = 10
while guesses > 0
puts "I'm thinking of a number between 1 and 100."
print "What number am I thinking of?"
guess = gets.chomp.to_i
guesses -= 1
break if guess == random_number
puts "Too high" if guess > random_number
puts "Too low" if guess < random_number
end
if guess == random_number
puts "You win!"
else
puts "You lose, sorry!"
end
How can I keep it from shutting itself down, so the user can see the displayed message?
Why not end with:
puts "Hit enter to exit."
gets
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.
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
I'm curious if there's a way to have the program go back up the if statement stack?
Ideally, the program would return to line 2 and prompt the user for the input variable, then continue to evaluate like it did the first time. Think of it like a cursor in a text editor, I just want to move it from either of those two comments back up to line 2. The two places of interest are commented out below:
while true
input = gets.chomp
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
elsif input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "HUH?! SPEAK UP, SONNY!"
input = gets.chomp
if input == 'BYE'
puts "GOOD BYE!";
break
else
# return to top-level if statement
end
else
# return to top-level if statement
end
else
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
end
end
In the code you show, you don't need to do anything to make the flow of execution go back to line 2. Just omit the else clauses in the two places you marked. The flow of execution will drop down to the bottom of the while loop, then loop back to the top, then go back to line 2.
You need to use a while statement to set a condition flag and check it, which will loop back to the while statement if you don't change the flag:
flag = 0
while flag1 == 0
if var = "string"
then ...statements...
flag1 = 1 ; this allows us to break out of this while loop
else ...statements...
end
end
If flag1 is not 0 at the end of the while statement, the while statement will loop back. For two such conditions, you need to nest the while loops. You might have to re-order your statements to make multiple while loops work this way.
You can avoid this level of neasted ifs with:
byecount = 0
while byecount < 3
input = gets.chomp
if input == "BYE"
byecount += 1
next
else
byecount = 0
end
if input != input.upcase
puts "HUH?! SPEAK UP, SONNY!"
else
puts "NO, NOT SINCE #{rand(1930..1950)}!"
end
end
puts "GOOD BYE!"
Or you can write a catch..throw flow structure. (Really.. if you need to use it, something is wrong with your design)
catch :exitloop do
while ...
if ...
if ...
if ...
throw :exitloop
end
end
end
end
end
Here's how I'd write a similar exercise:
BYE = 'BYE'
HUH = "HUH?! SPEAK UP, SONNY!"
loop do
input = gets.chomp
if input != input.upcase
puts HUH
next
end
if input != BYE
random_year = rand(1930..1950)
puts "NO, NOT SINCE #{random_year}!"
next
end
puts HUH
input = gets.chomp
if input == BYE
puts HUH
input = gets.chomp
if input == BYE
puts "GOOD BYE!";
break
end
end
end
I used loop instead of while. Matz, the main man for Ruby, recommends loop. See "Is there a “do … while” loop in Ruby?" for further discussion about it.