reikhelm/deepfall/DESIGN.md
Parley Hatch 277c158378 feat(deepfall): board-game design + balance-by-bot simulator
DEEPFALL: a tabletop game mashing up 9 loved games (SmashUp, Dragon
Rampage, Blood Rage, Catan, EverQuest, Daggerfall, Eye of the Beholder,
Quarriors, SmallWorld). Core fusion = SmallWorld decline + Blood Rage
glorious death -> three exits (Cash Out / Press On / Worthy End) under a
rising Maw that collapses a reikhelm dungeon floor-by-floor.

- deepfall-core: pure deterministic engine (vendored ChaCha8, 11 tests)
- deepfall-sim: balance-by-bot sweeps -> CSV/JSON
- analysis/analyze.py: stdlib ASCII heatmap (no deps)
- DESIGN.md (rules + post-critique revisions), FINDINGS.md (9-iteration
  balance journey), README.md
- reikhelm-core/examples/sample_dungeon.rs: dumps a real generated board

Method: adversarial design critics -> build -> simulate -> tune. 9
iterations compressed combo spread 77.8 -> 32.5 pts (all 36 combos
viable). Fixed deep-content lockout (value rises with the Maw), the
multi-collapse feel-bad (1 collapse/round cap; feel-bad now 0.00/game),
and dead combos. Open finding: Cash-Out and Worthy-End are substitutes
for a glory-maximizing bot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 22:49:33 -06:00

21 KiB
Raw Permalink Blame History

DEEPFALL — Design & Simulation Spec

This is two documents in one:

  • Part A — The Game. A playable tabletop design (the rulebook in prose).
  • Part B — The Simulation. The faithful abstraction the Rust engine implements, so the balance numbers mean something. Where the sim simplifies the tabletop rules, it says so explicitly.

The vocabulary (room themes, depth, connectivity, doors) is borrowed straight from reikhelm-core so a printed board is a real generated dungeon.


Part A — The Game

1. The fantasy & the arc

Reikhelm is a vertical megadungeon of stacked Floors. Floor 1 is the Threshold (the surface mouth, safe, poor). The deeper the floor, the richer and deadlier it is. Beneath the deepest floor waits the Maw — a rising hunger that devours the hold one floor at a time, from the bottom up. The richest plunder is therefore the most doomed: a deep Vault is worth a fortune and will be eaten first.

Rival delve-companies (players) descend to plunder before it's all gone. You don't win by surviving — you win by Glory, and the surest way to bank a fortune is to spend a party deliberately: send them quietly to ground with their loot, or hurl them into a Worthy End at the bottom and let the Maw take them gloriously.

The game lasts until the Maw eats Floor 1. Most Glory wins. Nobody is ever eliminated.

2. Your delve-company: Lineage × Calling

At the start, and every time you bring in a fresh party, you draft two halves that smash together (Smash Up / Small World / EverQuest / Daggerfall). Each half grants an advantage and a cost — there are no free builds (Daggerfall's custom-class trade-off).

Lineages (the "race" — sets your starting dice bag + a passive)

Lineage Advantage Cost (disadvantage) Bag leans
Dvergar +1 Steel resolved each turn (sturdy) 1 Move (slow) Warrior
Wisp +1 die drawn each hand (nimble) Setbacks hit double (fragile) Scholar
Revenant Ignore the first Dread each roll (fearless dead) Faith 1 (no living soul) Balanced, dread-proof
Gnoll +Loot from combat rooms (Den/Forge) (scavenger) Cunning 1 (clumsy at locks) Warrior/Rogue
Sylphid +1 Move (fleet) Steel 1 (frail) Scholar/Rogue
Golem-born Setback-resistant (rarely loses dice) Cannot recruit Relic dice (rigid) Big, balanced, slow to grow

Callings (the "class/faction" — special ability + recruit pool + scoring lean)

Calling Special Recruits Scoring lean
Reaver Pillage: extra loot from every room you claim Warrior dice (cheap) Loot
Lorekeeper Open Vaults solo (no trinity needed); +Cunning at locks Scholar dice Deep vaults
Wardancer Worthy-End payout +50%; cheap Moves Rogue dice Glorious death
Hierophant Cancel extra Dread; heal Setbacks; cheap denizen rep Priest dice Longevity (persist)
Warden Your control markers count double for majority Champion dice Area control
Prospector Cheaper recruits, bigger hand, can find Relic dice Relic dice Engine / snowball

6 × 6 = 36 companies. Natural synergies emerge (Dvergar Reaver = smash-and-grab; Wisp Lorekeeper = vault cracker; Sylphid Wardancer = fast martyr; Golem-born Warden = the immovable wall) — the simulator's job is to confirm none of them is solved.

3. The dice bag (Quarriors) & the six faces

Your bag is a pile of six-sided delve dice. Each face shows one symbol:

Symbol Meaning
Steel Combat power — clear a room's threat
🜏 Cunning Wits — open mechanisms / locks; buy extra Moves
Faith Spirit — cancel Dread; heal Setbacks; appease denizens
💰 Greed Loot — turns into banked Glory
Mettle Wild currency — recruit new dice; pay for actions
Dread Peril — accumulates; too much triggers a Setback

Different die kinds weight these faces differently. Starting bags are mostly Recruit dice (balanced, one of each). You grow your bag in play by recruiting stronger kinds (Warrior/Scholar/Priest/Rogue/Champion/Relic) from the dungeon's offer.

A turn

  1. Draw a hand from your bag (default 4 dice; modified by Lineage/Calling).
  2. Roll them.
  3. Resolve Dread — cancel Dread with Faith (and Lineage dread-resist). If Dread still standing ≥ the Dread threshold, suffer a Setback (lose a die to your bag's wound slot, or lose unbanked loot).
  4. Act with your symbols — Move between floors, clear & claim a room, take loot, recruit a die.
  5. Press On? (push-your-luck) — you may draw & roll another hand for more symbols this turn. Every press nudges the Maw and stacks more Dread risk. Stop whenever you like; greed is punished by the dice and by the clock.
  6. Exit decision — see §6.

4. The dungeon: rooms, themes, mechanisms (EotB / Catan / reikhelm)

Each Floor holds several rooms, each with a theme from reikhelm's palette. A room's base Glory and its threat/lock scale with its theme and its depth:

Theme Base value Threat (Steel) Lock (Cunning) Note
Threshold 1 0 0 safe mouth
Stone 2 1 0 plain
Cistern 3 1 1 flooded
Den 3 3 0 monster lair (Gnoll loot bonus)
Crypt 3 2 1 undead
Hall 4 1 1 pillared
Library 4 1 2 Cunning-gated, loot-rich
Forge 5 4 1 lava, produces Steel
Vault 6 2 3 needs the trinity (Steel+Cunning+Faith present) unless Lorekeeper
Throne 7 5 2 boss; deepest

A room's collapse Glory = base_value × depth. A Vault on Floor 6 is worth 6 × 6 = 36 to whoever holds the majority when Floor 6 falls. Holding a room also makes it produce each turn you roll its matching symbol (Catan): a held Forge throws extra Steel, a Library extra Cunning, a Vault extra Greed — your engine grows on the ground you keep, right up until the Maw takes it.

Claiming a room: spend Steel ≥ threat (fight through) and Cunning ≥ lock (crack the mechanism). Vaults additionally require trinity presence (you produced at least one Steel and one Cunning and one Faith this turn) — the EverQuest holy-trinity made literal — unless your Calling is Lorekeeper. Claiming drops a control marker.

5. The Maw: the doom clock (Blood Rage / Dragon Rampage)

The Maw begins below the deepest floor and rises. Each round it advances; its speed ramps up over the game and is pushed faster by the total Dread everyone rolled that round (your collective greed feeds it). When the Maw reaches a floor:

  1. The floor collapses and scores. For each room, the majority marker-holder takes its Glory (base × depth). Ties split, rounding down. A Warden's markers count double here; a party that went to ground leaves silent Fallen markers that still count.
  2. Anything still on that floor is Taken by the Maw (see §6).

Because collapse runs bottom-up, the deep treasure has the shortest window. The game ends when Floor 1 collapses.

6. The three exits (the heart of the game)

Every party, every turn, faces the same triangle. This is the design's core, fusing Small World's Decline with Blood Rage's glorious death:

  • Cash Out / "Go to Ground" (Small World Decline). End this party's adventuring. Bank their unbanked loot + a depth dividend (deepest floor reached × factor). Their control markers become permanent Fallen markers — still counted at every future collapse, but they can't move or act. Then draft a fresh Lineage×Calling and re-enter at the Threshold. Safe, repeatable, keeps your territory working for you from beyond the grave.

  • Press On / Persist (Catan/Quarriors engine). Keep the party active: keep recruiting, keep holding rooms, keep producing. More upside, but the Maw is climbing and a party caught flat-footed loses everything unbanked and its territory.

  • Worthy End (Blood Rage Valhalla). Declare a Last Stand (free if you hold a Throne or Vault, or via Wardancer). Now, when the Maw Takes your active party, you score a huge Worthy End bonus = deepest_floor × worthy_factor + held room values (×1.5 for Wardancer). This is the single biggest payout available — and it's all-or-nothing: you lose the party and its unbanked loot. Sometimes you want the Maw to eat you, as deep as possible, in a blaze.

Getting Taken without a Last Stand is the one true feel-bad in the game (lose loot

  • territory for nothing). The simulator measures how often bots fall into it — a healthy design keeps that rare, because the exits above are always offered.

7. Denizen reputation (Daggerfall / EverQuest — light)

A few denizen factions live in the deep (the Deep Cult, the Gnoll Warrens, the Revenant Court). Clearing their themed rooms angers them (reputation down → they raise the threat of their rooms against you); making offerings (spend Greed/Faith) raises reputation → unlocks premium recruit dice and safer passage. One short track per denizen, two or three thresholds. (Kept deliberately light; the sim models it as a small threat modifier so it doesn't dominate the balance picture.)

8. Why this fixes the originals' worst parts

  • No runaway leader. A trailing player dives recklessly, takes a Worthy End or a deep Cash-Out for a glory spike, and resets — catch-up is built into the core loop, not bolted on. Leaders who over-persist feed the Maw and lose territory.
  • No elimination, no downtime. A dead party is glory and silent territory, not a removed player. Turns are short; you're always choosing.
  • Variance with a floor. The dice can betray you, but Faith mitigates Dread and Cash-Out lets you lock in value before luck turns — Quarriors' whiff is defanged.
  • Conflict without spite. You fight for majorities at collapse, never to destroy an opponent's hand. The shared "robber" is the Maw, and it is perfectly fair.

Part B — Simulation model

The engine in deepfall-core is a faithful abstraction of Part A, not a literal table simulator. It keeps everything that drives the six balance questions (the dice faces, the bag-building, the three exits, the bottom-up Maw, the area-control scoring, the Lineage×Calling asymmetry) and abstracts the fiddly bits (exact board geometry, turn-by-turn movement pathing, denizen micro-management) into clean numeric rules. Each abstraction is called out below.

B1. State

  • Dungeon: floors[1..=D], default D = 6. Floor f has R rooms (default 4), drawn from the theme table weighted toward richer themes at greater depth. Each Room has theme, base_value, threat, lock, produces(symbol), markers: Vec<Marker>. (Abstraction: rooms are an unordered set per floor; we don't model intra-floor pathing or the corridor graph — only "which floor are you on" matters for the Maw.)
  • Maw: accum: f64, frontier: u32 = deepest uncollapsed floor (starts at D).
  • Players: glory, and an optional active Party.
  • Party: lineage, calling, bag: Vec<DieKind>, floor (starts at 1), unbanked_loot, deepest_reached, last_stand: bool, held: Vec<RoomRef>.
  • Marker: { owner, strength, fallen }. Active strength = 1 (×2 for Warden); Fallen strength = 1.

B2. Round structure

Each round, in player id order, every player takes a turn (B3). After all turns, the Maw advances (B4) and any collapses resolve (scoring + Takes). Repeat until the frontier passes Floor 1. Then final scoring (B5).

B3. A turn (the engine's core resolution)

  1. Decide intent from the controller (B6): a target floor and a risk appetite.
  2. Move: moves = 1 + lineage_move + calling_move; optionally spend Cunning/Mettle for extra moves if the bot wants to dive. Step floor toward the target (clamped to [1, frontier] — you cannot stand on an already-eaten floor). Update deepest_reached.
  3. Roll the hand: draw hand_size dice (+lineage/calling), roll each to a face, summing the six symbol totals. The controller may Press On (B6) to draw & roll additional hands, each appending to the totals and adding press_dread_push to the round's Maw pressure.
  4. Dread: dread -= faith_cancel (Faith spent to cancel, plus Hierophant bonus and Revenant's "ignore first"). If remaining dread ≥ dread_threshold, a Setback: lose one die from the bag (Golem-born resists; Wisp loses two) or, if the bag is minimal, lose setback_loot unbanked loot. Count it.
  5. Claim: among rooms on the current floor not already held by this party, pick the highest-value one the party can afford this turn (Steel ≥ threat, Cunning ≥ lock, trinity-or-Lorekeeper for Vault). Place/strengthen a marker; add to held. (Abstraction: one claim attempt per turn; "contest" = simply adding your own marker, majority resolved at collapse.)
  6. Loot: `unbanked_loot += greed × loot_per_greed × depth_factor(floor)
    • production(held) + reaver/gnoll bonuses`.
  7. Recruit: if mettle ≥ recruit_cost and the controller wants to grow, add a die of the Calling's recruit kind to the bag (Prospector discount; Golem-born can't take Relics).
  8. Exit decision (B6): Cash Out, set Last Stand, or persist.

B4. The Maw

After all turns in a round r:

speed   = base_rate + accel * r + dread_push * total_dread_this_round
accum  += speed
while accum >= 1.0 and frontier >= 1:
    accum -= 1.0
    collapse(frontier)          // score rooms (B5) + Take parties on this floor
    frontier -= 1

collapse(f): for each room on f, the owner with the greatest summed marker strength takes base_value × f Glory (ties split, floor-divided). Then every active party whose floor == f is Taken: if last_stand, score the Worthy End bonus (f × worthy_factor + held_value, ×1.5 Wardancer); else lose unbanked loot + all its active markers (a feel-bad event, counted). The party is removed; the player may draft a new one next round at the Threshold.

B5. Scoring

Glory accrues from: room majorities at collapse, Cash-Out banks (loot + depth dividend), and Worthy-End bonuses. At game end (frontier < 1), any still-active party is force-Taken under the same B4 rule (Last Stand pays; otherwise unbanked loot is lost). Highest total Glory wins; we also record the margin (winner runner-up).

B6. Controllers (the bots under test)

Each is a pure function of the public game view. Risk appetite and exit timing are the levers that distinguish them.

  • CashOut (Cautious). Targets a moderate depth; presses on rarely; Cash-Out as soon as banked-worthy loot clears a small bar or the Maw is within safe_margin floors. Rarely sets Last Stand. Models the safe Decline player.
  • PressOn (Greedy/Engine). Recruits hard early; targets rooms to hold; presses aggressively; persists, fleeing upward only when the Maw is one floor away; Cashes Out only when forced. Models the engine-builder who risks the clock.
  • WorthyEnd (Martyr/Diver). Rushes to the deepest reachable floor; sets Last Stand as soon as it holds a Vault/Throne (or is Wardancer); presses hard; rides the floor into a Worthy End. High variance, high ceiling.
  • Balanced (Adaptive). Reads collapses_until_my_floor vs. banked value: Cash-Out when the Maw is close and it has a worthwhile bank; opportunistically converts to a Worthy End when deep and doomed; otherwise presses moderately and grows. The "good human" baseline.

B7. What we measure (written to CSV/JSON; see analysis/)

  • Win rate by strategy (mixed tables) → dominance check (target: no strategy > ~40% in 4-player, none < ~12%).
  • Win rate by Lineage and by Calling → asymmetry fairness (flag any > ~1.6× the mean).
  • Game length (rounds) → pacing (target band ~814).
  • Victory margin as a fraction of the winner's score → runaway check (lower = tighter).
  • Feel-bad rate: Takes-without-Last-Stand per game → keep low.
  • Cash-Out timing spread: distribution of the depth/round at which winners cashed out → is the Decline decision genuinely live, or is there one correct moment?

Part C — Revisions after adversarial review (v2, what the engine implements)

Two independent reviewers (a "fun/decisions" lens and a "balance/exploits" lens) tore into v1 before any code was written. They converged on the same structural flaw, so v2 folds the high-leverage fixes in. The engine implements v2. The deltas:

  1. Value rises with the Maw (the big one). Problem: glory = base×depth + bottom-up collapse correlated value with time — the climax landed mid-game, the endgame fizzled on cheap floors, and the Worthy-End catch-up valve closed in the back half (no deep floors left to dive into) → a back-half runaway, contradicting the whole pitch. Fix: on collapse, a fraction (value_rise_frac) of each room's unclaimed value rises to the floor above as "refugee plunder." The Maw's frontier is therefore always the swelling hot zone, all the way up — the final Floor-1 collapse is now the biggest scoring event, the arc crescendos, and a trailing player can always dive into the (now-shallow) frontier for a real comeback.

  2. Re-entry near the frontier. Problem: a fresh party from the Cash-Out loop starts at Floor 1 and can never reach the deep live floor before it collapses → Cash-Out self-limited to shallow farming; deep game decided once, early. Fix: re-entering parties insert at max(1, frontier reentry_gap) — the upper floors are eaten anyway, so new delvers drop in near the action. Keeps every party relevant.

  3. One collapse per round (carry the remainder). Problem: while accum ≥ 1.0 could eat two floors in a round late-game, Taking "I'll flee next turn" parties with no Last Stand — a systematic feel-bad. Fix: at most max_collapses_per_round (default 1) collapses per round; the overflow carries in accum. "One floor away" is now an honest promise.

  4. Worthy End = burst + denial, not a bigger Cash-Out. Problem: Cash-Out did almost everything Worthy-End did, safely and repeatably, so the triangle was a binary and Worthy-End read as the loser's button; also a held_value double-count risk. Fix: the two exits now have non-overlapping upside. Cash-Out = the long game (loot + depth dividend + Fallen markers that keep earning). Worthy-End = burst + denial: a guaranteed worthy_factor × deepest_floor spike and the Maw consumes your held rooms clean — their value is removed from the board so no rival inherits it. Now a leader takes a Worthy-End to deny a chaser a 36-point Vault. worthy_held_bonus is 0 by default (no double-count); it's a separate knob.

  5. Variable-strength claims + assault progress. Problem: contest-by-marker-visit was solitaire and un-reactable; deep claims were a ~5% one-turn lottery with no floor. Fix: a claim's marker strength = 1 + symbols invested (Warden ×2), so majority is a live bid; and unmet Steel/Cunning accumulates as assault progress on the room across turns, so a near-miss is never a total zero.

  6. Lineages get behavioral hooks; dead cells fixed. Problem: Lineages were flat stat tweaks (no "two halves surprise you"), and some combos negated themselves (Golem-born Prospector locked out Relics; Gnoll Reaver / Revenant Hierophant stacked the same axis). Fix: Dvergar ignores room threat on its claim (smash-through — now Dvergar Lorekeeper cracks guarded Vaults); Revenant converts Dread→Mettle (peril as fuel, a decision, not redundant immunity); Golem-born's cost is now 1 hand size (orthogonal — and it cancels Prospector's +hand, no dead cell). See content.rs for the final tables.

  7. Sub-linear depth + comeback rubber-band, both as knobs. Room glory is base × (1 + depth_coeff × (depth1)) (depth_coeff tunes the cliff), and comeback_bp optionally scales a trailing party's exit payouts by its glory deficit. Both default conservatively and are what the sweeps actually move.

These are the hypotheses the sim must now confirm: that the triangle is live (no exit

~45% of winners' choices), that deep rooms are contestable but not free, that the runaway is dead (tight margins; real comeback rate), and that the feel-bad is rare.


B8. What the sim deliberately does not model

Intra-floor geometry & corridors; denizen reputation as a full subsystem (folded into a threat modifier); trading/negotiation (no table talk to simulate); the exact card/draft UI. These don't move the six questions above, and adding them would trade clarity for false precision. The balance levers that do matter all live in configs/default.json and in the Lineage/Calling tables in deepfall-core/src/content.rs.