How to Win a Game (or More) of Super Six
This article is originally published at http://staff.math.su.se/hoehle/blog/
Abstract:
We use simulation to analyse the family dicing game “Super Six”. In particular we show that the person starting the game has a very high chance of winning the game. Furthermore, a robust strategy to play during the game is to keep throwing the dice regardless of the number of free pits. In a mixed strategy landscape with more than two players this seems to do better than a strategy extrapolated from the analytically derived optimal strategy for the two player case.
This work is licensed under a Creative Commons
Attribution-ShareAlike 4.0 International License. The R-markdown
source code of this blog is available under a GNU General Public
License (GPL v3) license from GitHub.
Introduction
Super Six is a dice game which can be played at all ages. It consists of a container where the lid has 5 pits (labelled 1-5) and one hole (labelled 6). Furthermore, each player gets a predetermined number of sticks and a dice (see Fig.1). If a stick is put into pit 6 (the hole) it falls into container and stays there.
The rules of the games are as follows: The sticks are distributed among the players so that each player has exactly the same number of sticks (say: 4). Each player throws the dice in turn going round in clockwise direction. If a player throws a number and the corresponding pit is empty, then he/she can insert one of his/her sticks into it. If the pit is already occupied, however, then he/she must take the stick and and add it to his/her own pile of sticks. The number 6 is a “lucky throw”. With a 6, one can always place a stick into the hole labelled No. 6 and it stays there. Note: During the first round, each player is only allowed to throw the dice once. From the second round on, each player can throw the dice as often as desired, provided that one always managed to hit an empty pit. As soon as one throws the number of an occupied pit, one must take the stick that is located there and it is then the next players turn. Each player most throw the dice at least once per round, but after the first throw he/she can decide to stop throwing at any time in that round (that is also before hitting an occupied pit). Once a player has no sticks left, the game is over and he/she is the winner. This YouTube video by Rüdiger Jehn shows a exemplary game play of Super Six.
Empirical experience from playing a lot of Super 6 with the kids raises the question if the game is really fair: Does the player starting the game have a winning advantage? Furthermore, interest is in the optimal strategy to play: Should one always continue to throw the dice until one hits an occupied pit or should one stop playing once there are only \(x\) out of the 5 pits free (with \(x\) being either 1 or 2)? In what follows we shall use simulation to get an approximate answer to these questions in a realistic environment with different players following different strategies. For a two player situation analytic results exist (Jehn 2021).
Simulating Super 6
The file super6.R
contains a function simulate_super6
with arguments
players_strategy
and sticks
to simulate a game
of Super 6 for \(n\)=length(players_strategy)
players each initially having sticks
number of sticks. The
named list players_strategy
contains each player’s strategy
as a tibble
, mapping the number of free pits
(i.e. 0,1,2,3,4,5) to a probability to continue throwing the dice. If
this probability is either 0 or 1 then we have a deterministic strategy.
As an example the always
strategy describes a behaviour
where one keeps throwing the dice independent of the number of free
pits:
<- tibble(filled_holes=0:5, continue_prob=1)) (always
## # A tibble: 6 × 2
## filled_holes continue_prob
## <int> <dbl>
## 1 0 1
## 2 1 1
## 3 2 1
## 4 3 1
## 5 4 1
## 6 5 1
As an example: To simulate a single game of Super 6 with three players, A, B and C, each playing the “always” strategy with initially 4 sticks we call
<- simulate_super6(list(A=always, B=always, C=always), sticks=4) h
The resulting trajectory of state and actions of the game are returned by the function:
round | player | turn | dice | hole.1 | hole.2 | hole.3 | hole.4 | hole.5 | hole.6 | sticks.A | sticks.B | sticks.C | decision |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
1 | C | 1 | 6 | 0 | 0 | 0 | 0 | 0 | 1 | 4 | 4 | 3 | stop turn |
1 | A | 1 | 3 | 0 | 0 | 1 | 0 | 0 | 1 | 3 | 4 | 3 | stop turn |
1 | B | 1 | 2 | 0 | 1 | 1 | 0 | 0 | 1 | 3 | 3 | 3 | stop turn |
2 | C | 1 | 2 | 0 | 0 | 1 | 0 | 0 | 1 | 3 | 3 | 4 | lost turn |
2 | A | 1 | 6 | 0 | 0 | 1 | 0 | 0 | 2 | 2 | 3 | 4 | continue turn |
2 | A | 2 | 3 | 0 | 0 | 0 | 0 | 0 | 2 | 3 | 3 | 4 | lost turn |
2 | B | 1 | 5 | 0 | 0 | 0 | 0 | 1 | 2 | 3 | 2 | 4 | continue turn |
2 | B | 2 | 4 | 0 | 0 | 0 | 1 | 1 | 2 | 3 | 1 | 4 | continue turn |
2 | B | 3 | 6 | 0 | 0 | 0 | 1 | 1 | 3 | 3 | 0 | 4 | game over |
Note that in the above game the player to start is randomly selected.
The players then each take turns in the pre-determined order. The order
of the rows in the table corresponds to the sequence of actions in the
game. The column turn
is a counter indicating how many
times the player has thrown the dice during his/her round. The columns
hole.1
-hole.5
are binary indicators whether
the respective pit is currently filled with a stick (FALSE=0, TRUE=1).
Finally, a column counts for each player the current number of remaining
sticks (initially equal to 4). The last column, decision
contains information about the player’s action and its consequences.
Here, “stop turn” is a forced decision in round 1, because each player
can only throw the dice once in round 1. Furthermore, “lost turn”
indicates the situation that the pit indicated by the dice throw was
already occupied and “continue turn” means that the player based on
his/her strategy decided to throw again. Finally, “game over” means that
one player reduced their number of sticks to zero and the game thus
ends. The winner of the shown game shown is B.
Analysis
We can use the above simulation function to analyse different strategies for games with 4 initial sticks. Consider the four additional strategies:
<- tibble( filled_holes=0:5, continue_prob=1*(filled_holes<=3))
go50 <- tibble( filled_holes=0:5, continue_prob=1*(filled_holes<=4))
go67 <- tibble( filled_holes=0:5, continue_prob=0.5)
random <- tibble(filled_holes=0:5, continue_prob=c(1,1,1,1,0.5,0.25)) random2
Let’s assume that Agneta is playing the always
strategy
and that Bertil, Cecilia, Dagmar and Emil each are playing one of the
four other strategies.
<- list(Agneta= always,
player_list Birger= go50,
Carin= go67,
Dagmar= random,
Emil= random2)
<- names(player_list) player_names
We now consider all possible matches of these players. This can be games ranging from just 2 of the players to all 5 of them.
<- 2L:length(player_names)
n_players <- map(n_players, function(m) {
matches ::combn(player_names, m=m, simplify=FALSE)
utils%>% setNames(n_players)
}) <- map(matches, length)
n_combos %>% unlist() n_combos
## 2 3 4 5
## 10 10 5 1
To ensure that the we consider an equal amount of games for every
number of players, we simulate n_games
games for each
number of players (2-5). The games to play are then distributed equal
among the different pairings with that number of players. For this
reason, n_games
has to be divisible by 5 and 10.
<- 10000
n_games
# Parallelize analysis using the future-verse
plan(multisession, workers = 4)
# Play the games
<- future_map2(matches, n_combos, function(names, combos) {
all_games map(names, ~ {
<- player_list[.x]
the_players_strategy # Divide the n_games equal among the possible combinations
<- n_games / combos
n_games_each_combo map(seq_len(n_games_each_combo), ~
simulate_super6(players_strategy=the_players_strategy, sticks=4)
)%>% setNames(map(names, ~ str_c(.x, collapse="-")))
}) .options=furrr_options(seed=TRUE)) },
This provides a nested list structure, where the top-most level contains the number of players of the game, the 2nd level contains the name of the participating players and the 3rd level the recordings of every game played of the respective player combination (organised as a tibble). The recordings/history of each game is organised as a data frame with columns closely related to the above game description. As an example, a total of
"3"]][["Agneta-Birger-Carin"]] %>% length() all_games[[
## [1] 1000
games (=10000/10) are played between the players Agneta, Birger and Carin.
We now use this nested list structure of simulated games to answer our questions of interest. The traversing of the list is done by purrr. As an example we determine the total number of games as follows:
<- map(all_games, ~ map(.x, length)) %>% unlist() %>% sum()) (n_total_games
## [1] 40000
Beginner is Winner?
We now investigate the probability to win the starting player. Let
h
denote the tibble describing the history of a game (such
as shown above). We will write a function, which given the history of a
game returns the player name who won the game.
<- function(h) {
winner %>% slice_tail(n=1) %>% pull(player)
h
}
## Test the function on the above shown trajectory
winner(h)
## [1] "B"
We can use the function to determine the probability that the player starting the game also becomes the winner of the game. To take into account a possible dependence on the number of players we shall stratify our analysis by the number of players in the game.
<- map2_df(all_games, n_players, function(games, players) {
games_summary map_df(games, ~map_df(.x, ~ tibble(n_players=as.numeric(players),
beginner=.x %>% slice_head(n=1) %>% pull(player),
winner=.x %>% slice_tail(n=1) %>% pull(player))))
})
<- games_summary %>% group_by(n_players) %>%
tab summarise(p_initial_winner=mean(beginner==winner)) %>%
mutate(p_random = 1/n_players,
favor_factor = p_initial_winner / p_random)
n_players | p_initial_winner | p_random | favor_factor |
---|---|---|---|
2 | 0.6009 | 0.5000000 | 1.2018 |
3 | 0.4438 | 0.3333333 | 1.3314 |
4 | 0.3527 | 0.2500000 | 1.4108 |
5 | 0.2970 | 0.2000000 | 1.4850 |
The empirical probability that the initial player wins is always
higher than the probability \(1/n\)
that one of the \(n\) players in the
game wins at random. The larger the number of players the bigger the
relative advantage of beginning seems. Note that our analysis averages
over the all strategies so it also contains start players using an
otherwise sub-optimal playing strategy. This might bias the analysis
slightly, but a small sensitivity analysis letting all 5 players play
the always
strategy, showed that results did not change
substantially.
Best strategy
We can now compute for each player (i.e. strategy) the number of wins out of the 40000 games:
<- map(all_games, ~ map(.x, ~ map(.x, winner))) %>%
(league_table unlist() %>%
as_tibble() %>%
rename(player = value) %>%
group_by(player) %>%
summarise(wins=n()) %>%
arrange(desc(wins)))
## # A tibble: 5 × 2
## player wins
## <chr> <int>
## 1 Birger 8800
## 2 Carin 8593
## 3 Emil 8520
## 4 Agneta 8419
## 5 Dagmar 5668
This shows that the always
strategy appears to do best
among those strategies investigated. It is no surprise that the worst
strategy is the purely random strategy random
.
Discussion
We analysed aspects of how to win a game (or more) of Super Six. Important factors for winning in a multi-player mixed strategy landscape are
- To start the game
- To keep throwing the dice
A little surprising is that the best strategy (among those investigated) is to keep throwing the dice regardless of the number of free pits. The optimal strategy may in part depend on how the other players play, but our conclusions appear sufficiently robust, because the simulation study investigated a mixed strategy player field with two to five players. A limitation of our analysis is that we only considered games where each player initially holds 4 sticks.
It is worth pointing out that the best strategy we found is not optimal. As an example, Jehn (2021) analytically finds the optimal strategy for the \(n=2\) player situation. It depends on the number of sticks left in the game and the number of sticks each player has. In most cases the strategy stops if more than 3 pits are occupied – see Appendix 1 for details. This dependence on the other player’s hand is likely to carry forward to \(n>2\) player situation. Analytic results for \(n>2\) situations will be extremely difficult to obtain, but it makes intuitive sense to be more aggressive when there are more players, because there are more contestants for the win.
Did the game developers realized that the start player has such a high probability of winning the game? It seems advisable to accompany game development by simulations and mathematical calculations at the design stage. In practice one should randomize the start player to enhance fairness.
Final note: This will be the last post of Theory Meets Practice Blog in its current form. As of Jan 2023 I am working as full professor in Statistics and Data Science at the University of Greifswald, Germany. For both technical and time reasons a new setup is thus needed.
Appendix 1
The work of Jehn (2021) shows the derivation of the optimal strategies encoded in A345383 for a game with 2 players and a situation where \(i\) pits occupied, the first player (our player of interest) has \(j\) sticks in hand and the second player has \(k\) sticks. This state is denoted as \(i/j/k\). For example \(2/1/1\) denotes the situation where there are 2 pits occupied in the lid and each player has one stick.
A small helper function gives all states where a continue vs. stop choice needs to be made. It is assumed that if there are no sticks in the lid, we always continue:
#' Find all situations where a continue vs. stop choice is to be made
#' for player 1.
#'
#' See Jehn (2021), https://arxiv.org/pdf/2109.10700.pdf, for details.
#'
#' @param n Total number of sticks currently (i+j+k)
#' @return A vector of strings describing the situations in i/j/k notation
#' where i is the number of occupied pits in the lid, j is the number of
#' sticks player 1 has and k is the number of sticks player 2 has. Note
#' i+j+k = n
#'
<- function(n) {
situations <- NULL
res # Sticks in the Lid
for (i in min(n,5):1) {
# Sticks Player 1
for (j in n:1) {
# Sticks Player 2
<- n - i - j
k # Don't consider impossible situations or situations without a choice
if (k>0 & !(i==0 & j==k)) {
<- append(res, str_c(c(i,j,k), collapse="/"))
res
}
}
}return(res)
}
# All situations when there are 7 sticks left in the game
situations(n=7)
## [1] "5/1/1" "4/2/1" "4/1/2" "3/3/1" "3/2/2" "3/1/3" "2/4/1" "2/3/2" "2/2/3" "2/1/4" "1/5/1" "1/4/2" "1/3/3" "1/2/4" "1/1/5"
The optimal strategy is encoded in binary and converted to base 10 in the sequence A345383. We write a small R extractor to display the optimal strategy in more readable form:
## Handle the large integers using the GMP library
suppressPackageStartupMessages(library(gmp))
#' Encodes [A345383](https://oeis.org/A345383)
#'
#' @param n Number of sticks
#' @returns a(n) from A345383
#'
<- function(n) {
a <- as.bigz(c(0, 0, 1, 7, 63, 1023, 32760, 1048544, 33554304, 1073741312, 34359736320, 1099511619584, 35184370221056, 1125899873681408, 36028796752101376))
a if (n>=1 & n<=15) return(a[n]) else return(NA)
}
#' Convert a strategy encoded as a 0/1 string to a vector of Booleans
#' @param s The strategy as a string of 0 and 1's
#' @return As a vector
#' @examples
#' int_strategy_to_vec("0011")
<- function(s) {
int_strategy_to_vec str_split_1(as.character(s, b=2), pattern="") %>% as.integer()
}
#' Show the optimal stragegy for all situations with n sticks
<- function(n) {
optimal_strategy # Make a tibble representing the optimal strategy
tibble(situation= situations(n),
continue = rev(int_strategy_to_vec(a(n))))
}
optimal_strategy(n=7) %>% print(n=Inf)
## # A tibble: 15 × 2
## situation continue
## <chr> <int>
## 1 5/1/1 0
## 2 4/2/1 0
## 3 4/1/2 0
## 4 3/3/1 1
## 5 3/2/2 1
## 6 3/1/3 1
## 7 2/4/1 1
## 8 2/3/2 1
## 9 2/2/3 1
## 10 2/1/4 1
## 11 1/5/1 1
## 12 1/4/2 1
## 13 1/3/3 1
## 14 1/2/4 1
## 15 1/1/5 1
We observe that for situations with 7 sticks we continue to play as long as only 1-3 pits are filled. If 4 or 5 pits are filled, we don’t continue. However, in other settings with different number of sticks left, the optimal strategy can be different. As an example, if there 6 sticks left, the optimal strategy is
## # A tibble: 10 × 2
## situation continue
## <chr> <int>
## 1 4/1/1 1
## 2 3/2/1 1
## 3 3/1/2 1
## 4 2/3/1 1
## 5 2/2/2 1
## 6 2/1/3 1
## 7 1/4/1 1
## 8 1/3/2 1
## 9 1/2/3 1
## 10 1/1/4 1
which -opposite to the 7 sticks left situation- continues in the 4 pits filled situation, if each player has only one stick left (4/1/1).
Finally, we implement a function, which implements Jehn’s optimal
strategy in a way, which can be used by the simulation function
simulate_super6()
:
#' Jehn's optimal strategy for for two players
#'
#' @param player_idx Index of the player in the state_players variable (see simulate_super6 function)
#' @param filled_holes Number of pits filled
#' @param state_players Vector of length equal to the number of players containing the number of sticks each player has left
#' @return A numeric corresponding to the probability to continue
#'
<- function(player_idx, filled_holes, state_players) {
jehn # Some summaries
<- filled_holes + sum(state_players)
sticks_left <- state_players[player_idx]
my_sticks <- state_players[-player_idx]
other_sticks
#Strategy for basic cases
if (filled_holes <= 2) return(1)
if (filled_holes == 5) return(0)
#Complicated part of the strategy
<- optimal_strategy(n=sticks_left)
s_best <- str_c(filled_holes, my_sticks, other_sticks, sep="/")
state <- s_best %>% filter(situation == state) %>%
action pull(continue)
# Check if output fails (this should not happen).
if (length(action) == 0) { browser()}
if (!is.numeric(action)) { browser()}
# Done
return(action)
}
This allows us to test, if there is a winning advantage, despite both players playing the optimal strategy (see source code for details).
## # A tibble: 1 × 1
## p_win_beginner
## <dbl>
## 1 0.596
The answer is thus: Yes! In 60% of the games the starting player
wins. Thus, the winning advantage of the beginner in the \(n=2\) scenario remains even when both
players use the jehn
optimal winning strategy.
Literature
Thanks for visiting r-craft.org
This article is originally published at http://staff.math.su.se/hoehle/blog/
Please visit source website for post related comments.