Ruby language statement with regex
I'm working on my second ruby program and I'm literally stuck with the last thing between me and the finished program. My task is to write a Rock, Paper, Scissors method that returns the winning player. This requires a game argument of the form [["Dave", "S"], ["Dan", "R"]], where "S" and "R" are "Scissors" and "Rock", respectively. It then determines the winner and returns an array containing the winning strategy. Also raises errors if the game is of wrong length or strategy == or not in range.
class WrongNumberOfPlayersError < StandardError ; end
class NoSuchStrategyError < StandardError ; end
def rps_game_winner(game)
raise WrongNumberOfPlayersError unless game.length == 2
#Hash of whose Keys are the strategies and values are the players
playr = {(game[0].slice(1).downcase) => game[0],
(game[1].slice(1).downcase) => game[1]} #This fits on the above line in IRB
#Collect Strategies in array for comparison
stgys = playr.keys
#raise NoSuchStrategyError unless players give strategies in range, no duplicates
raise NoSuchStrategyError unless (stgys.select { |s| s.match /[prs]/ }.size == 2)
#determine Winner
case stgys.to_s
when /p/ && /r/
playr["p"]
when /p/ && /s/
playr["s"]
when /s/ && /r/
playr["r"]
end
end
This works as I expect, tests strategies against regex and returns a winner. Except for the last case, where the counter always returns nil. If I call player ["r"] under any of the others when it succeeds and it returns the correct player in "/ p / & & / r /". If I change the order it still doesn't work, so I know it is not related to its position. The regex / r / evaluates when needed if I make a separate match call outside of the case statement. So I believe I have narrowed it down to do something with / s / and / r /, but otherwise I'm stumped. Any help with DRYness is also appreciated, thanks for the help!
source to share
You don't really need to case
at all. My version of the solution for this task:
def rps_game_winner(game)
raise WrongNumberOfPlayersError unless game.length == 2
raise NoSuchStrategyError if game.any? {|n| !(n[1] =~ /^[spr]$/i)}
loose_hash = {'s' => 'r', 'p' => 's', 'r' => 'p'}
strategy1 = game[0][1].downcase
strategy2 = game[1][1].downcase
loose_hash[strategy1] == strategy2 ? game[1] : game[0]
end
source to share
The problem is with your format /X/ && /X/
. Ruby will not interpret this to require that both regular expressions match. I'm not sure what, but I believe it will treat it the same way when /p/, /r/
, which will be true if or regex matches . When you are testing game = [["Dave","S"], ["Dan","R"]]
, "r"
matches the first argument of case and you are trying to reference playr["p"]
.
Try this instead:
case stgys.to_S
when "pr", "rp"
playr["p"]
when "ps", "sp"
playr["s"]
when "sr", "rs"
playr["r"]
end
source to share
Your problem is what is being /p/ && /r/
evaluated before actually being used as a label. Since none of them are false or null, /p/ && /r/
is equal /r/
and in a similar way for your other case labels.
Unless you rewrite this to have one case argument for each case, it case
just doesn't seem like a good fit for what you are doing to me
source to share
Using the instructions in the problem, I solved the problem in a different way.
def rps_game_winner(game)
raise WrongNumberOfPlayersError unless game.length == 2
raise NoSuchStrategyError unless game[0].length == 2 and game[1].length == 2
raise NoSuchStrategyError unless game[0][1] =~ /^[RPS]$/ and game[1][1] =~ /^[RPS]$/
return game[0] unless (game[0][1] == "P" and game[1][1] == "S") or
(game[0][1] == "S" and game[1][1] == "R") or
(game[0][1] == "R" and game[1][1] == "P")
return game[1]
end
def rps_tournament_winner(tournament)
return rps_game_winner(tournament) unless tournament[0][1] !~ /^[RPS]$/
return rps_tournament_winner([rps_tournament_winner(tournament[0]), rps_tournament_winner(tournament[1])])
end
I tested all given scenarios and it worked for me.
source to share