How to replace matching characters in a string? (Ruby) - arrays

I'm building a hangman game and I have no idea how to replace underscores in hidden_word (String) with matching letters in player_input (Array). Any ideas what I should do? Thank you in advance, I appreciate it!
def update
if #the_word.chars.any? do |letter|
#player_input.include?(letter.downcase)
end
puts "updated hidden word" #how to replace underscores?
end
puts #hidden_word
puts "You have #{#attempts_left-1} attempts left."
end
I have two Strings, the_word and hidden_word, and an Array, player_input. Whenever the player selects a letter which matches with the_word, the hidden_word should update.
For example
the_word = "RUBY"
hidden_word = "_ _ _ _"
Player chooses "g", hidden_word still "_ _ _ _"
Player chooses "r", hidden_word updates "R _ _ _"
Here's the rest of the code:
class Game
attr_reader :the_word
def initialize
#the_word = random_word.upcase
#player_input = Array.new
#attempts_left = 10
end
def random_word
#the_word = File.readlines("../5desk.txt").sample.strip()
end
def hide_the_word
#hidden_word = "_" * #the_word.size
puts "Can you find out this word? #{#hidden_word}"
puts "You have #{#attempts_left} attempts left."
puts #the_word #delete this
end
def update
if #the_word.chars.any? do |letter|
#player_input.include?(letter.downcase)
end
puts "updated hidden word" #how to replace underscores?
end
puts #hidden_word
puts "You have #{#attempts_left-1} attempts left."
end
def guess_a_letter
#player_input << gets.chomp
puts "All the letters you have guessed: #{#player_input}"
end
def has_won?
if !#hidden_word.include?("_") || #player_input.include?(#the_word.downcase)
puts "You won!"
elsif #attempts_left == 0
puts "You lost..."
end
end
def game_round #the loop need fixin
puts "Let's play hangman!"
hide_the_word
while #attempts_left > 0
guess_a_letter
update
#attempts_left -= 1 #fix this
has_won?
break if #player_input.include?("q") #delete this
end
end
end
new_game = Game.new
new_game.game_round

Here's some code that should get you going. Collect the guessed letters in an array. Then, map the chars of the word to either the char if it was guessed or an underscore.
word = "RHUBARB"
guessed_letters = ['A', 'R', 'U']
hidden_word = word.chars.map { |c| guessed_letters.include?(c) ? c : '_' }.join
# => "R_U_AR_"

I don't sure about downcase because you have used uppercase too.
Choose only one letter case.
But it will work for you:
def update
#the_word.each_char.with_index do |letter, index|
#hidden_word[index] = letter if #player_input.include?(letter.downcase)
end
puts #hidden_word
puts "You have #{#attempts_left-1} attempts left."
end
It compares each letter of secret word with user's inputs and changes underscore in hidden word by coincidence.
Here I used String#each_char, String#[]=, Enumerator#with_index

One option is to use a regular expression:
#hidden_word = #the_word.gsub(/[^#{#player_input.join('')}\s]/i, '_')

Related

How to compare letters in two strings Ruby

I am new to ruby and creating a hangman game. Soo far I have my code comparing the words to the correct word. But I want it to compare letters. So basically, if the secrect word is glue, the user enters G it would come inncorrect, but if the user enters glue it would be correct. I need it to compare letter by letter just like hangman.
Having a bit of trouble with that. I have attached my code below.
secret_word = []
puts "enter a word to be guessed"
secret_word = gets.chomp
guess_letters = []
guess = ""
guess_limit = 3
guess_count = 0
out_of_guesses = false
while guess != secret_word and !out_of_guesses
if guess_count < guess_limit
puts "enter your guess: "
guess = gets.chomp()
guess_letters << guess
guess_count +=1
puts "you have used these letters thus far #{guess_letters.join(", ")}"
else
out_of_guesses = true
end
end
if out_of_guesses
puts "you Lose, the word was #{secret_word}"
else
puts "you win"
end
I'm not sure which hangman rules you are using but here's a rough draft that allows three failed attempts and works with lowercase characters
def guess_word(word, tries)
if tries < 1
puts "You are hanged!"
elsif word.empty?
puts "You guessed it! You are saved from the gallows!"
else
print "Enter character: "
c = STDIN.getc.downcase
STDIN.getc # get rid of newline
if word.index(c).nil?
puts "Ooops, #{c} was wrong!"
guess_word(word, tries - 1)
else
puts "#{c} was correct!"
guess_word(word.sub(/["#{c}"]/, ''), tries)
end
end
end
if __FILE__ == $0
TRIES = 3
print "Enter word to guess: "
word = gets.chomp
guess_word(word.downcase, 3)
end
This is untested..
The rules of the game hangman are given at its Wiki. I've assumed the player trying to guess the word loses when all seven parts of the man on the gallows have been drawn (head, neck, left arm, body, right arm, left leg, right leg).
Helper methods
Draw the man being hanged
First create a hash that can be used to draw the partial or full hangman:
MAN = [" O\n", " |\n", "\\", "|", "/\n", " |\n/", " \\"].
map.each_with_object([""]) { |s,arr| arr << (arr.last + s) }.
each.with_index.with_object({}) { |(s,i),h| h[i] = s }
The keys are the number of incorrect guesses. For example:
puts MAN[2]
O
|
puts MAN[6]
O
|
\|/
|
/
Keep track of the positions of the letters of the word
Next create a hash whose keys are unique letters of the secret word and whose values are arrays of indices of the keys location(s) in the word.
def construct_unknown(word)
word.each_char.with_index.with_object({}) { |(c,i),h| (h[c] ||= []) << i }
end
For example,
unknown = construct_unknown("beetle")
#=> {"b"=>[0], "e"=>[1, 2, 5], "t"=>[3], "l"=>[4]}
We will also create an empty hash for letters whose positions are known:
known = {}
Move guessed letters from the hash unknown to the hash known
If a letter that is guessed is a key of unknown that key and value are moved to known.
def move_unknown_to_known(letter, unknown, known)
known.update(letter=>unknown[letter])
unknown.delete(letter)
end
For example (for unknown and known above),
move_unknown_to_known("e", unknown, known)
unknown #=> {"b"=>[0], "t"=>[3], "l"=>[4]}
known #=> {"e"=>[1, 2, 5]}
See if the guesser has won or lost
We to determine when, after guessing a letter, the player has won or lost, or is to continue:
def win?(word_size, known)
known.values.flatten.sum == word_size
end
def lose?(wrong_guess_count)
wrong_guess_count == HANGMAN.size
end
For example,
win?(word.size, known)
#=> false
lose?(6) #=> false
lose?(7) #=> true
Display the known letters
def display_known(word_size, known)
known.each_with_object('_' * word_size) { |(k,a),s| a.each { |i| s[i] = k } }
end
For example (recall word #=> "beetle"),
puts display_known(word.size, known)
_ee__e
Main method
We are now ready to write the main method.
def hangman
puts "Player 2, please avert your eyes for a moment."
print "Player 1: enter a secret word with at least two letters: "
word = gets.chomp.downcase
unknown = construct_unknown(word)
known = {}
wrong_guess_count = 0
loop do
puts display_known(word.size, known)
puts MAN[wrong_guess_count] if wrong_guess_count > 0
if win?(word.size, known)
puts "You win! You win! Congratulations!"
break
end
if lose?(wrong_guess_count)
puts "Sorry, but you've run out of guesses"
break
end
print "Player 2: enter a letter or your guess of the word: "
guess = gets.chomp.downcase
if guess.size > 1
if guess == word
puts word
puts "You win! You win! Congratulations!"
break
else
puts "Sorry, that's not the word"
wrong_guess_count += 1
end
elsif unknown.key?(guess)
nbr = unknown[guess].size
puts nbr == 1 ? "There is 1 #{guess}" : "There are #{nbr} #{guess}'s"
move_unknown_to_known(guess, unknown, known)
else
puts "Sorry, the word contains no #{guess}'s"
wrong_guess_count += 1
end
end
end
Example
After explaining the rules to the two players and to the audience, the guest host ends by saying, "And don't forget, when guessing a letter or the word it must be expressed as a question...one moment...hold that...I've been told it is not necessary to frame that as a question".
Suppose the word is beetle and the letter guesses are 't', 'i', 'a', 'l', 'r', 's', 't', 'u', 'e', 'beetle'.
hangman
Player 2, please avert your eyes for a moment.
Player 1: enter a secret word with at least two letters: beetle
______
Player 2: enter a letter or your guess of the word: t
There is 1 t
___t__
Player 2: enter a letter or your guess of the word: i
Sorry, the word contains no i's
___t__
O
Player 2: enter a letter or your guess of the word: a
Sorry, the word contains no a's
___t__
O
|
Player 2: enter a letter or your guess of the word: l
There is 1 l
___tl_
O
|
Player 2: enter a letter or your guess of the word: r
Sorry, the word contains no r's
___tl_
O
|
\
Player 2: enter a letter or your guess of the word: s
Sorry, the word contains no s's
___tl_
O
|
\|
Player 2: enter a letter or your guess of the word: t
Sorry, the word contains no t's
___tl_
O
|
\|/
Player 2: enter a letter or your guess of the word: u
Sorry, the word contains no u's
___tl_
O
|
\|/
|
/
Player 2: enter a letter or your guess of the word: e
There are 3 e's
_eetle
O
|
\|/
|
/
Player 2: enter a letter or your guess of the word: beetle
beetle
You win! You win! Congratulations!

Selecting a single element from an array, within an "if" statement

Creating a method that receives input from a user -- and if said input includes any word from a predetermined array, it prints the word "success!" Otherwise, if the input doesn't include a word from that array, it prints the word "failure."
My ideal would be to create a scenario where I can populate the array with as many elements as I want, and the method will always reference that array when deciding how to react to the user's input.
When I run it, however, I get an error message saying "no implicit conversion of Array into String."
My code is below. Appreciate any and all help! Thanks.
def hello
word_bank = ["hello", "hi", "howdy"]
print "Say a word: "
greeting = $stdin.gets.chomp.to_s
if greeting.include?(word_bank[0..2])
puts "success!"
else
puts "failure."
end
end
hello
include? is an array's method.
word_bank = ["hello", "hi", "howdy"]
print "Say a word: "
greeting = gets.chomp
if word_bank.include?(greeting)
puts "success!"
else
puts "failure."
end
puts [1,2,3].include?(1) # true
puts [1,2,3].include?(4) # false
If word_bank was big, for performance reason we should use a set instead of an array.
require 'set'
word_bank = Set.new(["hello", "hi", "howdy"])
This is how I'd solve the issue, I haven't tested it though but it should work.
def hello
word_bank = ['hello', 'hi', 'howdy']
print 'Say a word: '
greeting = $stdin.gets.chomp.to_s
word_bank.each do |w|
w == greeting ? (puts 'success') : (puts 'failure')
end
end

How to check if a variable in one array exists in another and how to replace it in ruby?

I'm asking for input and then asking which words does the user want to redact. Then trying to print out the string with the words redacted.
puts "Input please"
text = gets.chomp
puts "What would you like redacted, sir? Type DONE when nothing else to redact"
redact = Array.new
answer = gets.chomp
until answer.downcase == "done"
redact << answer.downcase
answer = gets.chomp
end
words = text.split (" ")
words.each do |word|
if word.include? (# An array to redact)
# Replace words here
print "REDACTED "
else
print word + " "
end
end
Just solved it one way!!
words.each do |word|
if redact.include?(word)
#IF THE ARRAY REDACT INCLUDES VARIABLE WORD, PRINT REDACT
print "REDACTED "
else print word + " "
end
end
You can do something like this.
words.map {|word| redact.include?(word) ? 'REDACTED' : word}
This will iterate over each of the elements in your words array, and look to see if the element is in the redact array. If it is, it changes its value to 'REDACT', else it keeps the word the same.
Note that this will create a new array, so you will probably want to assign it to a new variable. If you want to edit the words array in place, you can use the map! function.
boolean ? true_path : false_path
is called a ternary, it is just short for
if boolean
true_path
else
false_path
end

Ruby .each method on Array from split string

I'm having trouble using the .each method on an array that results of a string.split
def my_function(str)
words = str.split
return words #=> good, morning
return words[words.count-1] #=> morning
words.each do |word|
return word
end
end
puts my_function("good morning") #=> good
With any multi-word string, I only get the 1st word, not each of the words. With this example, I don't understand why I don't get "good" and "morning" when the 2nd item clearly exists in the array.
Similarly, using a while loop gave me the same result.
def my_function(str)
words = str.split
i = 0
while i < words.count
return word[i]
i += 1
end
puts my_function("good morning") # => good
Any help is appreciated. Thanks in advance!
The return statement in ruby is used to return one or more values from a Ruby Method. So your method will exit from return words.
def my_function(str)
words = str.split
return words # method will exit from here, and not continue, but return value is an array(["good", "morning"]).
return words[words.count-1] #=> morning
....
end
puts my_function("good morning")
output:
good
morning
if you want to use each method to output words, you can do like this:
def my_function(str)
str.split.each do |word|
puts word
end
end
or
def my_function(str)
str.split.each { |word| puts word }
end
my_function("good morning")
output:
good
morning
You are assuming that return words returns the array to your outer puts function which is true. However once you return, you leave the function and never go back unless you explicitly call my_function() again (which you aren't) in which case you would start from the beginning of the function again.
If you want to print the value while staying in the function you will need to use
def my_function(str)
words = str.split
puts words #=> good, morning
puts words[words.count-1] #=> morning
words.each do |word|
puts word # print "good" on 1st iteration, "morning" on 2nd
end
end
my_function("good morning")

How to have the user input alternate between uppercase and lowercase in Ruby?

I have Ruby ask the user five times to enter a name, and want the answers to be spat out with each line to alternate with uppercase and lowercase. Below is what I have done and it prints out each name twice, one uppercase one lowercase. But I just want each line to alternate between uppercase and lowercase. I hope I'm making sense here...
Football_team = []
5.times do
puts "Please enter a UK football team:"
Football_team << gets.chomp
end
Football_team.each do |Football_team|
puts team.upcase
puts team.downcase
end
football_team = []
5.times do
puts "Please enter a UK football team:"
football_team << gets.chomp
end
football_team.each_with_index do |team, index|
if index.even?
puts team.upcase
else
puts team.downcase
end
end
Note that you should use identifiers starting with capitals only for constants. While Football_team might be a constant, it is generally not a good idea. Also note that your loop variable was wrong.
You don't really need two loops. You can do it all in one. See below
football_team = []
5.times do |i|
puts "Please enter a UK football team:"
team = gets.chomp
if i.even?
football_team << team.upcase
else
football_team << team.downcase
end
end
puts football_team
Alternatively, same solution but in shorthand:
football_team = []
5.times do |i|
puts "Please enter a UK football team:"
i.even? ? football_team << gets.chomp.upcase : football_team << gets.chomp.downcase
end
puts football_team

Resources