I have a hash here:
VALID_CHOICES = {
'r' => 'rock',
'p' => 'paper',
'sc' => 'scissors',
'l' => 'lizard',
'sp' => 'spock'
}
And a method which basically compares here:
def win?(first, second)
(first == 'sc' && second == 'p') ||
(first == 'p' && second == 'r') ||
(first == 'r' && second == 'l') ||
(first == 'l' && second == 'sp') ||
(first == 'sp' && second == 'sc') ||
(first == 'sc' && second == 'l') ||
(first == 'l' && second == 'p') ||
(first == 'p' && second == 'sp') ||
(first == 'sp' && second == 'r') ||
(first == 'r' && second == 'sc')
end
How can I rewrite my method in very short concise code that means exactly the same thing? Any idea? Is it possible to do it using hashes?
You should define clear rules for what each token can win:
WINS = {
'r' => %w{l sc},
'p' => %w{r sp},
'sc' => %w{p l},
'l' => %w{p sp},
'sp' => %w{r sc}
}
Now you can determine wins using a simple lookup:
def win?(first, second)
WINS[first].include?(second)
end
While there may be several 'clever' ways to avoid an explicit structure like WINS, explicit rules are much more understandable - and therefore, more maintainable. Conciseness in code is considered a positive attribute where it improves the readability of the code. Conciseness to the extreme that causes the code to be difficult to understand is not something to strive for.
In addition to user2864740's comment and Cary Swoveland's explanation, you could also use a hash to map "winning pairs" to their respective verb:
WINS = {
%w[scissors paper] => 'cuts',
%w[paper rock] => 'covers',
%w[rock lizard] => 'crushes',
%w[lizard spock] => 'poisons',
%w[spock scissors] => 'smashes',
%w[scissors lizard] => 'decapitates',
%w[lizard paper] => 'eats',
%w[paper spock] => 'disproves',
%w[spock rock] => 'vaporizes',
%w[rock scissors] => 'crushes'
}
It returns the corresponding verb if the key's first item beats the second:
WINS[['paper', 'rock']] #=> "covers"
and nil if it doesn't:
WINS[['rock', 'paper']] #=> nil
In your method:
def win?(first, second)
WINS.has_key?([first, second])
end
Or to check both sides:
if WINS.has_key?([first, second])
# first wins
elsif WINS.has_key?([second, first])
# second wins
else
# tie
end
Or more verbose:
def result(first, second)
if verb = WINS[[first, second]]
"first wins: #{first} #{verb} #{second}"
elsif verb = WINS[[second, first]]
"second wins: #{second} #{verb} #{first}"
else
"tie"
end
end
result('rock', 'scissors')
#=> "first wins: rock crushes scissors"
result('spock', 'lizard')
#=> "second wins: lizard poisons spock"
result('paper', 'paper')
#=> "tie"
Of course, you can also use your abbreviations (sc, p, r, l, sp) instead of whole words.
Related
I am having trouble using || ("or").
This is the first time I select using the "or" feature and I have been trying to select the words that are greater than 6 characters long OR start with an "e". I tried everything but I keep getting just one feature or an "and". This is the code so far
def strange_words(words)
selected_words = []
i = 0
while i < words.length
word = words[i]
if word.length < 6
selected_words << word
end
i += 1
end
return selected_words
end
print strange_words(["taco", "eggs", "we", "eatihhg", "for", "dinner"])
puts
print strange_words(["keep", "coding"])
Using the || operator is the same as writing multiple if statements. Let's use a silly example to demonstrate it. Say you wanted to determine if a word started with the letter 'e'. Well there are a few forms of 'e'. There is the lowercase e and the upppercase E. You want to check for both forms so you could do something like this:
def starts_with_e?(string)
result = false
if string[0] == 'e'
result = true
end
if string[0] == 'E'
result = true
end
result
end
Notice however that you're doing the same actions after checking for the condition. This means you could simplify this code using the OR/|| operator, like such:
def starts_with_e?(string)
result = false
if string[0] == 'e' || string[0] == 'E'
result = true
end
end
For your specific question, you can do the following:
def strange_words(words)
words.select { |word| word.length < 6 || word[0] == 'e' }
end
When you run with your example, it gives you this output:
> strange_words(["taco", "eggs", "we", "eatihhg", "for", "dinner"])
=> ["taco", "eggs", "we", "eatihhg", "for"]
This is still not good code. You'll want to protect the methods from bad input.
I've seen the solution and it more or less matches
Write a method that takes a string and returns the number of vowels
in the string. You may assume that all the letters are lower cased. You can treat "y" as a consonant.
Difficulty: easy.
def count_vowels(string)
vowel = 0
i = 0
while i < string.length
if (string[i]=="a" || string[i]=="e" || string[i]=="i" || string[i]=="o"|| string[i]=="u")
vowel +=1
end
i +=1
return vowel
end
puts("count_vowels(\"abcd\") == 1: #{count_vowels("abcd") == 1}")
puts("count_vowels(\"color\") == 2: #{count_vowels("color") == 2}")
puts("count_vowels(\"colour\") == 3: #{count_vowels("colour") == 3}")
puts("count_vowels(\"cecilia\") == 4: #{count_vowels("cecilia") == 4}")
def count_vowels(str)
str.scan(/[aeoui]/).count
end
/[aeoui]/ is a regular expression that basically means "Any of these characters: a, e, o, u, i". The String#scan method returns all matches of a regular expression in the string.
def count_vowels(str)
str.count("aeoui")
end
Your function is fine you are just missing a keyword end to close of your while loop
def count_vowels(string)
vowel = 0
i = 0
while i < string.length
if (string[i]=="a" || string[i]=="e" || string[i]=="i" || string[i]=="o"|| string[i]=="u")
vowel +=1
end
i +=1
end
return vowel
end
puts("count_vowels(\"abcd\") == 1: #{count_vowels("abcd") == 1}")
puts("count_vowels(\"color\") == 2: #{count_vowels("color") == 2}")
puts("count_vowels(\"colour\") == 3: #{count_vowels("colour") == 3}")
puts("count_vowels(\"cecilia\") == 4: #{count_vowels("cecilia") == 4}")
#=> count_vowels("abcd") == 1: true
#=> count_vowels("color") == 2: true
#=> count_vowels("colour") == 3: true
#=> count_vowels("cecilia") == 4: true
I think using HashTable data structure would be good way to go for this particular problem. Especially if you're required to output number of every single vowel separately.
Here is the code I'd use:
def vowels(string)
found_vowels = Hash.new(0)
string.split("").each do |char|
case char.downcase
when 'a'
found_vowels['a']+=1
when 'e'
found_vowels['e']+=1
when 'i'
found_vowels['i']+=1
when 'o'
found_vowels['o']+=1
when 'u'
found_vowels['u']+=1
end
end
found_vowels
end
p vowels("aeiou")
Or even this (elegant but not necessarily performant):
def elegant_vowels(string)
found_vowels = Hash.new(0)
string.split("").each do |char|
case char.downcase
when ->(n) { ['a','e','i','o','u'].include?(n) }
found_vowels[char]+=1
end
end
found_vowels
end
p elegant_vowels("aeiou")
which would output:
{"a"=>1, "e"=>1, "i"=>1, "o"=>1, "u"=>1}
So you don't have to turn the string into an array and worry about case sensitivity:
def getVowelCount(string)
string.downcase.count 'aeiou'
end
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
I am struggling finding a way to verify these methods and was wondering if anyone knew a basic way to do this?
class PigLatinTest < MiniTest::Unit::TestCase
def test_word_beginning_with_a
assert_equal "appleay", PigLatin.translate("apple")
end
def test_other_word_beginning_e
assert_equal "earay", PigLatin.translate("ear")
end
def test_word_beginning_with_p
assert_equal "igpay", PigLatin.translate("pig")
end
For example the first one might be:
module PigLatin
class Word
def initialize(word)
#word = word.to_s
end
# remember to use the .to_s method
def translate(word)
if word[0] == "a" || "e" || "o" || "u" || "i"
word = word + "ay"
elsif word[0] != "a" || "e" || "o" || "u" || "i"
word = word-word[0]+"ay"
end
end
end
# you can add method here even outside of the class ...
end
------------in another file
module PigLatin
class Word
# remember to use the .to_s method
end
# you can add method here even outside of the class ...
end
Your translate method won't work. The problem is here:
if word[0] == "a" || "e" || "o" || "u" || "i"
and
elsif word[0] != "a" || "e" || "o" || "u" || "i"
You can't compare that way as the right side of either will not do what you think it will.
Some simple checks will show why there's something wrong:
'abc'[0] == "a" || "e" || "o" || "u" || "i" # => true
'efg'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'opq'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'xyz'[0] == "a" || "e" || "o" || "u" || "i" # => "e"
'abc'[0] != "a" || "e" || "o" || "u" || "i" # => "e"
'efg'[0] != "a" || "e" || "o" || "u" || "i" # => true
'opq'[0] != "a" || "e" || "o" || "u" || "i" # => true
'xyz'[0] != "a" || "e" || "o" || "u" || "i" # => true
Why are those wrong? Let's look at what's happening:
When the word starts with 'a', the test 'a' == 'a' is true:
'abc'[0] == "a" # => true
If we || ("or") true and something, we get true back because it was the first "true" value seen:
true || "e" # => true
If the first test failed, then || causes the second test to be evaluated, which in your code was "e", and wasn't a test, but Ruby didn't know that, and thought it was a "true" return value so it became the result of the expression:
false || "e" # => "e"
Knowing that, a correct way to write this would be:
'abc'[0] == "a" || 'abc'[0] == "e" || 'abc'[0] == "o" || 'abc'[0] == "u" || 'abc'[0] == "i" # => true
'efg'[0] == "a" || 'efg'[0] == "e" || 'efg'[0] == "o" || 'efg'[0] == "u" || 'efg'[0] == "i" # => true
'opq'[0] == "a" || 'opq'[0] == "e" || 'opq'[0] == "o" || 'opq'[0] == "u" || 'opq'[0] == "i" # => true
'xyz'[0] == "a" || 'xyz'[0] == "e" || 'xyz'[0] == "o" || 'xyz'[0] == "u" || 'xyz'[0] == "i" # => false
'abc'[0] != "a" && 'abc'[0] != "e" && 'abc'[0] != "o" && 'abc'[0] != "u" && 'abc'[0] != "i" # => false
'efg'[0] != "a" && 'efg'[0] != "e" && 'efg'[0] != "o" && 'efg'[0] != "u" && 'efg'[0] != "i" # => false
'opq'[0] != "a" && 'opq'[0] != "e" && 'opq'[0] != "o" && 'opq'[0] != "u" && 'opq'[0] != "i" # => false
'xyz'[0] != "a" && 'xyz'[0] != "e" && 'xyz'[0] != "o" && 'xyz'[0] != "u" && 'xyz'[0] != "i" # => true
however, that rapidly becomes hard to read and unwieldy, so something more concise is needed:
%w[a e o u].include? 'abc'[0] # => true
%w[a e o u].include? 'efg'[0] # => true
%w[a e o u].include? 'opq'[0] # => true
%w[a e o u].include? 'xyz'[0] # => false
!%w[a e o u].include? 'abc'[0] # => false
!%w[a e o u].include? 'efg'[0] # => false
!%w[a e o u].include? 'opq'[0] # => false
!%w[a e o u].include? 'xyz'[0] # => true
There is a problem with this though; As the array size increases, more loops are required to compare to the [0] value, which slows the code unnecessarily. A regular expression, written correctly, can get rid of that looping so the speed stays very constant:
'abc'[0][/[aeou]/] # => "a"
'efg'[0][/[aeou]/] # => "e"
'opq'[0][/[aeou]/] # => "o"
'xyz'[0][/[aeou]/] # => nil
Notice though, that instead of getting true/false, the results are the character matched by the pattern or nil. In Ruby, only nil and false are considered false values, and everything else is true, so we can translate those into true, true, true, false respectively, but by taking advantage of the ! operator we can make it even more clear:
!!'abc'[0][/[aeou]/] # => true
!!'efg'[0][/[aeou]/] # => true
!!'opq'[0][/[aeou]/] # => true
!!'xyz'[0][/[aeou]/] # => false
It might seem that we'd have to use !!! to "not" the results like we'd want when using !=, but that isn't necessary. A single ! will do the same thing:
!'abc'[0][/[aeou]/] # => false
!'efg'[0][/[aeou]/] # => false
!'opq'[0][/[aeou]/] # => false
!'xyz'[0][/[aeou]/] # => true
But wait! There's more! Even that can be improved upon a slight amount by removing the string slice ([0]) and using a regex anchor. Compare these two, and their benchmark:
require 'fruity'
ALPHABET = ('a'..'z').to_a.join
compare do
slice_it { ALPHABET[0][/[aeou]/] }
regex_it { ALPHABET[/^[aeou]/] }
end
# >> Running each test 8192 times. Test will take about 1 second.
# >> regex_it is faster than slice_it by 39.99999999999999% ± 10.0%
So, using something like:
'abc'[/^[aeou]/] # => "a"
!'abc'[/^[aeou]/] # => false
!!'abc'[/^[aeou]/] # => true
will be fast and compact and let you test to see what a string starts with.
This is my code:
class RockPaperScissors
# Exceptions this class can raise:
class NoSuchStrategyError < StandardError
end
def self.winner(player1, player2)
if ((player1[1] == 'R') && (player2[1] == 'S') ||
(player1[1] == 'S') && (player2[1] == 'P') ||
(player1[1] == 'P') && (player2[1] == 'R'))
return player1
elsif ((player1[1] == 'R') && (player2[1] == 'P') ||
(player1[1] == 'S') && (player2[1] == 'R') ||
(player1[1] == 'P') && (player2[1] == 'S'))
return player2
elsif ((player1[1] == 'R') && (player2[1] == 'R') ||
(player1[1] == 'S') && (player2[1] == 'S') ||
(player1[1] == 'P') && (player2[1] == 'P'))
return player1
end
end
def self.tournament_winner(tournament)
player1 = Array.new
player2 = Array.new
nextround = Array.new
while tournament.length != 1 do
tournament.each_with_index {|item, index|
if (index%2!=0)
player2[0] = item[0]
player2[1] = item[1]
elsif (index%2 ==0)
player1[0] = item[0]
player1[1] = item[1]
else
puts 'bananas'
end
if (index%2!=0)
nextround[(index-1)/2] = winner(player1, player2)
end
}
tournament=nextround
end
return tournament
end
end
RockPaperScissors.tournament_winner([["num1", "R"], ["num2", "P"], ["num3", "P"], ["num4", "R"]])
Well, the last line is an execution launch. This code makes a tournament of rock, paper scissors. It takes as input an array of arrays with each character and its attack, and it has to return the array with the champion and its attack.
The tournament is num1 vs num2 (num2 wins), and num3 vs num4 (num3 wins). Then the final is Num2 vs Num3, and in this stablemate wins the first guy in the array (Num2).
It seems overcomplicated because the code has to work with any number of characters, as long as their number is base2 (2, 4, 8, 16 characters..., etc).
My problem is next (debug the code and you will see). When it changes the value of the array 'Player1' or 'Player2', it also changes the value in the array 'nextround', even if it is not in that line!
That is not suppose to happen!
By the way, I am learning Ruby so it may be a really stupid failure.
why does this need to be true?
"It seems overcomplicated because the code has to work with any number of characters, as long as their number is base2 (2, 4, 8, 16 characters..., etc)."
Rather having player1 and player2 be arrays, I would rewrite them to be instances of a class Player. Then write methods in the Player class so you can call player1.hand and it returns 'S' || 'R' || 'P'
that way you can store how many wins a player has on the player object,
things I'd look into learning more about
case/when statements
the special initialize method
attrs_accessor (used for making data accessible across classes)
modules
also i've seen it done and i may be wrong on this, but generally you don't put classes inside classes.
I'm supposed to create a method in ruby that will take in a structured,multi-dimensioned array, such as:
my_arr = [
[ [['Armando', 'P'], ['Dave', 'S']], [['Richard', 'R'], ['Michael', 'S']] ],
[ [['Allen', 'S'], ['Omer', 'P']], [['David E.', 'R'], ['Richard X.', 'P']] ]
]
This array is supposed to represent a tournament of Rock, paper & scissors, the number of players will always be 2^n and no repetitions (of players) are made.
The code I wrote is as follows:
class WrongNumberOfPlayersError < StandardError ; end
class NoSuchStrategyError < StandardError ; end
def rps_game_winner(game)
raise WrongNumberOfPlayersError unless game.length == 2
valid = ["r","p","s"]
a1=[(game[0][1]).downcase]
a2=[(game[1][1]).downcase]
raise NoSuchStrategyError unless (valid & a1) && (valid & a2)
return (game[0]) if a1 === a2
case (a1[0])
when "r"
case (a2[0])
when "p"
return (game[1])
else
return (game[0])
end
when "p"
case (a2[0])
when "s"
return (game[1])
else
return (game[0])
end
when "s"
case (a2[0])
when "r"
return (game[1])
else
return (game[0])
end
end
end
def rps_tournament_winner(tournament)
if tournament[0][0].is_a?(Array)
rps_tournament_winner(tournament[0])
elsif tournament[1][0].is_a?(Array)
rps_tournament_winner(tournament[1])
else
rps_game_winner(tournament)
end
end
So my problem is that given the use of array i mentioned earlier being passed to rps_tournament_winner Dave always wins instead of Richard and i haven't been able to figure out where i went wrong.
Ty for reading the wall of text/code :)
One thing I noticed is that your use of 'valid' doesn't do anything to check if your input is actually valid. If you're trying to check that a1 and a2 are either "r" "p" or "s" you should use a regular expression:
valid = /[rps]/ # matches either "r" "p" or "s"
raise NoSuchStrategyError unless (a1 =~ valid) && (a2 =~ valid)
Your array of players is nested very deep. You'll make your life simpler by thinning it out:
my_arr = [['Armando', 'P'], ['Dave', 'S'], ['Richard', 'R'], ['Michael', 'S'],
['Allen', 'S'], ['Omer', 'P'], ['David E.', 'R'], ['Richard X.', 'P']]
You can make it easier to read and maintain by breaking your program into parts. For example, create a method for determining a win:
# This is incomplete as it doesn't deal with ties. I'll let you do that part
def win(first, second)
if (first == "p" && second == "r") || (first == "r" && second == "s") || (first == "s" && second == "p")
return true
else
return false
end
end
Now it's easier to write and understand the game itself, using the above method:
def rps_game_winner(player1, player2)
first = player1[1].downcase
second = player2[1].downcase
win(first, second) ? player1 : player2 # ternary operator returns the winner
end
You now have a method to put all this into play (sort of the main logic), and we'll use recursion here:
def rps_tournament_winner(player_list)
round_winners = [] # place to hold the winners for each round
if player_list.size == 1 # final condition to stop the recursion
puts "The winner is #{player_list[0][0]}."
else
player_list.each_slice(2) do |l1, l2| # take pairs from your list to play each other
round_winners << rps_game_winner(l1, l2)
end
rps_tournament_winner(round_winners) # use recursion to play the winners against each other
end
end
# put it into play
puts test_array(my_arr)
That's it. The winner is Richard and it'll always be Richard because the play is deterministic.
While this will run, you should know that I left out some important things like dealing with ties and odd numbers of players. The program will not work right under these conditions. But I'll leave it up to you to solve those parts.
EDIT: Modifying the original array:
new_array = []
my_arr.flatten.each_slice(2) do |name, pick|
new_array << [name, pick]
end
Your recursion only goes down tournament[0] before ending never tournament[1].
You need to play both tournament[0] and tournament[1] then play them against each other.
Something like. ( I leave it to you write the ruby code)
rps_game_winner( [rps_tournament_winner(tournament[0], rps_tournament_winner(tournament[1]] )
It's very hard to debug your code.
But I think that it is fundamentally flawed.
When you call rps_tournament_winner you pass in
[
[ [['Armando', 'P'], ['Dave', 'S']], [['Richard', 'R'], ['Michael', 'S']] ],
[ [['Allen', 'S'], ['Omer', 'P']], [['David E.', 'R'], ['Richard X.', 'P']] ]
]
It asks tournament[0][0].is_a?(Array)
[['Armando', 'P'], ['Dave', 'S']], [['Richard', 'R'], ['Michael', 'S']]
Yes it is.
It calls rps_tournament_winner(tournament[0])
tournament = [ [['Armando', 'P'], ['Dave', 'S']], [['Richard', 'R'], ['Michael', 'S']] ]
It asks tournament[0][0].is_a?(Array)
['Armando', 'P']
Yes it is.
it calls rps_tournament_winner(tournament[0])
tournament = [['Armando', 'P'], ['Dave', 'S']]
It asks tournament[0][0].is_a?(Array)
'Armando'
No its not.
It calls rps_game_winner(tournament)
And in the game Dave beats Armando!
I don't think this is the functionality you intended.
I suggest you rewrite this, trying to keep things simple.