5 min read

Glass Stepping Stones

Glass Stepping Stones is the fifth game in Squid Game. There are two parallel bridges made up of 18 glass panels each. There are two types of glass panels: regular glass that breaks when a player steps on it and tempered glass that won’t break.

glass-bridge-graphic

There is a regular and tempered glass panel at each ‘step’ of the bridge. Contestants must choose a path across the bridge that avoids the regular glass panels (and their deaths). All players cross the same bridge sequentially and they know the glass panel types as they are ‘revealed’ by players preceding them.

There are 18 ‘steps’ across the bridge. The game starts with 16 players. How many players are expected to make it safely across?

We will assume:

  1. no time limit
  2. the panels are indistinguishable from each other
  3. information is instantly shared among players
  4. player harmony

First let’s create a function that simulates one game.

bridge_game <- function(players = 16, steps = 18) {
  
  # precompute random number for each step. probably unnecessary.
  rands <- runif(steps, 0, 1)

  for (rand in rands) {
    
    # players have a 50% chance of guessing the tempered glass panel
    # <= 0.50 is regular glass, > 0.50 is tempered glass
    if (rand > 0.50) {
        players <- players - 1 # regular glass, player dies
    }
    
    # regardless, players learn which panel is tempered and move one step closer
    steps <- steps - 1
    
    # game ends when there are either no steps or players remaining
    if (steps == 0 | players == 0) {
      return(players) # return the number of surviving players
    }
  }
}

Next, we’ll simulate 50k games and look at the distribution of surviving players.

results = vector('numeric', 17) # surviving players range from 0 to 16
n_iter <- 50000

for (x in 1:n_iter) {
  surviving_players <- bridge_game() + 1 # r indices start at 1
  results[surviving_players] <- results[surviving_players] + 1
}

results <- results / n_iter # percent of observations

library(ggplot2)
library(scales)

ggplot() +
  aes(x = 0:16, y = results) +
  geom_bar(stat = 'identity') +
  theme_minimal() +
  scale_x_continuous(breaks = 0:16 ) +
  scale_y_continuous(breaks = seq(0,.25,.01), 
                     labels = scales::percent_format(accuracy = 1)) +
  labs(x = 'Surviving Players', y = 'Percent of Games', 
       title = 'Glass Stepping Stones') +
  geom_text(aes(label = label_percent(accuracy = .1)(results)),
            position=position_dodge(width = 0.9), vjust = -0.50, size = 3)

surviving-players-simulation

Pretty neat! The most plausible outcome is that 7 players win the game, but it looks like the game designers were happy with 4-10 players surviving (90% probability). It also minimizes the likelihood that no players win (~0%) or that 12+ players win (~1%).

In Squid Game, the players were given an option to choose a jersey labeled with a number 1-16 that determines the order the play the game. What is the likelihood of surviving for each position?

cum_results <- (cumsum(rev(results)) / sum(results))[1:16]
ggplot() +
  aes(x = 1:16, y = cum_results) +
  geom_col() +
  theme_minimal() +
  scale_x_continuous(breaks = 1:16) +
  scale_y_continuous(breaks = seq(0, 1, .05), 
                     labels = scales::percent_format(accuracy = 1)) +
  labs(x = 'Player Number', y = 'Likelihood of Surviving',
       title = 'Squid Game Glass Bridge - Simulated Survival Rates') +
  geom_hline(yintercept = 0.5, color = 'red', linetype = 2) +
  geom_text(aes(label = label_percent(accuracy = .1)(cum_results)),
            position=position_dodge(width = 0.9), vjust = -0.50, size = 3)

simulated-survival-rates

A non-simulated approach to determine the likelihood each player survives is to use Combinations, as mentioned in this Reddit comment. Essentially we model the bridge as a 18 digit binary string and mark correct choices as 0 and incorrect choices (aka player deaths) as 1, then we enumerate all the possible outcomes of the game.

An example should help. Here is one possible binary string: 010011011100010100. Reading this left to right:

  1. Player 1 correctly guessed step 1, failed on step 2
  2. Player 2 correctly guessed steps 3 and 4, failed on step 5
  3. Player 3 failed on step 6
  4. Player 4 correctly guessed step 7, failed on step 8
  5. Player 5 failed on step 9
  6. Player 6 failed on step 10
  7. Player 7 correctly guessed steps 11,12, and 13, failed on step 14
  8. Player 8 correctly guessed step 15, failed on step 16
  9. Player 9 correctly guessed steps 17 and 18 and crosses the bridge
  10. Players 10 - 16 also survive the bridge because the correct path is known

The important insight here is that Player X survives if there are X-1 or fewer 1s in the binary string. If there are 7 1s, that means that 7 players died while revealing the full path across the bridge. Conversely, this means that players 8 - 16 survive.

There are 2 ** 18 possible combinations. For each Player X we count the number of outcomes that have X-1 or fewer 1s and divide that by the total possible outcomes. That ratio is the player’s survival rate.

players = vector('numeric', length = 16)

for (player in 1:16) {
  # count the number of outcomes where only 0 - (player - 1) players fail
  players[player] <- sum(choose(18, 0:player-1) / 2**18)
}

?geom_col

ggplot() +
  aes(x = 1:16, y = players) +
  geom_col() +
  theme_minimal() +
  scale_x_continuous(breaks = 1:16) +
  scale_y_continuous(breaks = seq(0, 1, .05), 
                     labels = scales::percent_format(accuracy = 1)) +
  labs(x = 'Player Number', y = 'Likelihood of Surviving',
       title = 'Squid Game Glass Bridge - Theoretical Survival Rates') +
  geom_hline(yintercept = 0.5, color = 'red', linetype = 2) +
  geom_text(aes(label = label_percent(accuracy = .1)(players)),
            position=position_dodge(width = 0.9), vjust = -0.50, size = 3)

theoretical-survival-rates