Concepts → CoC sanity as gambler's ruin

Call of Cthulhu sanity as gambler's ruin — when the math says you're going to zero

An Investigator's sanity pool is mathematically identical to an HP pool with per-encounter damage and slow trickle healing. The same engine machinery that answers "how many strikes until the monster dies?" answers "how many sessions until the investigator breaks?" — because both questions are the same Markov walk on cumulative loss vs. pool depth. The narrative wrapper is different; the math is identical. And in nearly every published CoC scenario, the cure ratio is set so the walk drifts to zero with probability one. The interesting question isn't whether your investigator will go insane — it's how long it'll take, and what's in the survivors curve.

The short answer

Map sanity onto HP and CoC's sanity-loss problem becomes a vanilla rounds-to-kill chain:

HP-side concept CoC sanity equivalent
Starting HP Starting sanity (typically POW × 5)
Per-round damage roll Per-encounter sanity loss (e.g. 1d6 for a Mythos shock)
Per-round healing Per-downtime recovery (e.g. +1 per week of psychotherapy)
Max rounds Number of in-game intervals to simulate
"Killed by round N" "Insane by encounter N"

Hand this to the engine's existing rounds_to_kill and it returns the full "rounds-until-zero" distribution — not just the mean.

{:ok, r} = Diceplots.Engine.rounds_to_kill(
  ["1d6"],            # per-encounter sanity loss
  ["1"],              # per-encounter recovery (off-table)
  50,                 # starting sanity
  20                  # max encounters to simulate
)
r.expected_rounds      # E[encounters until insanity]
r.cumulative           # P(insane by encounter N) for N = 1..max

The cure ratio decides whether the chain converges

Same dichotomy as the HP problem (covered in expected strikes to kill):

  • E[recovery] ≥ E[loss]: the chain doesn't absorb at zero in finite expectation. The investigator's sanity wanders around or trends back up. Player can take unlimited Mythos exposure in the long run.
  • E[recovery] < E[loss]: strict drift toward zero. The chain absorbs at insanity with probability 1; the only question is when. The expected time to insanity is bounded by (starting sanity) / (E[loss] − E[recovery]).

Most published CoC scenarios pace per-investigation sanity loss at 1d6 or 1d10 per scene of Mythos contact, and per-downtime recovery at +1 per week of psychotherapy. Investigation pacing typically front-loads Mythos contact and back-loads downtime. The cure ratio rarely favours the player. The walk drifts; the only design lever is how long the player has before the descent.

Variance changes the whole picture

Two investigators, same starting sanity 50, both facing 15 sanity-loss encounters with the same expected loss per encounter (3.5). One faces 1d6 per encounter; the other faces 2d4 − 1.5 for the same mean but lower variance. Same expected total loss (52.5). Same expected ending sanity (-2.5).

But the distributions are different. The high-variance investigator (1d6) has a longer tail in both directions — they might stagger out at sanity 12 if the dice run cold, or hit zero by encounter 9 if they run hot. The low-variance investigator (2d4 − 1.5) clusters tightly around the mean — they reliably hit zero between encounters 14-15, with very little chance of finishing on either side of that band.

For a published scenario, the variance picture matters more than the mean. The "this scenario is rough on sanity" warning that authors put on adventures usually translates to "the variance of per-encounter loss is high enough that some investigators will absorb early". A scenario with the same mean sanity loss but lower variance gives every investigator a more similar experience; high variance is what produces "the table of investigators ended in vastly different mental states" stories. The variance and kill probability pillar covers this for HP — the same math applies, just relabeled.

The Mythos cap and the temporary insanity carousel

Two CoC-specific wrinkles the basic gambler's-ruin model doesn't capture out of the box:

The Cthulhu Mythos cap. Maximum sanity is 99 − Cthulhu Mythos skill. As an investigator learns more about the Mythos to fight cultists, their maximum sanity drops — even healthy recovery hits a hard ceiling. So the gambler's ruin chain has a shrinking pool ceiling over time. Compose this by chunking: run the chain one segment per Mythos-acquisition milestone (each segment uses the ceiling-of-the-moment as the starting sanity for the next), then compose the per-segment survivor distributions together. Standard pattern: a sequence of independent gambler's-ruin chains with input distributions tracked across segment boundaries.

Temporary vs. indefinite vs. permanent insanity. The "absorbed at zero" event in the HP analogy isn't a single thing in CoC. Hitting 0 sanity = permanent insanity (the character is retired). But losing 5+ sanity in one scene = temporary insanity (player loses control of the character for a few rounds). Losing 1/5 of starting sanity in a single day = indefinite insanity. The basic gambler's-ruin model treats all sanity loss uniformly. For richer modelling, split the input into "shock loss" and "drain loss" expressions and run two parallel chains — temporary insanity events become a separate per-round Bernoulli on top of the cumulative-loss Markov, composed via the same multi-track pattern.

What the engine ships today

The same call that computes strikes-to-kill against a target answers the sanity question:

# A 5-scenario campaign arc, sanity 50, 1d6 loss per scenario,
# +1d4 recovery per scenario (downtime woven in):
{:ok, r} = Diceplots.Engine.rounds_to_kill(["1d6"], ["1d4"], 50, 5)
r.converged            # true if the player makes it through all 5
r.expected_rounds      # if converged is false: E[rounds until insanity]
r.cumulative           # P(insane by round N) for each N

# An arc with 2d6 loss (heavier Mythos density) and no recovery —
# strict drift to zero:
{:ok, r} = Diceplots.Engine.rounds_to_kill(["2d6"], [], 50, 10)
r.expected_rounds      # ~ 50 / 7 ≈ 7.14 by linearity, exact value via engine

Multi-pool sanity questions (e.g., the party splits into two investigation tracks; one faces Mythos contact, the other recovers; recombine sanity at the end) compose by running separate rounds_to_kill evaluations per track and joining the marginal end-state distributions — same pattern used for chunked Mythos-cap segments above.

Where this matters in practice

The gambler's-ruin frame surfaces design and play decisions that the "did my investigator survive?" binary lens hides.

Scenario length tuning. If your sanity-loss / recovery cure ratio is 2:1, expected sessions to insanity is (starting sanity) / (loss − recovery). A POW 50 character (sanity ~70) with 1d6 loss / 1 recovery per scenario hits insanity at ~28 scenarios in expectation. A 6-session arc is well-within survival territory; a 12-session campaign with the same pacing is on the cusp. Variance still matters — the 95th-percentile time to insanity is much shorter than the mean for high-variance loss distributions.

The "down for the count" cliff. Once an investigator drops below (starting sanity − 20) they're at risk of indefinite insanity from any moderate shock. So the practical "broken" threshold isn't zero — it's about 20 below starting, where the next significant Mythos contact has a ~50% chance of crossing the indefinite line. Keepers tuning the late-game should compute "P(reach 20-below) by session N" rather than "P(reach zero) by session N" — the former captures when the player effectively loses control.

Cross-system: any drift-to-absorption mechanic. The same shape applies to Eclipse Phase stress, Delta Green's sanity track, Trail of Cthulhu's stability, Vampire's Hunger die — anywhere a finite pool drains via per-encounter loss against trickle recovery, the engine's rounds_to_kill substrate computes the survivors curve. You don't need a sanity-specific module; the framing transfers.

Common questions

Why is sanity loss a gambler's ruin walk and not just averaging?
Because the per-encounter sanity loss isn't a constant — it's a distribution. A 1d6 loss has expected value 3.5, but its variance creates a fat-tailed survivors curve. Two investigators starting at sanity 50 and facing 15 1d6 encounters: averaging says they end at 50 - 15·3.5 = -2.5, both broken. But variance means investigator A might end at 8 (six low rolls in a row) while investigator B might hit zero on encounter 11. The engine's rounds_to_kill machinery computes the full distribution of 'rounds until zero' so you can answer 'what's my chance of finishing this 5-scenario arc with sanity ≥ 10?', not just the mean.
When does the chain converge vs. drift to zero forever?
The same answer as for HP-vs-healing: when E[per-round healing] ≥ E[per-round damage], the Markov chain doesn't kill you in finite expectation; the long-run behaviour wanders or recovers. When E[healing] < E[damage], you're on a strict drift toward zero with probability 1 — gambler's ruin. Most published CoC scenarios have per-scenario sanity loss in the 1d6–1d10 range and per-downtime recovery in the 1–1d6/week range, with downtime typically much shorter than the in-game investigation pace. The cure ratio rarely favours the investigator. Telling the player 'in expectation you have N sessions before insanity' is what rounds_to_kill computes.
How does this compose with the success-bands pillar?
Sanity tests are themselves percentile checks (current sanity is the skill). A failed roll triggers per-encounter sanity loss (often '0/1d4' meaning 0 on success, 1d4 on fail). Compose Diceplots.Engine.percentile_test(sanity, ...) with rounds_to_kill: P(failed sanity check at S=current) gives the conditional probability of triggering the loss; rounds_to_kill takes the loss distribution. As sanity drains, P(fail) rises (the skill drops), accelerating the drift. The full state-carrying chain is more elaborate than the engine ships today; the gambler's-ruin framing here uses a fixed loss distribution per round as a useful upper bound.
Is this just rounds_to_kill with relabelled inputs?
Yes — that's the point of the framing. The engine doesn't know you're modelling sanity vs. HP; it computes the cumulative-loss-vs-pool Markov chain regardless of the narrative wrapper. The pillar makes the mapping explicit so keepers and players can drive existing site machinery to answer sanity-pool questions: HP slider = starting sanity, damage expression = per-scenario loss distribution, healing expression = per-downtime recovery, max rounds = number of in-game intervals. The rest is the same exact-rational Markov walk that powers /concepts/expected-strikes-to-kill.