Diceplots → Games → Call of Cthulhu (BRP percentile)
Call of Cthulhu probability math — d100 success bands as exact rationals
Chaosium's BRP-derived percentile system resolves checks with d100 roll-under against a percentile skill, producing one of six discrete success bands off the same roll. The engine answers the canonical CoC questions — band probabilities at any skill, the bonus / penalty die order statistic, and the gambler's-ruin shape of sanity loss — with exact rationals. Every number is a closed-form ratio, not a Monte-Carlo estimate.
The recurring CoC questions, in one click each
-
Six outcomes off one d100 — what's the math?
Critical, extreme, hard, regular, fail, fumble — bands are cumulative on the success side, exclusive on the failure side. The fumble band has a discontinuity at skill 50: P(fumble) jumps from 2/100 at S=49 to 5/100 at S=50. The "or better" rule keeps the success ladder monotonic at low skills.
-
Why the bonus / penalty die isn't `kh1`/`kl1`
Only the tens digit gets the order statistic; the units stays shared between both d100 readings. That changes the math meaningfully — the advantage curve peaks at skill 50 (a 25-point P(regular) swing) and tapers to ~9 points at skills 10 or 90.
-
Sanity loss as gambler's ruin
An Investigator's sanity pool is mathematically identical to an HP pool with per-encounter damage and slow trickle healing. The same Markov machinery that answers "how many strikes until the monster dies?" answers "how many sessions until the Investigator breaks?" — because both are random walks on cumulative loss vs pool depth.
The d100 success bands
Every CoC skill check rolls a d100 and reads the result against
the character's percentile skill. For skill S:
| Band | Condition | Notes |
|---|---|---|
| Critical | roll = 01 |
Always; one in a hundred. |
| Extreme | roll ≤ ⌊S/5⌋ (or 01) |
"One-fifth success." |
| Hard | roll ≤ ⌊S/2⌋ (or better) |
"Half success." |
| Regular | roll ≤ S (or better) |
Standard pass. |
| Fail | roll > S, not in fumble range |
Exclusive. |
| Fumble | roll ≥ 96 (S ≥ 50) or ≥ S+50 (S < 50) |
Catastrophic; widens at low skill. |
The "or better" promotion ensures the band hierarchy stays
monotonic at low skills — a roll of 01 at S=4 is a critical
(and therefore an extreme, hard, regular) even though
floor(4/5) = 0 would otherwise exclude it from
extreme.
Engine grammar
The percentile-test analyzer is a direct API call. Skill is
a 0..=100 integer; die mod is one of
:plain, :bonus,
:penalty:
{:ok, r} = Diceplots.Engine.percentile_test(50)
# r.p_critical / p_extreme / p_hard / p_regular / p_fail / p_fumble
# All exact rationals over the 100-outcome (or 1000-outcome with
# bonus/penalty) sample space.
{:ok, r} = Diceplots.Engine.percentile_test(50, :bonus)
# Bonus die: tens-d10 with shared units; min over both readings.
# 1000 equally-likely (T1, T2, U) triples enumerated exactly.
{:ok, r} = Diceplots.Engine.percentile_test(50, :penalty)
# Same shape; max over both readings.
Worked examples
| Skill | P(any pass) | P(hard) | P(extreme) | P(fumble) |
|---|---|---|---|---|
| S = 25 | 25/100 | 12/100 | 5/100 | 26/100 (S+50…100) |
| S = 49 | 49/100 | 24/100 | 9/100 | 2/100 (99..100) |
| S = 50 | 50/100 | 25/100 | 10/100 | 5/100 (96..100) |
| S = 75 | 75/100 | 37/100 | 15/100 | 5/100 |
The S=49→50 fumble jump is the wrinkle worth pinning. It's not a mistake in the rule — it's how the threshold band is defined. A character at skill 49 fumbles on 99-100 (2%); at skill 50 they fumble on 96-100 (5%). Crossing the 50% threshold is generally a buff (more passes, more extremes) but the fumble rate goes up by 3 percentage points.
Combat damage — standard dice grammar
CoC weapon damage uses regular dice expressions
(weapon_dice + DB where DB is the Damage Bonus
table from the rulebook — typically +1d4 for
average humans, +1d6 for stronger fighters). The
damage analyzer handles these directly:
- /strike/1d8+1d4 — handgun + DB for an average Investigator.
- /strike/2d6+1d4 — shotgun at close range with DB.
The "impale" rule (extreme success with an impaling weapon = max damage + reroll) is a saved-by-rules-text conditional damage rider; not yet a single keyword but composable in the comparison tool.
What the engine doesn't model yet
-
Sanity Markov chain: the gambler's-ruin
pillar frames sanity loss as a random walk on a pool, but
the engine doesn't yet have a dedicated
sanity_chainanalyzer. The pillar uses existing rounds_to_kill machinery analogically. - Opposed checks: CoC contests where both sides roll d100. The engine analyzes single tests; opposed composition is a future ship.
- Pushed rolls: failed checks can be retried at the cost of harder consequences on a second failure. State chain — out of v1 scope.
- Impale / multi-attack damage chains: the max-damage-on-extreme rider isn't a single keyword yet; compose manually.