How to compare letters in two strings Ruby - arrays

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!

Related

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

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, '_')

How can I find a category of words in a string and take their tally in Ruby?

Given a sentence, I want to tally up the total number of times a category of noun (people vs. animals) is present. This is not the same as finding out how many number of times each of the words occur. Nor am I looking for the total number of times each designated word occurs but rather the tally of total occurrences of all designated words in an array. While advanced methods are appreciated, the search is on for simpler more beginner level coding; one liner coding may be great and sincerely appreciated but I want to have an understanding as a beginner.
In the sentence "John and Mary like horses, ducks, and dogs." I want to tally up the number of animals (3).
str = "John and Mary like horses, ducks, and dogs."
animals= ["horses", "ducks", "dogs"]
def count_a(string)
animals = 0
i = 0
while i < string.length
if (string[i]=="horses" || string[i]=="ducks" ||
string[i]=="dogs")
animals +=1
end
i +=1
end
end
puts count_a(str)
Expecting: 3
Actual: not showing anything in return
> str.scan(Regexp.union(animals)).size
# => 3
Change Regexp to
Regexp.new(animals.join("|"), true)
for case insensitive match.
Your code works one letter at a time:
"abcd"[0]
=> "a"
Then your condition compares that letter to a word:
"abcd"[0] == "duck"
# which is the same as:
"a" == "duck"
# which will never be true
You can split the string into an array of words and use Array#count and Array#include? to count occurences:
ANIMALS = ["horses", "ducks", "dogs"]
def count_a(string)
string.split(/\b/).count { |word| ANIMALS.include?(word) }
end
puts count_a("John and Mary like horses, ducks, and dogs.")
To search matches inside words, like "bulldog" counting as a dog, you can use:
def count_a(string)
ANIMALS.inject(0) { |count, animal| count + string.scan(animal).size }
end
Keeping your logic, fix like this (see inline comments):
def count_a(string)
string = string.scan(/\w+/).map(&:downcase) # <------------ split into words
animals = 0
i = 0
while i < string.length
if (string[i]=="horses" || string[i]=="ducks" ||
string[i]=="dogs")
animals +=1
end
i +=1
end
return animals # <------------ return the count
end
puts count_a(str) #=> 3

Issues with the output format of hash

So I was trying to create a program that resembles a grocery list where the user puts the item and its associated cost and it would display it as a form of a list. So I created this:
arr = []
arr2 = []
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
arr << item
arr2 << cost
end
h = { arr => arr2 }
for k,v in h
puts "#{k} costs #{v}"
end
(Code is probably very inefficient, but with my limited starter knowledge it's the best I can do)
So my problem is when I try more than two items the results would display like this (Let's say I used Banana and Kiwi for item and put a random number for their costs):
["Banana", "Kiwi"] costs [2.0, 3,0]
I, however, would like it to display like this:
Banana costs $2.00
Kiwi costs $3.00
I know it probably has to do something with this line:
h = { arr => arr2 }
But I just don't know what I can change about it. I already spend hours trying to figure out how it works so if anyone can give me a hint or help me out I would appreciate it! (Also my apologies for the vague title, didn't know better on how to describe it...)
yes, you are correct. Problem is with this line h = { arr => arr2 }. This line will create a hash like h = {["Banana", "Kiwi"] => [2.0, 3,0]}.
1) You can modify your code as below if you want to use two arrays.
(0...arr.length).each do |ind|
puts "#{arr[ind]} costs $#{arr2[ind]}"
end
2) Better, you can use a hash to store the item and it's cost and then iterate over it to show the results
hash = {}
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
hash[item] = cost
end
hash.each do |k,v|
puts "#{k} costs $#{v}"
end
You are storing the item names and their costs in 2 different arrays. So, if want to keep your storage structure like that only, you will need to modify the display of result as below:
arr.each_with_index do |item, i|
puts "#{item} costs #{arr2[i]}"
end
But a better approach would be to store all the data in 1 hash instead of 2 arrays.
items = {}
entry = " "
while entry != "q"
print "Enter your item: "
item = gets.chomp
print "Enter the associated cost: "
cost = gets.chomp.to_f
print "Press any key to continue or 'q' to quit: "
entry = gets.chomp
items[item] = cost
end
items.each do |item, cost|
puts "#{item} costs #{cost}"
end
Let me know if it helps.

Ruby loop and array

I've been trying to teach myself Ruby. I've found a few code problems to try solving, but I'm stuck. Here is what I have and the problems I'm trying to solve.
My algorithm is as follows:
Prompt the user to enter a number between 1 and 10 (inclusive).
Read that number into an appropriately named variable.
Test that the number lies in the appropriate range.
Input new number if it is out of bounds as per the condition.
Use the number entered by the user to create an array with that
number of elements.
Write a loop which will run through a number of iterations equal to
the size of the array.
Each time through, prompt the user to enter a text string - a name
(names of cars, that sort of thing).
Once the array is entered, display the contents of the array three
items to a line.
You will want a for loop for this, and within the for loop you should include a decision which will insert a line break at the appropriate places.
Also,
Separate the array elements with dashes - but do not put a dash
before the first element on a line, and do not put a dash after the
last element on a line.
Use a Ruby function to sort the array alphabetically, then display it
again, the same way as before.
Reverse the order of the array
Display its contents a third time, again putting three elements on each line of output and placing dashes the way you did with the first display effort.
loop do
print "Enter an integer between 1 and 10: "
s = gets.chomp.to_i
if s >0 && s <= 10
break
else
puts "Interger entered is outside specified range."
end
end
array=[]
array.size
loop do
print "Enter name of a car model: "
car=gets.chomp
array<<car
for i in array
array.slice(1..9) {|car|
puts car.join(", ")
}
end
end
Is that solution you looking for?
loop do
print "Enter an integer between 1 and 10: "
s = gets.chomp.to_i
if (1..10).include?(s)
arr = [""] * s
i = 0
while i < arr.length
print "Enter name of a car model: "
car = gets.chomp
arr[i] = car
i += 1
end
puts arr.join(", ")
break
else
puts "Interger entered is outside specified range."
break
end
end
Result is:
[retgoat#iMac-Roman ~/temp]$ ruby loop.rb
Enter an integer between 1 and 10: 2
Enter name of a car model: car_a
Enter name of a car model: car_b
car_a, car_b
UPDATE
Below solution to print an array by 3 elements per line with natural sorting
loop do
print "Enter an integer between 1 and 10: "
s = gets.chomp.to_i
if (1..10).include?(s)
arr = [""] * s
i = 0
while i < arr.length
print "Enter name of a car model: "
car = gets.chomp
arr[i] = car
i += 1
end
puts arr.sort.each_slice(3){ |e| puts "#{e.join(", ")}\n"}
break
else
puts "Interger entered is outside specified range."
break
end
end
Result is:
[retgoat#iMac-Roman ~/temp]$ ruby loop.rb
Enter an integer between 1 and 10: 4
Enter name of a car model: z
Enter name of a car model: a
Enter name of a car model: x
Enter name of a car model: b
a, b, x
z
And reverse sorting:
loop do
print "Enter an integer between 1 and 10: "
s = gets.chomp.to_i
if (1..10).include?(s)
arr = [""] * s
i = 0
while i < arr.length
print "Enter name of a car model: "
car = gets.chomp
arr[i] = car
i += 1
end
puts arr.sort{ |x, y| y <=> x }.each_slice(3){ |e| puts "#{e.join(", ")}\n"}
break
else
puts "Interger entered is outside specified range."
break
end
end
Result is:
[retgoat#iMac-Roman ~/temp]$ ruby loop.rb
Enter an integer between 1 and 10: 4
Enter name of a car model: z
Enter name of a car model: a
Enter name of a car model: x
Enter name of a car model: b
z, x, b
a
It's better to split you program into small pieces. Also, try not to use loop without necessity.
# Specify Exception class for your context
class ValidationException < RuntimeError
end
def number_of_cars_from_input
# Get user input
print 'Enter an integer between 1 and 10: '
number = gets.chomp.to_i
# Validate input for your requirements
unless (1..10).cover?(number)
raise ValidationException, 'Interger entered is outside specified range.'
end
number
rescue ValidationException => err
# Print exception and retry current method
puts err
retry
end
# Get car name from user input
def car_from_input
print 'Enter name of a car model: '
gets.chomp
end
# Create array with size equal to number from imput and fill it with cars
array_of_cars = Array.new(number_of_cars_from_input) { car_from_input }
# Separate cars in groups by 3 and join groups
puts array_of_cars.each_slice(3).map { |a| a.join(', ') }

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