HW impossibility?: Create a rock paper scissors program in ruby WITHOUT using conditionals
Here's one only using hashes:
RULES = {
:rock => {:rock => :draw, :paper => :paper, :scissors => :rock},
:paper => {:rock => :paper, :paper => :draw, :scissors => :scissors},
:scissors => {:rock => :rock, :paper => :scissors, :scissors => :draw}
}
def play(p1, p2)
RULES[p1][p2]
end
puts play(:rock, :paper) # :paper
puts play(:scissors, :rock) # :rock
puts play(:scissors, :scissors) # :draw
Learning ruby, what do these snippets of code mean?
I admit, this is not the most readable code for a novice programmer. I rewrote it, extracted some variables and added comments. Hope you can understand it better now.
def winner(p1, p2)
# rules of dominance, who wins over who
wins = {
rock: :scissors,
scissors: :paper,
paper: :rock
}
# this hash is here to bypass restriction on using conditional operators
# without it, the code would probably look like
# if condition
# p1
# else
# p2
# end
answers = {
true => p1,
false => p2
}
# given the choice of first player, which element can he beat?
element_dominated_by_first_player = wins[p1]
# did the second player select the element that first player can beat?
did_player_one_win = element_dominated_by_first_player == p2
# pick a winner from our answers hash
answers[did_player_one_win]
end
winner(:rock, :scissors) # => :rock
winner(:rock, :paper) # => :paper
winner(:scissors, :paper) # => :scissors
ruby: Rock Scissors Paper program. How to make algorithm function properly?
def judge(p1, p2)
case
when @wins[p1] == p2 then "Win"
when @wins[p2] == p1 then "Lose"
else "Draw"
end
end
Is there a way to include an 'OR' statement in this method? (Ruby) Rock, Paper, Scissors
(x == y && z == a) || (x == b && z == y)
and so on should do the trick?
If-less programming (basically without conditionals)
There are some resources on the Anti-IF Campaign site, such as this article.
I believe it's a matter of degree. Conditionals aren't always bad, but they can be (and frequently are) abused.
Additional thoughts (one day later)
Refactoring: Improving the Design of Existing Code is a good reference on this subject (and many others). It covers Replace Conditional with Polymorphism. There's also a new one, Replace Conditional with Visitor, on the web site.
I value simplicity and single responsibility over removing all if
statements. These three goals often coincide. Static analysis tools that support the cyclomatic complexity metric can quickly point out code with nested or serial conditionals. The if
statements may remain post-refactoring, but could be broken out into smaller methods and/or multiple classes.
Update: Michael Feathers wrote an article on Unconditional Programming.
This is a popular topic: Phil Haack on Death to the IF statement!
Call * method on implicit receiver
That's not possible. There are some methods that simply cannot be called without an explicit receiver because they would otherwise be ambiguous. In particular, in every case where you have both a unary prefix and a binary infix operator, it would be impossible to distinguish the two cases:
+ a == a.+@()
- a == a.-@()
& a # interpreted as the unary prefix proc-block conversion operator
* a # interpreted as the unary prefix splat operator
or in cases where the operator conflicts with other syntax:/ a # interpreted as the start of a Regexp literal
For consistency, Ruby forces the use of two operands for all binary infix operators, not just the ones for which the grammar would be ambiguous.Likewise, []
cannot be called without an explicit receiver because it would conflict with literal syntax for arrays.
The other well-known example of methods that cannot be called without an explicit receiver, is setters:
self.foo = bar # setter
foo = bar # local variable
For private
setters, there is actually an exception to the rule for private
methods, that allows them to be called with an explicit receiver, as long as that explicit receiver is the literal pseudo-variable keyword self
. However, no such exception exists for calling private binary operators or methods like []
. (There are proposals to that effect, though.) Manipulating arrays with recursive function
Several problems with your original code:
- You mix iteration with recursion
- You throw away the result of the recursion (
rps_tournament_winner(tournament[i])
) - You don't return anything useful (
return
simply returnsnil
, but it doesn't matter, because you don't use the result anyways)
def rps_tournament_winner(tournament)
result = []
for i in 0..1 do
if tournament[i][0][0].is_a? Array then
result << rps_tournament_winner(tournament[i])
else
result << rps_game_winner(tournament[i])
end
end
return result
end
But actually that's equivalent to a simpledef rps_tournament_winner(tournament)
if tournament[0][0].is_a? String
rps_game_winner(tournament)
else
tournament.map { |t| rps_tournament_winner(t) }
end
end
And it would be so much nicer using objects:class Game < Struct.new(:player1, :choice1, :player2, :choice2)
def winner
'PRSP'.include?(choice1 + choice2) ? player1 : player2
end
end
def rps_tournament_winner(tourn)
return tourn.winner if tourn.is_a? Game
tourn.map { |t| rps_tournament_winner(t) }
end
Related Topics
Warning While Installing The Rails Plugin
How to Scrape a Website with The Socksify Gem (Proxy)
Waiting for Ruby Child Pid to Exit
Reading Files in a Zip Archive, Without Unzipping The Archive
Accessing Microsoft Exchange Server from Ruby
Without Converting to a String, How Many Digits Does a Fixnum Have
How to Change "3 Errors Prohibited This Foobar from Being Saved" Validation Message in Rails
Ruby - Append Content at The End of The Existing S3 File Using Fog
What Is Returned in Ruby If The Last Statement Evaluated Is an If Statement
How to Add Usr/Local/Bin to Path Environment Variable on Ubuntu 12.0.4
Gem Install Rmagick Fails on Os X El Capitan
When Is The Enumerator::Yielder#Yield Method Useful
Undefined Method 'Merge' for '####':String <%= Form_For %> Helper
How to Remove Backslashes from a JSON String
Outputting Stdout to a File and Back Again
Define_Method with Predefined Keyword Arguments
Script Executes Successfully in Commandline But Not as a Cronjob