Rock, paper, scissors game in Ruby - ruby

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!

Related

Using Key Value pairs in Hash as question and answer

I'm working on an assignment in my code bootcamp, it involves ruby.
Create a program with a hash of countries & capitals such as the following:
cos_n_caps = {
"USA" => "Washington, DC",
"Canada"=>"Ottawa",
"United Kingdom"=>"London",
"France"=>"Paris",
"Germany"=>"Berlin",
"Egypt"=>"Cairo",
"Ghana"=>"Accra",
"Kenya"=>"Nairobi",
"Somalia"=>"Mogadishu",
"Sudan"=>"Khartoum",
"Tunisia"=>"Tunis",
"Japan"=>"Tokyo",
"China"=>"Beijing",
"Thailand"=>"Bangkok",
"India"=>"New Delhi",
"Philippines"=>"Manila",
"Australia"=>"Canberra",
"Kyrgyzstan"=>"Bishkek"
}
Ask the user for the capital of each country, and tell them if they are correct. Also, keep score and give them their score at the end of the quiz.
I want to know if I can somehow cycle through the list of keys and ask for user_input after each key and then check again value.
I've tried to use hash.for_each{|key| puts key}, but I don't know how to ask for user_input between the keys.
This is what I was going to do, unless I can find something easier:
s = "What is the capital of"
score = 0
count = 0
until count == 1
puts "#{s} USA"
a = gets.chomp.downcase
if a == c["USA"].downcase
puts "Congrats"
score += 1
count += 1
else
puts "nope"
count +=1
end
end
Use Hash#each to loop through each pair of countries and capitals. In that loop, use Kernel#gets to read their answer and String#chomp to remove the newline off their answer.
cos_n_caps.each do |country,capital|
puts "What is the capital of #{country}?"
answer = gets.chomp
if capital.downcase == answer.downcase
puts "Right!"
else
puts "Sorry, it's #{capital}."
end
end

gets.chomp three times in a row to exit

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

Ruby exact string match

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".

Is there a way to 'cd ..' up a nested "if" statement tree?

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.

IF conditions not recognising in Ruby

I'm testing my basics of Ruby by writing a simple ChingChongCha program. One of my methods takes the choice entered and turns it into a number (for easy of use for future in the program) however this if statement keeps defaulting to the 'else' condition, even though I can see clearly that if rock is entered it matches the if condition perfectly with ROCK. Any ideas?
def user_choice(choice)
# 1 is rock
# 2 is paper
# 3 is scissors
userintchoice = 0
choice.upcase!
# turns user's choice into an integer
puts choice #debugging
if (choice == 'ROCK') then
userintchoice = 1
elsif (choice == 'PAPER') then
userintchoice = 2
elsif (choice == 'SCISSORS') then
userintchoice = 3
else
puts "Invalid Choice!"
end
return userintchoice
end
Code calling this method and getting input is:
puts "What would you like to choose (input Rock, Paper or Scissors and <ENTER>)?"
userstringchoice = gets()
userchoice = user_choice(userstringchoice)
It seems you have to call .strip on userchoice because otherwise the string will contain a trailing \n.

Resources