@max has idenfied your problem. I would like to give a solution that works with strings directly; that is, it does not convert strings to arrays of characters.
Let's first create two helper methods that we can test separately. The first obtains a guess of a letter from the player.
def request_guess(guesses)
loop do
print "enter a letter: "
guess = gets.chomp.downcase
if !guess.match?(/\A[a-z]\z/)
puts "You must enter a single letter. Try again."
elsif guesses.include?(guess)
puts "Er, you already guessed that letter. Try again."
else
break guess
end
end
end
The regular expression /\A[a-z]\z/ used by String#match reads, "match the beginning of the string (\A) followed by any one lowercase letter followed by the end of the string (\z). Suppose letters in the string "erluz" had already been guessed. Here is a possible dialogue.
request_guess("erluz")
enter a letter: 4
You must enter a single letter. Try again.
enter a letter: ab
You must enter a single letter. Try again.
enter a letter: l
Er, you already guessed that letter. Try again.
enter a letter: U
Er, you already guessed that letter. Try again.
enter a letter: i
#=> "i"
The second helper method creates a string that displays the letters in the word that have been identified by previous guesses and displays hyphens in place of the letters that have not yet been identified.
def reveal(word, guesses)
word.gsub(/./) { |c| guesses.include?(c) ? c : '-' }
end
The regular expression used by String#gsub (/./) matches any single character so it matches each character in the string. The character is held by the block variable c.
If, for example, the word were "blizzard" and the letters in the string "erluoz" had been guessed, the "reveal" would be:
reveal("blizzard", "erluz")
#=> "-l-zz-r-"
Notice that this is not especially efficient. Firstly, for this example, had guesses contained only successful guesses we wouldn't have to check if 'e' or 'u' were in the string. However, if want to inform the player that they have repeated an unsuccessful guess we need to keep the unsuccessful guesses somewhere. We could also improve efficiency by making guesses a set of guessed characters, rather that a string, but let's be realistic, efficiency is irrelevant here when one considers that English words contain no more than 45 characters1.
We can operate the game as follows.
word = "hello"
guesses = ''
loop do
break if word.delete(guesses).empty?
puts "guess the word: #{reveal(word, guesses)}"
guess = request_guess(guesses)
puts "The word contains #{word.count(guess)} #{guess}'s."
guesses << guess
end
puts word
puts "Congratulations! You won!"
We may have the following dialogue.
guess the word: -----
enter a letter: ab
You must enter a single letter. Try again.
enter a letter: a
The word contains 0 a's.
guess the word: -----
enter a letter: e
The word contains 1 e's.
guess the word: -e---
enter a letter: l
The word contains 2 l's.
guess the word: -ell-
enter a letter: u
The word contains 0 u's.
guess the word: -ell-
enter a letter: o
The word contains 1 o's.
guess the word: -ello
enter a letter: h
The word contains 1 h's.
hello
Congratulations! You won!
1. The number of letters in "pneumonoultramicroscopicsilicovolcanoconiosis".
chars.each.with_indexand test each character.chars = ['b', 'a', 'n', 'a', 'n', 'a']; guess = 'a'; chars.each_index.select { |i| chars[i] == guess } #=> [1, 3, 5].