I\'m a novice ruby programmer and although this code works, I\'m wondering how I can improve it. I have very limited knowledge on lambdas and procs and the like, but any advice
You can build a hash that contains the pick and which option lose against that pick:
hash = {'scissors' => 'paper', 'rock' => 'scissors', 'paper' => 'rock'}
Then you check if the machine pick is the same like you did:
roll_ops = ["rock", "paper", "scissors"]
pick = roll_ops.sample
if roll == pick
And the win/lose condition becomes something like this:
if hash[roll] == pick
"win"
else
"lose"
end
Nice and clean with just 2 conditions.
I think a proc or other similar setup is probably overkill. Just use inline-ifs:
def rps(roll)
raise "Choose rock, paper, or scissors" if roll.nil?
roll_ops = ["rock", "paper", "scissors"]
pick = roll_ops.sample
result = if roll == pick
"tie"
else
case roll
when "scissors"
pick == "paper" ? 'win' : 'lose'
when "rock"
pick == "scissors" ? 'win' : 'lose'
when "paper" then
pick == "rock" ? 'win' : 'lose'
end
end
puts "#{pick}, #{result}"
end
rps("scissors")
I removed your extra else that was supposed to handle non-input. Better to use errors in such a case.
There are a couple tricks here:
1- The inline-ifs. Those should be pretty clear.
2- The result variable is set equal to the return value of the if expression. This is a handy trick that you can use because in Ruby, everything is an expression!
If you are interested in using a lambda for this, it should work pretty well too:
def rps(roll)
raise "Choose rock, paper, or scissors" if roll.nil?
roll_ops = ["rock", "paper", "scissors"]
pick = roll_ops.sample
did_win = lambda do |choice|
return choice == pick ? 'win' : 'lose'
end
result = if roll == pick
"tie"
else
case roll
when "scissors"
did_win.call('paper')
when "rock"
did_win.call('scissors')
when "paper" then
did_win.call('rock')
end
end
puts "#{pick}, #{result}"
end
ROLL_OPS = %w[rock paper scissors]
RESULTS = %w[tie lose win]
def rps(roll)
unless i = ROLL_OPS.index(roll)
return puts "Please input rock paper or scissors".freeze
end
pick = ROLL_OPS.sample
puts "#{pick}, #{RESULTS[(i - ROLL_OPS.index(pick)) % 3]}"
end
Here are a couple of ways:
#1 Use Array#cycle
OPS = %w[rock paper scissors]
def rps(roll)
pick = OPS.sample
enum = OPS.cycle
(prev = enum.next) until enum.peek == roll
return [pick, "lose"] if prev == pick
enum.next
return [pick, "win"] if enum.peek == pick
[pick, "tie"]
end
rps "scissors" #=> ["scissors", "tie"]
rps "scissors" #=> ["scissors", "tie"]
rps "scissors" #=> ["rock", "win"]
rps "scissors" #=> ["paper", "lose"]
#2 Take @MurifoX's answer one step farther
def rps(roll)
roll_ops = %w|rock paper scissors|
h = (roll_ops + [roll_ops.first]).each_cons(2).
with_object(Hash.new("tie")) { |(a,b),h| h[[a,b]]="lose"; h[[b,a]]="win" }
pick = roll_ops.sample
[pick, h[[roll,pick]]]
end
rps "scissors" #=> ["rock", "lose"]
rps "scissors" #=> ["scissors", "tie"]
rps "scissors" #=> ["paper", "win"]
Here:
h #=> {["rock", "paper"]=>"lose", ["paper", "rock"]=>"win",
# ["paper", "scissors"]=>"lose", ["scissors", "paper"]=>"win",
# ["scissors", "rock"]=>"lose", ["rock", "scissors"]=>"win"}
and because of the default value "tie":
h[["rock", "rock"]] #=> "tie"
h[["paper", "paper"]] #=> "tie"
h[["scissors", "scissors"]] #=> "tie"