feat(deepfall): live 6-agent playtest + playtest-driven fixes
Ran an observed 6-player game with persona AI players (one per Lineage/Calling) via a Workflow (72 agents): avgFun 7.3/10, 6/6 would replay, tight 84-82-75 finish. PLAYTEST.md captures the session + designer's assessment. Findings the Monte-Carlo bots structurally missed: - the careful/safe line feels UNFAIR, not just weak (banker finished last and resented it) — three human angles confirm the sim's Cash-vs-Worthy substitution finding - humans hit the no-Last-Stand feel-bad 6x where rational bots hit it 0x - deep-collapses-first geometry confuses newcomers (component fix) Fixes (re-validated, 1000 seeds): WorthyEnd strategy 46.7 -> 42.5%, safe lines lifted, 6p margin to 9.5%. - Desperate Strike: a no-Last-Stand Take salvages 1/3 unbanked loot - Extraction bonus: +3 glory per crew retired alive - RULEBOOK: "Maw eats NEXT" indicator as a component rule Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c2420f8cd6
commit
1a712e0c22
5 changed files with 125 additions and 2 deletions
99
deepfall/PLAYTEST.md
Normal file
99
deepfall/PLAYTEST.md
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
# DEEPFALL — Live 6-Player Playtest (2026-06-19)
|
||||||
|
|
||||||
|
A complement to the Monte-Carlo balance sweeps (`FINDINGS.md`): six **persona AI
|
||||||
|
players** — each a different *kind* of human, each piloting a different one of the six
|
||||||
|
Lineages and six Callings — sat at a virtual table and played a full game, making real
|
||||||
|
in-character decisions refereed by a faithful rules engine, then reflected on the fun.
|
||||||
|
|
||||||
|
This is the thing the bots can't measure: **how it *feels*.**
|
||||||
|
|
||||||
|
## The table
|
||||||
|
|
||||||
|
| Player | Archetype | Company | Final |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Vex** | the spoiler (competitive, denial) | Dvergar Hierophant | 🥇 84 |
|
||||||
|
| **Rook** | the gambler (chaos, push-luck) | Sylphid Wardancer | 🥈 82 |
|
||||||
|
| **Mina** | the min-maxer (cold optimizer) | Wisp Prospector | 🥉 75 |
|
||||||
|
| **Pip** | the first-timer | Gnoll Reaver | 59 |
|
||||||
|
| **Tav** | the roleplayer (story over winning) | Revenant Lorekeeper | 50 |
|
||||||
|
| **Greta** | the cautious banker | GolemBorn Warden | 30 |
|
||||||
|
|
||||||
|
11 rounds, clean ending on Floor 1. Top three within **9 glory** (84-82-75), 2 lead
|
||||||
|
changes, 5 contested collapses, 2 Worthy Ends.
|
||||||
|
|
||||||
|
## The verdict
|
||||||
|
|
||||||
|
**avgFun 7.3/10 · 6 of 6 would play again.** The game is *fun* — its problems are
|
||||||
|
balance and clarity, not fun. The drama landed exactly where it was designed to:
|
||||||
|
|
||||||
|
- **Rook's round-8 Last Stand** — Maw one tick away, zero loot, stands up and declares
|
||||||
|
anyway, rides the Hall collapse to 22 glory while a rival is swallowed for nothing:
|
||||||
|
*"The Maw blinked first."* Their favorite moment of the night. **The three-exit triangle
|
||||||
|
produces real table theatre.**
|
||||||
|
- **Pip (the newbie) literally screamed** when a Den he'd half-forgotten he was holding
|
||||||
|
paid out 10 glory at collapse. *"I did not understand what happened. Everyone looked at
|
||||||
|
me. Worth it."* **The collapse-scoring surprise is delightful even when you don't fully
|
||||||
|
get the rules.**
|
||||||
|
- **Tav (the roleplayer)** stood in a burning Library as the floor fell and walked out
|
||||||
|
with 24 glory of "ancient marginalia." The theme carries.
|
||||||
|
|
||||||
|
## What it caught (and how it maps to the bot data)
|
||||||
|
|
||||||
|
**1. The careful line is under-rewarded — and it feels *unfair*, not just weak.**
|
||||||
|
Greta never lost a crew and finished *last*: *"The scoring punishes the careful and
|
||||||
|
rewards the reckless. That is not a game, that is a bad lesson."* Mina played the
|
||||||
|
provably-optimal cash-out line, found it **joyless** (*"that's not daring, that's
|
||||||
|
invoicing"*), and still lost to riskier players. Even **the winner, Vex**, flagged it:
|
||||||
|
*"Rook got eaten twice and still finished two points behind me — that gap is not
|
||||||
|
satisfying."* → This is exactly the sim's `CashOut`-strategy weakness (2.8%) and the
|
||||||
|
open Cash-vs-Worthy *substitution* finding — now confirmed from **three independent human
|
||||||
|
angles**, with the emotional weight the win-rate number can't convey.
|
||||||
|
|
||||||
|
**2. Humans hit the feel-bad 6× where the bots hit it 0×.** The Monte-Carlo bots cash out
|
||||||
|
defensively and almost never get Taken without a Last Stand (0.00/game). But the
|
||||||
|
*gambler*, the *roleplayer*, and the *newbie* walked into the silent death **six times**
|
||||||
|
this game. Rook and Tav independently demanded the same thing: *"Every death deserves a
|
||||||
|
punchline."* **This is the headline meta-finding: the live-agent playtest caught a problem
|
||||||
|
the perfect-play simulator structurally hid.**
|
||||||
|
|
||||||
|
**3. The deep-collapses-first geometry confuses newcomers.** Pip, round 5: *"Nobody told
|
||||||
|
me the floors count DOWN. I thought it went up. Why does it go down. WHO DESIGNED THIS."*
|
||||||
|
Got caught thinking he had five turns when he had one. → The fun-critic predicted exactly
|
||||||
|
this ("richest = doomed first" fights loss-aversion intuition); the playtest makes it a
|
||||||
|
hard **component requirement**.
|
||||||
|
|
||||||
|
**4. Contests feel flat.** Mina and Vex both noted that piling markers into a room is a
|
||||||
|
free hedge and ties split evenly: *"two markers beats four markers because the split is
|
||||||
|
flat — that is irrational"* (Mina); *"flooding a room should be a genuine gamble"* (Vex).
|
||||||
|
|
||||||
|
## Changes made (and re-validated in the sim)
|
||||||
|
|
||||||
|
| Change | Why | Knob |
|
||||||
|
|---|---|---|
|
||||||
|
| **Desperate Strike** — a no-Last-Stand Take salvages ⅓ of unbanked loot | Rook & Tav: a silent death shouldn't be a humiliating zero | `taken_salvage_frac = 0.33` |
|
||||||
|
| **Extraction bonus** — flat +3 glory per crew retired alive | Greta: reward surviving the delve, not just dying deep | `cashout_extraction_bonus = 3.0` |
|
||||||
|
|
||||||
|
Re-sweep (1000 seeds): the strategy band tightened — WorthyEnd **46.7 → 42.5%**, the
|
||||||
|
safe/engine lines lifting (PressOn 25.8 → 31.9, CashOut 2.8 → 3.7); 6-player victory
|
||||||
|
margin fell to **9.5%** (even tighter top-of-table). Pacing, combo spread, and the
|
||||||
|
0.00-feel-bad-for-bots all held.
|
||||||
|
|
||||||
|
## Recommended next (not yet built)
|
||||||
|
|
||||||
|
- **Marker-weighted contest shares** (Mina) — split a contested room's Glory in proportion
|
||||||
|
to marker strength, and hand every contestant a Dread token (Vex) so piling in is a real
|
||||||
|
gamble. The cleanest fix for finding #4, and it further differentiates Warden.
|
||||||
|
- **The "Maw eats NEXT" indicator** (Pip) — a big, unmissable physical marker on the next
|
||||||
|
floor to collapse. A *component* fix for the onboarding cliff; folded into the rulebook.
|
||||||
|
- **The deeper Cash-vs-Worthy fix** (finding #1) — the flat extraction bonus helps but
|
||||||
|
doesn't fully solve it. The real lever (per `FINDINGS.md`) is making *denial* score for
|
||||||
|
you, or paying Worthy End on *cumulative* depth dived. The next prototype.
|
||||||
|
|
||||||
|
## The bottom line for 6 humans
|
||||||
|
|
||||||
|
It **scales** — a clean 11-round game with a tight 3-way finish and real lead changes —
|
||||||
|
and it **delivers the fantasy**: the gambler got their blaze of glory, the newbie got their
|
||||||
|
scream, the roleplayer got their story, the spoiler got their win by playing the table.
|
||||||
|
With the survival incentive, the Desperate Strike, and a big "Maw-eats-next" marker, this
|
||||||
|
is a table-ready 6-player game. The one thing still worth a design pass before a print run
|
||||||
|
is making the *careful* player feel as clever as the reckless one feels alive.
|
||||||
|
|
@ -239,6 +239,11 @@ fuel. This is your in-game engine (Quarriors' dice-building).
|
||||||
|
|
||||||
## 9. The Maw & player count
|
## 9. The Maw & player count
|
||||||
|
|
||||||
|
> **Component rule (playtest-critical):** the Maw marker must **loudly flag the next floor
|
||||||
|
> to collapse.** New players reliably forget the dungeon collapses *bottom-up* and think
|
||||||
|
> they have more time than they do — give the Maw a big, unmissable "EATS THIS FLOOR NEXT"
|
||||||
|
> pointer. (See `PLAYTEST.md`, finding #3.)
|
||||||
|
|
||||||
The Maw rises **1 floor per round**, **+1** whenever the shared **Dread track** fills (then
|
The Maw rises **1 floor per round**, **+1** whenever the shared **Dread track** fills (then
|
||||||
it resets). Players add Dread to the track every time they **Press On** and whenever
|
it resets). Players add Dread to the track every time they **Press On** and whenever
|
||||||
uncanceled ☠ is rolled — so the table's collective greed literally pulls the doom down
|
uncanceled ☠ is rolled — so the table's collective greed literally pulls the doom down
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,8 @@
|
||||||
|
|
||||||
"cashout_depth_dividend": 2.0,
|
"cashout_depth_dividend": 2.0,
|
||||||
"cashout_min_bank": 12.0,
|
"cashout_min_bank": 12.0,
|
||||||
|
"cashout_extraction_bonus": 3.0,
|
||||||
|
"taken_salvage_frac": 0.33,
|
||||||
"worthy_factor": 2.5,
|
"worthy_factor": 2.5,
|
||||||
"worthy_held_bonus": 0.0,
|
"worthy_held_bonus": 0.0,
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,12 @@ pub struct Config {
|
||||||
pub cashout_depth_dividend: f64,
|
pub cashout_depth_dividend: f64,
|
||||||
/// The Cautious bot Cashes Out once its bankable value clears this bar.
|
/// The Cautious bot Cashes Out once its bankable value clears this bar.
|
||||||
pub cashout_min_bank: f64,
|
pub cashout_min_bank: f64,
|
||||||
|
/// Cash-Out flat bonus per crew retired "with boots on" — a survival incentive the
|
||||||
|
/// 6-human playtest demanded (the careful banker finished last and felt cheated).
|
||||||
|
pub cashout_extraction_bonus: f64,
|
||||||
|
/// Fraction of unbanked loot salvaged when Taken with **no** Last Stand — the
|
||||||
|
/// "Desperate Strike" so a silent death isn't a humiliating zero (playtest ask).
|
||||||
|
pub taken_salvage_frac: f64,
|
||||||
/// Worthy-End spike = `worthy_factor * deepest_floor` (+ optional held bonus).
|
/// Worthy-End spike = `worthy_factor * deepest_floor` (+ optional held bonus).
|
||||||
pub worthy_factor: f64,
|
pub worthy_factor: f64,
|
||||||
/// Extra Worthy-End credit per point of held room value. **0 by default** to
|
/// Extra Worthy-End credit per point of held room value. **0 by default** to
|
||||||
|
|
@ -97,6 +103,10 @@ impl Default for Config {
|
||||||
// shallow parties) and cratered to a 1.8% win rate; banking richer hauls
|
// shallow parties) and cratered to a 1.8% win rate; banking richer hauls
|
||||||
// before retiring makes the safe line viable.
|
// before retiring makes the safe line viable.
|
||||||
cashout_min_bank: 12.0,
|
cashout_min_bank: 12.0,
|
||||||
|
// Playtest-driven (2026-06-19): reward surviving the delve, and give a silent
|
||||||
|
// death a small consolation. See PLAYTEST.md.
|
||||||
|
cashout_extraction_bonus: 3.0,
|
||||||
|
taken_salvage_frac: 0.33,
|
||||||
// v2 (iter 3-4): 4 -> 2.5. At 4 the generic Worthy End (any deep party on a
|
// v2 (iter 3-4): 4 -> 2.5. At 4 the generic Worthy End (any deep party on a
|
||||||
// Throne) made the WorthyEnd strategy a 45% king; lowering the base while
|
// Throne) made the WorthyEnd strategy a 45% king; lowering the base while
|
||||||
// raising the Wardancer multiplier (now 2.2x) shifts the payoff onto the
|
// raising the Wardancer multiplier (now 2.2x) shifts the payoff onto the
|
||||||
|
|
|
||||||
|
|
@ -542,7 +542,9 @@ impl Game {
|
||||||
/// Cash Out: bank loot + depth dividend, convert held markers to Fallen, retire.
|
/// Cash Out: bank loot + depth dividend, convert held markers to Fallen, retire.
|
||||||
fn cash_out(&mut self, pi: usize) {
|
fn cash_out(&mut self, pi: usize) {
|
||||||
let party = self.players[pi].party.take().unwrap();
|
let party = self.players[pi].party.take().unwrap();
|
||||||
let raw = party.unbanked + self.cfg.cashout_depth_dividend * party.deepest as f64;
|
let raw = party.unbanked
|
||||||
|
+ self.cfg.cashout_depth_dividend * party.deepest as f64
|
||||||
|
+ self.cfg.cashout_extraction_bonus;
|
||||||
let banked = raw * self.comeback_mult(pi);
|
let banked = raw * self.comeback_mult(pi);
|
||||||
self.players[pi].glory += banked;
|
self.players[pi].glory += banked;
|
||||||
self.players[pi].cashouts += 1;
|
self.players[pi].cashouts += 1;
|
||||||
|
|
@ -598,7 +600,12 @@ impl Game {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The one true feel-bad: lose unbanked loot and all active territory.
|
// Taken with no Last Stand. Still the worst exit (territory lost, no depth
|
||||||
|
// dividend), but a "Desperate Strike" salvages a fraction of unbanked loot so
|
||||||
|
// it isn't a humiliating zero — the playtest's most-requested change.
|
||||||
|
let salvage = party.unbanked * self.cfg.taken_salvage_frac;
|
||||||
|
let cm = self.comeback_mult(pi);
|
||||||
|
self.players[pi].glory += salvage * cm;
|
||||||
self.players[pi].takes_no_laststand += 1;
|
self.players[pi].takes_no_laststand += 1;
|
||||||
for (d, ri) in &party.held {
|
for (d, ri) in &party.held {
|
||||||
let fi = Self::floor_idx(*d);
|
let fi = Self::floor_idx(*d);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue