How many repeated attempts?

Suppose you, the GM, allow repeated attempts at … say, picking a lock. Often this comes with some type of risk (random encounter rolls) or resource expenditure (eg, torch timer, spell expiration, etc). If each attempt has a high probability of success then you can just roll and roll a second time if necessary and probably won’t have to roll a third. But if each attempt has a low probability, you may be rolling for awhile and it would be nice if there was a shortcut for “how many turns will this take.” Here is that shortcut:

  • If each attempt is around 1 in d6 / 15% / DC 17 +0, then roll 2d12 keep low for number of turns before you get it right.
  • If each attempt is around 2 in 6 / 25% / DC 15 +0, then roll 2d8 keep low for number of turns.
  • If each attempt is around 3 in 6 / 50% / DC 10 +0, then roll 2d4 keep low for number of turns.
  • If each attempt is > 50%, just roll separately for each attempt.

That is the shortcut. Here is how I got it.

What we are trying to do is approximate at the table the geometric distribution, which tells us that if each attempt has probability p then the probability of succeeding on attempt n is:

So, for instance, if the probability of success on each attempt is 50% then the probability of succeeding on exactly your third attempt is:

(1-.5)^(3-1)*.5 = .25*.5 = .125

In practice this means that if Sniknar the thief has a 50% chance of picking a lock and the GM allows repeated attempts, there is a 12.5% chance the party will burn three torch turns and roll three random encounters. So that is what we are attempting to approximate. It turns out that roll two, keep low, has a similar shape to the geometric distribution. Not exactly the same since the geometric distribution is curved and roll two keep low is pretty linear, but it’s very close. To see how close, I calculated the probabilities for both distributions and compared them.

Here are the geometric distribution graphs.

And the roll twice, keep low graphs.

And here are the fits for 2d12 keep low vs 15%, 2d8 keep low vs 25%, and 2d4 keep low vs 50%.

And here is the R code.

library(tidyverse)
times.vec <- seq(1:20)

# .15 (1 in d6, 15%, d20 DC 17)
p <- .15
1/p # mu
((1-p)/p^2)^0.5 # sigma
p.n.15 <- vector()
for (n in times.vec) {
  p.n.15[n] <- p*(1-p)^(n-1)
}
p.n.15 %>% format(scientific = F)

# .25 (2 in d6, 25%, d20 DC 15)
p <- .25
1/p # mu
((1-p)/p^2)^0.5 # sigma
p.n.25 <- vector()
for (n in times.vec) {
  p.n.25[n] <- p*(1-p)^(n-1)
}
p.n.25 %>% format(scientific = F)

# .5 (1-3 in d6, 50%, d20 DC 10)
p <- .5
1/p # mu
((1-p)/p^2)^0.5 # sigma
p.n.5 <- vector()
for (n in times.vec) {
  p.n.5[n] <- p*(1-p)^(n-1)
}
p.n.5 %>% format(scientific = F)

dicebag <- c(4,6,8,10,12,20)

for (diesize in dicebag) {
  die1 <- seq(1:diesize)
  die2 <- die1
  df.dice <- expand.grid(die1,die2)
  df.dice <- df.dice %>%
    mutate(low.2=if_else(Var1<Var2,Var1,Var2))
  temp.df <- df.dice %>% 
    group_by(low.2) %>% 
    summarise(n=n()) %>%
    mutate(prob = n / sum(n)) %>% 
    select(-n) 
  temp.vec <- temp.df$prob
  length(temp.vec) <- 20
  temp.vec[is.na(temp.vec)] <- 0
  assign(paste0("low2d",diesize), temp.vec) 
}
n <- seq(1:20)
diceprobs <- cbind.data.frame(n,
  low2d4, low2d6, low2d8, low2d10, low2d12, low2d20)
geoprobs <- cbind.data.frame(n, 
                             p.n.15, p.n.25, p.n.5)#, p.n.66, p.n.75, p.n.85)

diceprobs.long <- diceprobs %>% pivot_longer(
                          cols = starts_with("low2"),
                           names_to = "varname", 
                           values_to = "prob")

geoprobs.long <- geoprobs %>% pivot_longer(
  cols = starts_with("p.n."),
  names_to = "varname",
  values_to = "prob"
)

diceprobs.long %>% 
  ggplot(aes(x=n, y=prob, color=varname)) + 
  geom_line(lwd=1.5, alpha=0.5) + 
  theme_classic()
geoprobs.long %>% 
  ggplot(aes(x=n, y=prob, color=varname)) + 
  geom_line(lwd=1.5, alpha=0.5) + 
  theme_classic()

dice.geo.probs.long <- rbind(diceprobs.long,geoprobs.long) 

dice.geo.probs.long %>%
  ggplot(aes(x=n, y=prob, color=varname)) + 
  geom_line(lwd=1.5, alpha=0.5) + 
  theme_classic()

dice.geo.probs.long %>%
  filter(varname=="p.n.15" | varname=="low2d12") %>%
  ggplot(aes(x=n, y=prob, color=varname)) + 
  geom_line(lwd=1.5, alpha=0.5) + 
  theme_classic()

dice.geo.probs.long %>%
  filter(varname=="p.n.25" | varname=="low2d8") %>%
  ggplot(aes(x=n, y=prob, color=varname)) + 
  geom_line(lwd=1.5, alpha=0.5) + 
  theme_classic()

dice.geo.probs.long %>%
  filter(varname=="p.n.5" | varname=="low2d4") %>%
  ggplot(aes(x=n, y=prob, color=varname)) + 
  geom_line(lwd=1.5, alpha=0.5) + 
  theme_classic()

Leave a comment

Comments (

0

)