Short answer
You can return a list from your function.
Simple example
dealcard <- function(deck, hand) {
# There needs to be a card to deal
stopifnot(length(deck) > 0)
# Add the card from the top of the deck to the hand
hand <- c(hand, deck[1])
# Remove the card from the deck
deck <- deck[-1]
# Create a named list from the modified inputs
output <- list(deck=deck, hand=hand)
return(output)
}
But note that this approach does not actually modify the deck
and hand
objects in the global environment. (You can do that using <<-
, but in most cases you shouldn't.) So you can do this instead:
# Initialize deck and hand as a list
# Full deck, empty hand
deck.hand <- list(deck=sample(52, 52, replace=FALSE), hand=NULL)
# Deal a card
deck.hand <- with(deck.hand, dealcard(deck, hand))
Now if you look at deck.hand
, you'll see that the deck
and hand
items have been updated appropriately.
More involved example
This can be easily expanded to deal to a certain player when there are multiple players in the same list.
# Initialize the game
# Three players, everyone starts with empty hands
game <- list(deck=sample(52, 52, replace=FALSE) %% 13 + 1,
dealer=NULL, sally=NULL, john=NULL)
dealcard <- function(game, player, deck="deck") {
# Make sure the player specified is in the game
stopifnot(player %in% names(game) && deck %in% names(game))
# There needs to be a card to deal
stopifnot(length(game[[deck]]) > 0)
# Give the next card to the specified player
game[[player]] <- c(game[[player]], game[[deck]][1])
# Remove the card from the deck
game[[deck]] <- game[[deck]][-1]
# Return the new state of the game
return(game)
}
# Give everyone a card
game <- dealcard(game, "dealer")
game <- dealcard(game, "sally")
game <- dealcard(game, "john")
This uses the syntax list[["itemname"]]
. The list item is passed as a character string. This is equivalent to extracting the item using $
, i.e. list$itemname
.
Here we're passing in the whole list, modifying only the necessary parts of it, and returning the whole modified list. Setting the original list equal to the returned value is just like modifying the list in place.
Now if you look at the contents of game
, each player will have one card and the deck will be missing those that were distributed.
Extensions for fun
I got a little too into this.
Want to deal five cards to each player in succession?
players <- names(game)[names(game) != "deck"]
for (i in 1:5) {
for (player in players) {
game <- dealcard(game, player)
}
}
Take a look at game
again. Each player has 5 cards, and since the loops are structured in this way, the dealing order mimics that of a standard card game.
You can return a card to the deck by using "deck"
as the player and the player as the deck, like so:
# John returns his first card to the deck
game <- dealcard(game, player="deck", deck="john")
Given a function that scores a hand in some way, you can obtain the score for each player easily.
# Example scoring
scorehand <- function(game, player) {
return(sum(game[[player]]))
}
sapply(players, scorehand, game=game)
That will show you the current score for each player by applying the scorehand()
function to each element of the vector players
, which consists of the names of the players.
TL;DR
Use a list.