unable to reach method inside if else statement ruby - ruby

I am currently working on building a word guessing game. There is a word and a user would guess a character in the word. If the character the user guesses is in fact in the word then feedback would be given back. For example the word is "cookie" and the user enters "o" then feedback would be
_ o o _ _ _
the number of attempts is equal to the length of the word in this case 6. If the user guesses a character that is not part of the word for example "z" a message would display error. However I am having problems accessing the if and else statements, they work only for the first input. here is my code:
class Game
attr_reader :word
attr_accessor :guess_counts, :guesses, :user_input, :guess_array
def initialize(word)
#word = word
#guesses = ""
end
def tries
#tries = #word.length
end
def guesses(user_input)
#user_input = user_input
#guesses << #user_input
end
def underscored
return #word.tr('^' + #guesses, '_').chars.join(' ')
end
end
# user interface
puts "Please enter a word to initialize the Guessing The Word game"
secret_word = gets.chomp
game = Game.new(secret_word)
puts "you have #{game.tries} attemps left"
guesses = []
tries = secret_word.length
while tries > 0
puts "Plese enter a letter you believe is in the secret word"
letter = gets.chomp
game.guesses(letter)
guesses << letter
guess = guesses[0]
if !secret_word.include? guess
puts "Letter not in word"
tries -= 1
puts "you have #{tries} left "
next
elsif secret_word.include? guess
p game.underscored
else
puts "you lost"
end
end
as you see in this image when I input "o" it says letter not in word but if I start with the letter "o" it would display

The problem is you are always grabbing the first element in the array of guesses when you wrote:
guess = guesses[0]
So the expression !secret_word.include? guess is:
always true when the first guess is correct, and then is
always false when the first answer is incorrect
To fix this you probably want grab the last guess; ie.
guess = guesses.last
# `.last` is convenience method for accessing last element of an array; like so
#
# guess = guesses[-1]
or the current letter for your if statement: i.e.
if !secret_word.include? letter
# [...]
elsif secret_word.include? letter
# [...]

Related

why is my input method for the hangman game failing to function properly?

I have this method where it gets an input from the user and it checks it against a while condition. if the user inputted anything that isnt a string or if the user inputted a character that was longer than 1 the method would prompt the user again for a valid input, basically adhering to the hangman rules. Heres the code
class Hangman
def initialize
dictionary = File.open('5desk.txt',"r")
line = dictionary.readlines
#word = line[rand(1..line.length)]
#length = #word.length
random = #word.length - rand(#word.length/2)
random.times do
#word[rand(#word.length)] = "_"
end
end
This method fails to function properly.
def get_input
puts #word
puts "Letter Please?"
#letter = gets.chomp
while !#letter.kind_of? String || #letter.length != 1
puts "Invalid input,try again!"
#letter = gets.chomp
end
end
end
Game = Hangman.new
Game.get_input
class Hangman
Stop right there! Why create a class considering that you would only create a single instance of it? There's no need for one. A few methods and one instance variable are sufficient.
Generate secret words randomly
I assume the file '5desk.txt' contains one secret words per line and you will be selecting one randomly. So begin by gulping the entire file into an array held by an instance variable (as opposed to reading the file line-by-line). I assume '5desk.txt1' contains the three words shown below.
#secret_words = File.readlines('5desk.txt', chomp: true)
#=> ["cat", "violin", "whoops"]
See the doc for the class method IO::readlines1,2. The option chomp: true removes the newline character from the end of each line.
This method closes the file after it has been read. (You used File::open. When doing so you need to close the file when you are finished with it: f = File.open(fname)...f.close.)
You need a method to randomly choose a secret_word.
def fetch_secret_word
#secret_words.sample
end
fetch_secret_word
#=> "violin"
See Array#sample. You could have instead used
#secret_words[rand(#secret_words.size)]
See Kernel#rand. The first and last words in #secret_words are #secret_words[0] and #secret_words[#secret_words.size-1]. Therefore, where you wrote
#word = line[rand(1..line.length)]
it should have been
#word = line[rand(0..line.length-1)]
which is the same as
#word = line[rand(line.length)]
Now let's create a method for playing the game, passing an argument that equals the maximum number of incorrect guesses the player has before losing.
def play_hangman(max_guesses)
First get a secret word:
secret_word = fetch_secret_word
Let us suppose that secret_word #=> "violin"
Initialize objects
Next, initialize the number of incorrect guesses and an image of the secret word:
incorrect_guesses = 0
secret_word_image = "-" * secret_word.size
#=> "------"
So we now have
def play_hangman(max_guesses)
secret_word = fetch_secret_word
incorrect_guesses = 0
secret_word_image = "-" * secret_word.size
Loop over guesses
Now we need to loop over the player's guesses. I suggest you use Kernel#loop, in conjuction with the keyword break for all your looping needs. (For now, forget about while and until, and never use for.) The first thing we will do in the loop is to obtain the guess of a letter from the player, which I'll do by calling a method:
loop do
guess = get_letter(secret_word_image)
...<to be completed>
end
def get_letter(secret_word_image)
loop do
puts secret_word_image
print "Gimme a letter: "
letter = gets.chomp.downcase
break letter if letter.match?(/[a-z]/)
puts "That's not a letter. Try again."
end
end
guess = secret_letter(secret_word_image)
#=> "b"
Here this method returns "b" (the guess) and displays:
------
Gimme a letter: &
That's not a letter. Try again.
------
Gimme a letter: 3
That's not a letter. Try again.
------
Gimme a letter: b
See if letter guessed is in secret word
Now we need to see which if any of the hidden letters equal letter. Again, let's make this a method3.
def hidden_letters(guess, secret_word, secret_word_image)
(0..secret_word.size-1).select do |i|
guess == secret_word[i] && secret_word_image[i] = '-'
end
end
Suppose guess #=> "i". Then:
idx = hidden_letters(guess, secret_word, secret_word_image)
#=> [1,4]
There are two "i"'s, at indices 1 and 4. Had there been no hidden letters "i" the method would have returned an empty array.
Before continuing let's look at our play_hangman is coming along.
def play_hangman(max_guesses)
secret_word = fetch_secret_word
incorrect_guesses = 0
secret_word_image = "-" * secret_word.size
loop do
unless secret_word_image.include?('-')
puts "You've won. The secret word is '#{secret_word}'!"
break
end
guess = get_letter(secret_word_image)
idx = hidden_letters(guess, secret_word, secret_word_image)
...<to be completed>
end
Process a guess
We now have to carry out one course of action if the array idx is empty and another if it is not.
case idx.size
when 0
puts "Sorry, no #{guess}'s"
incorrect_guesses += 1
if incorrect_guesses == max_guesses
puts "Oh, my, you've used up all your guesses, but"
puts "we'd like you take home a bar of soap"
break
else
puts idx.size == 1 ? "There is 1 #{guess}!" :
"There are #{idx} #{guess}'s!"
idx.each { |i| secret_word_image[i] = guess }
if secret_word_image == secret_word
puts "You've won!! The secret word is '#{secret_word}'!"
break
end
end
Complete method
So now let's look at the full method (which calls fetch_secret_word, get_letter and hidden_letters).
def play_hangman(max_guesses)
secret_word = fetch_secret_word
incorrect_guesses = 0
secret_word_image = "-" * secret_word.size
loop do
guess = get_letter(secret_word_image)
idx = hidden_letters(guess, secret_word, secret_word_image)
case idx.size
when 0
puts "Sorry, no #{guess}'s"
incorrect_guesses += 1
if incorrect_guesses == max_guesses
puts "Oh, my, you've used up all your guesses,\n" +
"but we'd like you take home a bar of soap"
return
end
else
puts idx.size == 1 ? "There is 1 #{guess}!" :
"There are #{idx.size} #{guess}'s!"
idx.each { |i| secret_word_image[i] = guess }
if secret_word_image == secret_word
puts "You've won!! The secret word is '#{secret_word}'!"
return
end
end
end
end
Play the game!
Here is a example play of the game.
play_hangman(4)
------
Gimme a letter: #
That's not a letter. Try again.
------
Gimme a letter: e
Sorry, no e's
------
Gimme a letter: o
There is 1 o!
--o---
Gimme a letter: i
There are 2 i's!
-io-i-
Gimme a letter: l
There is 1 l!
-ioli-
Gimme a letter: v
There is 1 v!
violi-
Gimme a letter: r
Sorry, no r's
violi-
Gimme a letter: s
Sorry, no s's
violi-
Gimme a letter: t
Sorry, no t's
Oh, my, you've used up all your guesses,
but we'd like you take home a bar of soap
1 The class File has no (class) method readlines. So how can we write File.readlines? It's because File is a subclass of IO (File.superclass #=> IO) and therefore inherits IO's methods. One commonly sees IO class methods invoked with File as the receiver.
2 Ruby's class methods are referenced mod::meth (e.g., Array::new), where mod is the name of a module (which may be a class) and meth is the method. Instance methods are referenced mod#meth (e.g., Array#join).
3 Some Rubyists prefer to write (0..secret_word.size-1) with three dots: (0...secret_word.size). I virtually never use three dots because I find it tends to create bugs. The one exception is when creating an infinite range that excludes the endpoint (e.g., 1.0...1.5).

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

Sentence rotate program not working ruby

I'm trying to write a program that takes a user's input (sentence), a word the user wants to rotate around, and outputs a rotated sentence around the word that has been chosen by user.
eg. Sentence: This is a book
Word to rotate around: book
Output: book This is a
I can't seem to exit this loop of entering data (the program keeps asking for input, not doing anything more.)
Please help. Here's my code:
class SentenceRotator
def get_sentence
puts "Please enter your sentence: "
sentence = gets.chomp
get_word
end
def get_word
puts "Please enter the word you want to rotate around: "
word = gets.chomp
if converts_sentence_to_array.include?(word)
rotate_sentence_around_word
else
puts "Your word isn't in the sentence. Please enter another word."
word = gets.chomp
end
rotate_sentence_around_word
end
def converts_sentence_to_array()
get_sentence.split(" ")
end
def rotate_sentence_around_word()
new_array = converts_sentence_to_array.each_with_index {|word,index| converts_sentence_to_array.rotate(index)}
new_array
end
end
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
So, calling methods: rotate_sentence_around_word => converts_sentence_to_array => get_sentence => get_word => converts_sentence_to_array => get_sentence => ...
Try something like this:
class SentenceRotator
def gets_user_data
puts "Please enter your sentence: "
#sentence = get_sentence.split(" ")
puts "Please enter the word you want to rotate around: "
#word = get_word_to_rotate_on
end
def get_sentence
gets.chomp
end
def get_word_to_rotate_on
word = gets.chomp
return word if #sentence.include?(word)
puts "Your word isn't in the sentence. Please enter another word."
get_word_to_rotate_on
end
def rotate_sentence_around_word()
gets_user_data
#sentence.rotate(#sentence.index(#word)).join(' ')
end
end
new_app = SentenceRotator.new
new_app.rotate_sentence_around_word
Here's the logic you want, split up into separate lines:
# input
sentence = 'This is a book'
word = 'book'
# processing
words = sentence.split
pos = words.index(word)
rotated = words.rotate(pos)
back_together = rotated.join(' ')
# output
p back_together
I'd advise you to separate out your processing code as much as possible. Then you can focus on the terminal input and output logic, which is what actually seems to be your issue.
I can't seem to exit this loop of entering data (the program keeps asking for input, not doing anything more.)
It looks like your foremost problem is to take the input from user properly and enter the rotation logic. You could make use of attr_reader to access the input across methods. I have made some changes to your class to accept the input in multiple steps:
class SentenceRotator
attr_reader :sentence, :rotate_on_word
def get_sentence
puts "Please enter your sentence: "
#sentence = gets.chomp
end
def get_word_to_rotate_on
puts "Please enter the word you want to rotate around: "
#rotate_on_word = gets.chomp
unless sentence.include?(rotate_on_word)
puts "Your word isn't in the sentence. Please enter another word."
get_word_to_rotate_on
end
end
def rotate
puts sentence
puts rotate_on_word
puts 'You have all the info. Add the logic to rotate.'
end
end
> new_app = SentenceRotator.new
> new_app.get_sentence
Please enter your sentence:
This is a very funny book
> new_app
=> #<SentenceRotator:0x00007fee44178c40 #sentence="This is a very funny book">
> new_app.get_word_to_rotate_on
Please enter the word you want to rotate around:
a
> new_app
=> #<SentenceRotator:0x00007fee44178c40 #sentence="This is a very funny book", #rotate_on_word="a">
> new_app.rotate
This is a very funny book
a
You have all the info. Add the logic to rotate.

How to determine whether input is empty or enter is pressed

I have a task to puts an infinite number of word, each in one line to array, and when enter is pressed on an empty line, puts these words in reverse order. How can I define when enter is pressed or empty line is input?
Code is here:
word = []
puts "Enter word"
add = 0
until add == ????
word.push gets.chomp
add = word.last
end
puts word.reverse
Here's a possible solution, with comments. I didn't see any useful role being played by your add variable, so I ignored it. I also believe in prompting the user regularly so they know the program is still engaged with them, so I moved the user-prompt inside the loop.
word = [] # Start with an empty array
# Use loop when the terminating condition isn't known at the beginning
# or end of the repetition, but rather it's determined in the middle
loop do
print 'Enter word: ' # I like to prompt the user each time.
response = gets.chomp # Read the response and clean it up.
break if response.empty? # No response? Time to bail out of the loop!
word << response # Still in the loop? Append the response to the array.
end
puts word.reverse # Now that we're out of the loop, reverse and print
You may or may not prefer to use strip rather than chomp. Strip would halt if the user input a line of whitespace.
Here, this is a modified version of your code and it works as requested.
word = []
puts "Enter word"
add = 0
while add != -1
ans = gets.chomp
word.push ans
if ans == ""
puts word.reverse
exit
end
add += 1
end
puts word.reverse
This is another version, using (as you did originally) the until loop.
word = []
puts "Enter word"
add = 0
until add == Float::INFINITY
ans = gets.chomp
word.push ans
if ans == ""
puts word.reverse
exit
end
add += 1
end
puts word.reverse

Command not found Ruby - trying to create 24 game

I am trying to solve the "24" game. The point of the game is to generate 4 random integers from 1-9 and ask the player to use addition, subtraction, multiplication, or division to get the number 24. My code runs fine until a player enters a number, and then I get "Command not found". Can someone please take a look at this:
def evaluate (input,solved_v)
input = eval (input.to_f)
#convert to a float and then evaluates it; it computes
if input == 24
solved_v = true
puts "great job! you did it!"
else
puts "please try again"
end
end
def test_entry (input)
if input.scan(%r{[^\d\s()+*/-]}).empty?
#this scan detects letters and special characters because only numbers work
true
else
false
end
end
puts
puts "try to use +, -, / or * to"
puts "get 24 from the integers provided"
puts
series = (1..4).collect{ rand(1..9)}
#generates 4 random numbers between 1 and 9
for i in series
puts i
end
puts "Please guess"
solved = false
unless solved = true
user_input = gets.chomp
if test_entry(user_input) == true
evaluate(user_input)
else
puts "invalid characters entered"
puts "please try again"
puts
end
end
There are numerous problems with your program.
Don't put spaces between your method names and parentheses.
eval takes a string argument, not a float.
Ruby passes arguments by value, so solved_v isn't going to get
returned. Make it the return value of your evaluate method. I'd also
suggest renaming your methods to express their boolean intent. See below...
Don't check boolean expressions for equality to true or false, just use them.
def correct?(input)
if eval(input) == 24
puts "great job! you did it!"
true
else
puts "please try again"
false
end
end
def good_entry?(input)
input.scan(%r{[^\d\s()+*/-]}).empty?
end
and they get used as follows
while true
user_input = gets.chomp
if good_entry?(user_input)
break if correct?(user_input)
else
...
end
end
Finally, note that you're not actually checking that the input provided by the user uses only the supplied random numbers.

Resources