reikhelm/deepfall/FINDINGS.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

8.3 KiB
Raw Permalink Blame History

DEEPFALL — Simulation Findings

What the balance-by-bot rig actually said, after 9 tuning iterations over ~thousands of seeded games per sweep. This is the honest post-mortem: what the design got right, what the sim broke, what I fixed, and what's still open.

The deliverable was never "a perfectly balanced game." It was: design a mashup, then let a simulator tell me the truth about it. It did.


1. The headline

Final config (configs/default.json), 2000 seeds × 24 seat-permutations per strategy sweep, 2000 games per combo:

Metric Result Verdict
Game length 10.9 rounds (1011) in the 814 target band; the Maw ends it cleanly, every time
Victory margin 13.7% of winner's score tight — no runaway leader
Feel-bad rate (Taken w/o Last Stand) 0.00 / game the one true feel-bad is fully designed out
Deep content reachability only 6.5% of deep-floor claims by round-1 parties the deep game is not decided at game start
Combo spread (best worst of 36) 32.5 pts (8.1%40.6%) 🟡 every combo viable; still a soft Lineage/Calling tilt
Strategy band WorthyEnd 46.7 / PressOn 25.8 / Balanced 24.8 / CashOut 2.8 🟡 aggression is the strongest line; pure passivity is weak

Three of the design's central promises held up under simulation — no runaway, no elimination feel-bad, and a living deep game. Two things stayed stubborn — the draft has a soft power tilt, and the four play-styles aren't a flat band. Both are understood (below), not mysterious.


2. The three biggest things the sim caught (and the fixes)

A. Value and time were correlated → the arc fizzled and the back half ran away

Room glory was base × depth and the Maw eats deepest-first, so the richest rooms scored earliest; the game crescendo'd in the middle and ended on its cheapest floors, and the Worthy-End catch-up valve closed once the deep floors were gone. Both critics flagged it before a line of code ran.

Fix (the single best rule in the game): on collapse, a fraction of each room's unclaimed value rises to the floor above as "refugee plunder." The Maw's frontier is now always the swelling hot zone, all the way to Floor 1. The data confirms it: the claim distribution is non-monotonic — Floor 4 (162k claims) draws more than Floor 3 (96k), because value pools wherever the frontier sits, and Floor 1 draws the most of all as everyone converges for the finale. The endgame crescendos instead of fizzling, and margins stayed tight (13.7%).

B. The deep game was nearly a game-start-only contest

A fresh party from the Cash-Out loop entered at Floor 1 and physically could not reach the deep live floor before it collapsed (the balance critic walked the move-math). So the Decline loop would have fed you parties that could only farm shallow rooms.

Fix: re-entering parties insert at max(1, frontier 1). Result: only 6.5% of deep-floor claims come from round-1 parties — i.e. 93.5% of the deep game is played by later-drafted crews. The Decline loop stays relevant the whole game.

C. The multi-collapse feel-bad

while accum ≥ 1.0 could eat two floors in one round late-game and Take a "flee next turn" party with no Last Stand. Fix: cap one collapse per round (carry the remainder). The feel-bad rate is a flat 0.00 across every config tested since.


3. The balance-by-bot trajectory

Each row is a tuning pass; the simulator drove every decision. Regressions are left in on purpose — they're the method working (a change that looked good and the data vetoed).

Iter Lever changed Combo spread Weakest combo WorthyEnd / CashOut strat
1 initial design 77.8 0.5% 41.6 / 1.8
2 sub-linear depth; neuter ignore_threat; mixed combo field 59.8 44.8 / 0.0
3 Champion-recruit Wardancer; Cautious plays the frontier 46.5 44.8 / 3.1
4 half-strength Fallen markers 56.2 44.0 / 3.5
5 revert Fallen; cap marker overflow; raise dividend 47.2 37.0 / 5.0
6 bots compare worthy-vs-bank payouts 55.5 38.5 / 4.2
7 Wardancer +1 marker strength; revert dividend 43.4 4.9% 46.1 / 2.8
8 Dvergar 1 Cunning; Wisp gets a Warrior 35.9 5.0% 45.4 / 2.8
9 Dvergar 1 Faith (gentler); trim Reaver 32.5 8.1% 46.7 / 2.8

Net: combo spread 58% (77.8 → 32.5), weakest combo ×16 (0.5% → 8.1%), and the dead cells (Sylphid ~1%, Wardancer ~0.3%) all revived into the playable band.

The two ⚠ regressions taught real lessons: half-strength Fallen markers (iter 4) handed Warden's ×2 markers an immortal hold and spiked it to 56%; and making bots compare exit payouts (iter 6) — correct in itself — exposed that Wardancer's whole value was a rare conditional, which is what forced the iter-7 fix.


4. Final combo heatmap (focal combo as Balanced vs. a mixed random field)

           Reaver     Lorekeeper Wardancer  Hierophant Warden     Prospector
  Dvergar  40.6%      33.7%      18.4%      32.5%      30.9%      31.6%
  Wisp     20.6%      15.9%      11.9%      14.5%      14.4%      14.2%
  Revenant 17.3%      13.9%       8.9%      12.2%      12.7%      11.7%
  Gnoll    31.7%      28.7%       9.6%      23.9%      24.4%      24.8%
  Sylphid  23.3%      18.8%      12.4%      15.0%      15.3%      14.6%
  GolemBorn24.9%      20.4%       8.1%      19.6%      19.1%      32.0%

  Lineage avg : Dvergar 31.2  Gnoll 23.9  GolemBorn 20.7  Sylphid 16.6  Wisp 15.3  Revenant 12.8
  Calling avg : Reaver 26.4  Lorekeeper 21.9  Prospector 21.5  Hierophant 19.6  Warden 19.5  Wardancer 11.6

(Mean focal win-rate sits below 25% because the focal always plays the generalist Balanced bot while the field includes the more aggressive WorthyEnd/PressOn strategies — so read the vs-mean column in combo_winrates.csv, not the raw 25% line.)


5. What's still open (and why)

The Cash-Out vs. Worthy-End substitution (the deepest finding)

The strategy band stays lopsided (WorthyEnd 46.7%, CashOut 2.8%) for a structural reason the sim made undeniable: for a glory-maximizing agent, Cash-Out and Worthy-End are substitutes — both convert a doomed party into points. So every lever that helps the safe banker (e.g. the depth dividend) directly nerfs the martyr, and vice-versa. They sat on a trade-off frontier across iterations 510: I could balance the strategies or the Wardancer calling, but not both with the same worthy_factor.

The intended differentiator — Worthy-End's denial (the Maw salts your held rooms so no rival inherits them) — is real in the rules but invisible to the bots, because a win-rate maximizer doesn't model the opponent's loss, and you mostly salt rooms you were soloing anyway. This is the headline design lesson: the two exits only become a true triangle (not a binary) when denial is made to matter to your own score — e.g. score denial explicitly (gain a cut of the value you deny), or make Worthy-End pay on cumulative depth dived across the whole game (rewarding a play-pattern banking can't copy). That's the first thing I'd prototype next.

The soft draft tilt

Dvergar (threat-reduction is just broadly useful) and Reaver (loot-on-claim is a flywheel) sit on top; Revenant and Wardancer sit low. No combo is dead (worst is 8.1%), so the draft is spicy, not solved — acceptable for a Small-World-style game where some combos are stronger — but a shipping version would want Dvergar's hook costed harder and Revenant's Dread-to-Mettle made to actually pay.

Pacing variance is too tight

Game length is 1011 rounds with almost no spread. Real push-your-luck wants more variance in when the Maw breaks through. The maw_dread_push and press_dread_push knobs are the levers; I'd widen them and accept a 914 spread.


6. Reproduce

cd deepfall
cargo test                                       # 11 tests: determinism, pacing, no-double-count
cargo run --release -p deepfall-sim -- --out out --seeds 2000
python3 analysis/analyze.py out                  # the heatmap above, no dependencies

Every number here is a pure function of (configs/default.json, seed) — same input, same output, on any machine.