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:

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_chain analyzer. 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.