DRYing up Rock Paper Scissors

北慕城南 提交于 2019-12-20 05:31:15

问题


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 would be great. Is there any way to simplify the if else statements in each case? Also, is there any alternative way to the case statement skipped instead of making almost the entirety of the code one big if else statement?

def rps(roll)
    roll_ops = ["rock", "paper", "scissors"]
    pick = roll_ops.sample
    result = nil
    if roll == pick
        result = "tie"
    else
    case roll
    when "scissors" then
        if pick == "paper"
            result = "win"
        else
            result = "lose"
        end
    when "rock" then
        if pick == "scissors"
            result = "win"
        else
            result = "lose"
        end
    when "paper" then
        if pick == "rock"
            result = "win"
        else
            result = "lose"
        end
    else
        puts "Please input rock paper or scissors"
    end
    end

    puts "#{pick}, #{result}"
end

rps("scissors")

回答1:


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.




回答2:


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



回答3:


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"



回答4:


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


来源:https://stackoverflow.com/questions/32105234/drying-up-rock-paper-scissors

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