Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Trying to randomise a game of rock, paper, scissors in R

Tags:

r

I have created an RPS function that randomises the computer's move for one turn. For example, if you type in rps("rock"), the function will randomly spit out either rock, paper, scissors along with the result (win,loss,tie).

rps = function(move){
  options = c("rock", "paper", "scissors")
  comp.move = sample(options, size = 1)
  if(move == "rock" & comp.move == "rock"){
    names(comp.move) = "tie"
}else
if(move == "rock" & comp.move == "scissors"){
    names(comp.move) = "loss"
}else
if(move == "rock" & comp.move == "paper"){
    names(comp.move) = "win"
}else
if(move == "paper" & comp.move == "paper"){
    names(comp.move) = "tie"
}else
if(move == "paper" & comp.move == "scissors"){
    names(comp.move) = "win"
}else
if(move == "paper" & comp.move == "rock"){
    names(comp.move) = "loss"
}else
if(move == "scissors" & comp.move == "scissors"){
    names(comp.move) = "tie"
}else
if(move == "scissors" & comp.move == "rock"){
    names(comp.move) = "win"
}else
if(move == "scissors" & comp.move == "paper"){
    names(comp.move) = "loss"
}
  return(comp.move)
}

Now, I want to play a version of the game where this happens 50 times. I have already created a vector of length 50 that has the prerecorded human moves.

human.move = c("rock", "paper", "scissors", "paper", "paper", "rock", "scissors", "rock", "rock", "paper", "paper", "scissors", "rock", "rock", "paper", "paper", "paper", "scissors", "paper", "rock", "paper", "rock", "rock", "scissors", "scissors", "paper", "rock", "paper", "scissors", "rock", "paper", "paper", "scissors", "rock", "paper", "rock", "paper", "paper", "scissors", "scissors", "paper", "rock", "rock", "scissors", "scissors", "rock", "paper", "scissors", "scissors", "rock")

How do I get the computer to run the rps function on each element in this vector, and give me the results? Currently my function just takes the first element of my human moves vector (which is rock) and only compares its random moves to that.

rps.random=function(move,n=50){
comp.moves = vector("character")

for(i in human.move){
comp.move=rps(move[i])
comp.moves = append(comp.moves,comp.move, after = length(comp.moves))
}
return(comp.moves)
}
like image 813
Harsh Avatar asked Dec 06 '15 02:12

Harsh


1 Answers

Here are a couple of suggestions. First, an update to your function. You can cut down on all those if() statements by using a few "or" statements with the | operator for either win or loss, and the identical() function for a tie. Also, it is my understanding that randomized games such as this one should be played a single round at a time, and replicated for more than one round. Therefore, this function checks for a single-length vector given in move.

rps <- function (move) {
    opts <- c("rock", "paper", "scissors")
    stopifnot(length(move) == 1L, move %in% opts)
    cpu <- sample(opts, 1)
    names(cpu) <- if (identical(move, cpu)) {
        "tie"
    }
    else if (move == "rock" & cpu == "scissors" | move == "paper" & 
        cpu == "rock" | move == "scissors" & cpu == "paper") {
        "win"
    }
    else {
        "loss"
    }
    cpu
}

Here are a couple of example runs -

rps("paper")
#    win 
# "rock" 
rps("rock")
#    loss 
# "paper" 

Now you don't need to manually create a vector of 50 items. We can just replicate the process 50 times by using a sample in the move argument.

replicate(50, rps(sample(c("rock", "paper", "scissors"), 1)))
#        win        win       loss        win       loss        tie        tie 
# "scissors"    "paper"    "paper"    "paper"     "rock"    "paper" "scissors" 
#        win       loss       loss        tie       loss        win        win 
#     "rock"    "paper"    "paper"    "paper"    "paper" "scissors" "scissors" 
#        tie       loss        tie        win        win        tie       loss 
# "scissors"     "rock" "scissors"    "paper"    "paper" "scissors"     "rock" 
#        tie        tie       loss        tie        win        win        win 
#     "rock" "scissors" "scissors" "scissors"     "rock" "scissors" "scissors" 
#        win        win       loss        win        tie        tie        win 
# "scissors" "scissors"    "paper"    "paper"    "paper"    "paper"    "paper" 
#        win        tie        win        tie        tie        win        win 
#    "paper"     "rock"     "rock"    "paper" "scissors"    "paper" "scissors" 
#       loss        tie        tie       loss        tie        win        win 
#     "rock"     "rock"    "paper" "scissors" "scissors"     "rock"     "rock" 
#        win 
#    "paper" 

Of course, if you did have an input vector we can run it through sapply() to see the same results as above (with or without the input vector in the names).

sapply(c("rock", "paper", "scissors"), rps)
#   rock.loss    paper.win scissors.tie 
#     "paper"       "rock"   "scissors" 
sapply(c("rock", "paper", "scissors"), rps, USE.NAMES = FALSE)
#       tie       loss        win 
#    "rock" "scissors"    "paper" 

Update: It just dawned on me that you will have no idea what the input was in the above cases that we got from replicate(), so here is a fix. If we change the last line if the function to a matrix (or a list), we can also include what the input was. We can use something like this for the last line =

matrix(c(move, cpu), 1, dimnames = list(wlt, c("move", "cpu")))

Then we would get the following for results -

rps("paper")
#      move    cpu       
# loss "paper" "scissors"
rps("paper")
#     move    cpu    
# tie "paper" "paper"

Now we know what we put in as input without doing any extra work. And we can also bring replications together into a matrix with our old friend do.call(rbind, ...).

like image 103
Rich Scriven Avatar answered Oct 06 '22 00:10

Rich Scriven