DRYing up Rock Paper Scissors

左心房为你撑大大i 提交于 2019-12-02 04:16:34

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.

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"

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
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!