Compare commits

..

2 commits

Author SHA1 Message Date
1a712e0c22 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>
2026-06-19 01:03:35 -06:00
c2420f8cd6 docs(deepfall): rulebook + 6-player scaling (sim-validated)
- RULEBOOK.md: full playtest rules — turn flow (Delve + communal Maw
  phase), the three exits, claiming/trinity, dice, generator companion.
- 6-player support: rooms-per-floor = player count, Dread-track length
  = 3 + players. deepfall-sim --players diagnostic confirms ~11-round
  games with tight margins and 0.00 feel-bad from 4 to 6 players.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-18 23:33:28 -06:00
6 changed files with 487 additions and 2 deletions

99
deepfall/PLAYTEST.md Normal file
View 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.

295
deepfall/RULEBOOK.md Normal file
View file

@ -0,0 +1,295 @@
# DEEPFALL — Rulebook
*Reikhelm, the deep hold, is sinking into the Maw. Delve fast, hold ground, and know
when to die. The deepest grave is the brightest glory.*
**Players:** 26 · **Time:** 4575 min · **Ages:** 12+
> This is the playtest rulebook. Every number here is grounded in the DEEPFALL balance
> simulator (`deepfall-sim`); see `FINDINGS.md` for what the data said and `DESIGN.md`
> for the deeper why. Where the sim used continuous coefficients, this rulebook rounds
> them to clean tabletop integers.
---
## 1. The idea & how you win
You lead a **delve-company** plundering a collapsing megadungeon. Beneath the deepest
floor waits **the Maw**, a rising hunger that devours the hold one floor at a time, **from
the bottom up** — so the richest, deepest rooms are doomed *first*.
You don't win by surviving. You win by **Glory**, and the surest fortunes come from
spending a company deliberately: send a crew quietly **to ground** with its loot, or hurl
it into a **Worthy End** at the bottom and let the Maw take it gloriously. Nobody is ever
eliminated — a dead crew becomes Glory and silent territory, and you muster a fresh one.
**The game ends when the Maw eats Floor 1. Most Glory wins.**
---
## 2. Components
| Component | Count | Notes |
|---|---|---|
| **Room tiles** | ~48 deck | 10 themes (Threshold → Throne); each shows value, ⚔ threat, 🜏 lock, produce icon |
| **Floor strips** (or trays) | 6 | The dungeon, top (Floor 1) to bottom (Floor 6) |
| **The Maw marker** + **Dread track** | 1 | The doom clock |
| **Delve dice** | 7 types | Recruit / Warrior / Scholar / Priest / Rogue / Champion / Relic (see §5) |
| **Dice bags** | 1 per player | Your bag is your engine |
| **Lineage cards** | 6 | Your "race" half |
| **Calling cards** | 6 | Your "class/faction" half |
| **Player mats** | 1 per player | Party, held rooms, Mettle, hand |
| **Control markers** + **Fallen (skull) markers** | per player | Claim rooms / mark your dead |
| **Mettle & Loot tokens**, **Glory track**, **Denizen rep tracks** | shared | The economy |
The six dice **faces**: ⚔ **Steel** · 🜏 **Cunning** · ✚ **Faith** · 💰 **Greed** · ✦ **Mettle** · ☠ **Dread**.
---
## 3. Setup
1. **Build the dungeon.** Stock the six floor strips, **rooms-per-floor = number of
players** (so a 4-player game has 4 rooms per floor, a 6-player game has 6). Draw room
tiles weighted richer with depth: Floor 1 is mostly Threshold/Stone; Floor 6 holds the
Throne and the Vaults. *(Easiest: use the **reikhelm Generator Companion** — give it a
seed and it prints the exact layout. Every game, a new dungeon.)*
2. **Depth track.** Each floor has a **Depth Value** printed beside it:
| Floor | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| **Depth Value** | ×1 | ×2 | ×2 | ×3 | ×3 | ×4 |
A room's **Glory at collapse = its tile value × its floor's Depth Value.** (A Vault,
value 6, on Floor 6 is worth `6 × 4 = 24`.)
3. **The Maw** starts just below Floor 6. Set the shared **Dread track** to 0; its length
is **3 + number of players** (see §9).
4. **Draft your company.** Each player takes one **Lineage** + one **Calling** (deal 2 of
each, keep 1 of each, or draft) and builds their **starting dice bag** as printed on the
Lineage card.
5. Give each player their markers, a player mat, and 0 Glory. Seed the shared **Recruit
Pool** with dice of each recruitable type. Pick a first player; pass the marker
clockwise each round.
---
## 4. The company: Lineage × Calling
Two halves smash into one identity. Each grants an **advantage** and a **cost** — no free
builds.
### Lineages (the "race" — sets your starting bag + a passive)
| Lineage | Advantage | Cost |
|---|---|---|
| **Dvergar** | Ignore 1 point of a room's ⚔ threat when claiming | 1 ✚ Faith (courts Dread) |
| **Wisp** | Draw +1 die every hand | Bigger hand rolls more ☠ Dread |
| **Revenant** | Convert leftover ☠ Dread into ✦ Mettle (peril as fuel) | Cannot cancel Dread with Faith |
| **Gnoll** | +Loot when you claim a **combat** room (Den/Forge/Crypt/Throne) | 1 ✚ Faith |
| **Sylphid** | +1 Move and +1 🜏 Cunning | Fragile: a Setback sheds an extra die |
| **Golem-born** | Never lose dice to a Setback | 1 hand size (rigid) |
### Callings (the "class/faction" — special + recruit pool)
| Calling | Special | Recruits |
|---|---|---|
| **Reaver** | Pillage: gain Loot every time you claim a room | Warrior (cheap) |
| **Lorekeeper** | Open **Vaults solo** — no trinity needed | Scholar |
| **Wardancer** | Free **Last Stand**; Worthy End **×2.2**; +1 marker strength; +1 Move | Champion |
| **Hierophant** | Cancel +1 Dread; heal Setbacks; +25% held-room production | Priest |
| **Warden** | Your control markers count **double** for majority | Champion |
| **Prospector** | Cheaper recruits; +1 hand; can find **Relic** dice | Relic |
6 × 6 = **36 companies.** They are deliberately *spicy, not flat* — Dvergar and Reaver run
a touch strong, Wardancer a touch swingy — but the sim confirms **every combo is viable**
(no dead picks).
---
## 5. The dice
Your bag holds delve dice. Each is a d6; the types differ only in their face mix:
| Die (color) | ⚔ | 🜏 | ✚ | 💰 | ✦ | ☠ |
|---|---|---|---|---|---|---|
| **Recruit** (grey) | 1 | 1 | 1 | 1 | 1 | 1 |
| **Warrior** (red) | 3 | | | 1 | 1 | 1 |
| **Scholar** (blue) | | 2 | 1 | 1 | 1 | 1 |
| **Priest** (white) | | 1 | 2 | 1 | 1 | 1 |
| **Rogue** (green) | 1 | 1 | | 2 | 1 | 1 |
| **Champion** (gold) | 2 | 1 | 1 | 1 | 1 | |
| **Relic** (purple) | | | 1 | 3 | 2 | |
---
## 6. A round
A round has two phases.
### Phase A — The Delve (players take turns, clockwise)
> *Speed tip:* roll your opening hand during the previous player's turn, so your turn is
> decisions, not dice-fishing. With 56 players this keeps downtime low.
On your turn:
1. **Draw & roll** a hand from your bag (default **4 dice**, ± Lineage/Calling).
2. **Press On?** *(push-your-luck)* You may draw & roll **another** hand for more symbols
— as many times as you dare, stopping whenever you like. **But every Press On feeds the
Maw** (add a Dread to the shared track), and rolling too much ☠ risks a **Setback**.
3. **Resolve Dread.** Cancel ☠ with ✚ Faith one-for-one (Hierophant cancels one extra;
Revenant converts instead — see its card). If **3 or more ☠ remain**, suffer a
**Setback**: return your lowest die to the box (Golem-born ignores this; Sylphid loses
an extra; Hierophant heals one).
4. **Spend your symbols** in any order:
- **Move** between floors. You get **1 Move** free (± Lineage/Calling); buy extra Moves
with 🜏 Cunning or ✦ Mettle (1 each).
- **Claim a room** on your current floor (see §7).
- **Loot:** each 💰 Greed banks Loot, worth more the deeper you are.
- **Recruit:** spend ✦ Mettle to add a die of your Calling's type to your bag (§8).
5. **Choose your exit** (see §7.3): **Stay**, **Cash Out**, or **declare a Last Stand**.
### Phase B — The Maw (everyone, together — the big moment)
1. **Advance the Maw.** The Maw rises **1 floor** automatically each round, **+1 more** if
the shared **Dread track** is full (then reset it). *(It never eats more than one floor
per round — see §9 — so "one floor away" is always an honest warning.)*
2. **Collapse** the floor it reaches:
- **Score it.** For each room, whoever has the most marker strength takes its Glory
(`tile value × Depth Value`). Ties split, rounding down. **Warden** markers count
double; **Fallen** markers count as 1.
- **Value rises.** Half the value of every **unclaimed** room **rises to the floor
above** as refugee plunder (add a plunder token). *This is why the top floors swell
into the richest finale — never abandon the shallows.*
- **The Maw takes the floor**, and anyone still on it (see §7.3).
3. Pass the first-player marker. New round.
The game ends the instant **Floor 1 collapses.**
---
## 7. The heart of the game
### 7.1 Claiming rooms (area control + the trinity)
To **claim** a room you must, across this turn, produce:
- **⚔ Steel ≥ the room's threat** (fight through the monsters), **and**
- **🜏 Cunning ≥ the room's lock** (crack the mechanism).
**Vaults** also demand the **trinity**: you must have rolled at least one ⚔, one 🜏, *and*
one ✚ this turn (the EverQuest holy trinity, made literal) — unless you're a **Lorekeeper**.
Place a **control marker**; its **strength = 1 + extra symbols you poured in** (Warden ×2).
Didn't quite make it? Your Steel/Cunning **carry over as assault progress** — keep chipping
at the same room next turn. A near-miss is never a total loss.
Holding a room makes it **produce** each turn (a Forge throws Steel, a Library Cunning, a
Vault Greed) — your engine grows on the ground you keep, right up until the Maw takes it.
### 7.2 Room tiles at a glance
| Theme | Value | ⚔ Threat | 🜏 Lock | Produces | Note |
|---|---|---|---|---|---|
| Threshold | 1 | 0 | 0 | | safe mouth |
| Stone | 2 | 1 | 0 | | plain |
| Cistern | 3 | 1 | 1 | ✦ | flooded |
| Den | 3 | 3 | 0 | ⚔ | combat lair |
| Crypt | 3 | 2 | 1 | ✚ | undead (combat) |
| Hall | 4 | 1 | 1 | ✦ | pillared |
| Library | 4 | 1 | 2 | 🜏 | loot-rich, locked |
| Forge | 5 | 4 | 1 | ⚔ | lava (combat) |
| **Vault** | 6 | 2 | 2 | 💰 | **needs trinity** |
| **Throne** | 7 | 5 | 2 | 💰 | boss (combat), deepest |
### 7.3 The three exits (this is the game)
Every turn, every crew faces the same choice. This triangle *is* DEEPFALL:
- **STAY / Press On** — keep the crew active: keep recruiting, holding, producing. More
upside, but the Maw is climbing, and a crew caught flat-footed loses **everything
unbanked and all its active territory**.
- **CASH OUT ("Go to Ground")** — retire the crew. **Bank** its Loot + a **depth dividend**
(deepest floor reached × 2). Its markers become permanent **Fallen** markers (strength 1)
that still score at every future collapse. Then muster a **fresh** company at the
frontier. Safe, repeatable, keeps your dead earning. *(Decline, from Small World.)*
- **WORTHY END ("Last Stand")** — declare it (free if you hold a Throne/Vault, or as a
**Wardancer**). When the Maw **Takes** your active crew, score a huge **Worthy End**:
`deepest floor × 2.5` (Wardancer ×2.2 on top), and **the Maw consumes your held rooms
clean** so no rival inherits them. The single biggest payout — and all-or-nothing.
*(Valhalla, from Blood Rage.)*
Being Taken **without** a Last Stand is the game's one real disaster (lose loot + territory
for nothing). The exits above are *always* offered, so a careful player never falls into
it — and the simulator confirms it almost never happens.
> **Design note:** under perfectly greedy play the bots treat Cash Out and Worthy End as
> substitutes, so aggression slightly over-wins. At a human table the *denial* (salting a
> rival's inheritance) and the sheer drama of a deep martyr make Worthy End pull its
> weight. This is the one spot still under playtest — see `FINDINGS.md`.
---
## 8. Recruiting (building your bag)
Spend ✦ **Mettle** (default cost 2) to take a die from the Recruit Pool into your bag —
the type set by your **Calling** (Reaver→Warrior, Lorekeeper→Scholar, etc.; Prospector
gets the rare **Relic**). A bigger, sharper bag means stronger hands *and* more Press-On
fuel. This is your in-game engine (Quarriors' dice-building).
---
## 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
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
faster. **The Maw never collapses more than one floor in a single round.**
**Scaling (validated to 11-round games at every count):**
| Players | Rooms / floor | Dread track length |
|---|---|---|
| 2 | 4 | 5 |
| 3 | 4 | 6 |
| 4 | 4 | 7 |
| 5 | 5 | 8 |
| 6 | 6 | 9 |
Rooms-per-floor scaling keeps contention (and the fight for majorities) constant; the
longer Dread track on a bigger table absorbs the extra collective Dread so the game still
ends in ~11 rounds with tight margins and a near-zero feel-bad rate.
---
## 10. Turn quick-reference
```
YOUR TURN (Delve Phase)
1. Roll hand (4 dice ± mods)
2. Press On? → +1 Dread to track, risk a Setback (repeat / stop)
3. Dread: cancel with Faith; 3+ left = Setback (lose a die)
4. Spend: Move · Claim a room · bank Loot (Greed) · Recruit (Mettle)
5. Exit: STAY / CASH OUT (bank + Fallen markers) / LAST STAND
MAW PHASE (all together)
• Maw rises 1 floor (+1 if Dread track full → reset)
• Collapse: score majorities (value × depth) → unclaimed value RISES up
→ Take anyone still on the floor (Worthy Ends fire here)
• Floor 1 collapses → GAME OVER, most Glory wins
```
---
## 11. The generator companion (procgen)
DEEPFALL's dungeon is a **real reikhelm dungeon**. The companion turns a seed into either:
- a **Setup Sheet** ("Floor 6: Vault, Throne, Forge, Crypt…") you copy with the tile deck —
30-second setup, infinite dungeons from one box; or
- a fully **AI-rendered board** for that seed (the deluxe one-off / campaign mode).
Same generator that powers the reikhelm playground — so the board, the variety, and the
art all come from the engine you already have.

View file

@ -19,6 +19,8 @@
"cashout_depth_dividend": 2.0,
"cashout_min_bank": 12.0,
"cashout_extraction_bonus": 3.0,
"taken_salvage_frac": 0.33,
"worthy_factor": 2.5,
"worthy_held_bonus": 0.0,

View file

@ -47,6 +47,12 @@ pub struct Config {
pub cashout_depth_dividend: f64,
/// The Cautious bot Cashes Out once its bankable value clears this bar.
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).
pub worthy_factor: f64,
/// 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
// before retiring makes the safe line viable.
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
// Throne) made the WorthyEnd strategy a 45% king; lowering the base while
// raising the Wardancer multiplier (now 2.2x) shifts the payoff onto the

View file

@ -542,7 +542,9 @@ impl Game {
/// Cash Out: bank loot + depth dividend, convert held markers to Fallen, retire.
fn cash_out(&mut self, pi: usize) {
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);
self.players[pi].glory += banked;
self.players[pi].cashouts += 1;
@ -598,7 +600,12 @@ impl Game {
}
}
} 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;
for (d, ri) in &party.held {
let fi = Self::floor_idx(*d);

View file

@ -32,6 +32,15 @@ fn main() {
None => Config::default(),
};
// `--players N` runs a quick player-count scaling diagnostic instead of the full
// sweeps (used to ground the rulebook's 6-player rules in data).
if let Some(n) = arg_value(&args, "--players").and_then(|s| s.parse::<usize>().ok()) {
for pc in [4usize, 5, 6, n].iter().copied().collect::<std::collections::BTreeSet<_>>() {
diagnose_players(&cfg, seeds, pc);
}
return;
}
fs::create_dir_all(&out).expect("create out dir");
println!("DEEPFALL sweeps seeds={seeds} out={out} config={}", cfg_path.as_deref().unwrap_or("<default>"));
println!("{}", "=".repeat(72));
@ -203,6 +212,69 @@ fn strategy_sweep(cfg: &Config, seeds: u64, out: &str) -> StratOut {
}
}
// ---------------------------------------------------------------------------------
// Player-count scaling diagnostic
// ---------------------------------------------------------------------------------
fn diagnose_players(base: &Config, seeds: u64, players: usize) {
// Linear board scaling: rooms-per-floor = player count (keeps rooms-per-capita and
// thus contention roughly constant). Maw dread-push is scaled by 4/players so the
// larger table's extra collective Dread doesn't crash the game length.
let mut cfg = base.clone();
cfg.rooms_per_floor = players as u32;
cfg.maw_dread_push = base.maw_dread_push * 4.0 / players as f64;
let mut sum_rounds = 0u64;
let (mut rmin, mut rmax) = (u32::MAX, 0u32);
let mut sum_margin = 0.0;
let mut seat_wins = vec![0u64; players];
let mut feelbad = 0u64;
let mut worthy = 0u64;
let mut cashout = 0u64;
let mut games = 0u64;
for seed in 0..seeds {
let roster: Vec<(Strategy, Option<(Lineage, Calling)>)> =
(0..players).map(|i| (ALL_STRATEGIES[i % 4], None)).collect();
let r = Game::new(cfg.clone(), seed.wrapping_mul(131), &roster).run();
games += 1;
sum_rounds += r.rounds as u64;
rmin = rmin.min(r.rounds);
rmax = rmax.max(r.rounds);
let wg = r.players[r.winner].glory;
sum_margin += if wg > 0.0 { r.margin / wg } else { 0.0 };
seat_wins[r.winner] += 1;
feelbad += r.players.iter().map(|p| p.takes_no_laststand as u64).sum::<u64>();
worthy += r.players.iter().map(|p| p.worthy_ends as u64).sum::<u64>();
cashout += r.players.iter().map(|p| p.cashouts as u64).sum::<u64>();
}
let g = games as f64;
println!(
"\n{players}P (rooms/floor={}, dread_push={:.3})",
cfg.rooms_per_floor, cfg.maw_dread_push
);
println!(
" length: mean {:.1} rounds ({rmin}-{rmax}) margin: {:.1}% feelbad: {:.3}/game",
sum_rounds as f64 / g,
sum_margin / g * 100.0,
feelbad as f64 / g
);
let seat_pct: Vec<String> = seat_wins
.iter()
.enumerate()
.map(|(i, w)| format!("P{i}:{:.0}%", *w as f64 / g * 100.0))
.collect();
let fair = 100.0 / players as f64;
println!(" seat win% (fair={fair:.0}%): {}", seat_pct.join(" "));
println!(
" exits/game: cashout {:.1}, worthy {:.2}, feelbad {:.2}",
cashout as f64 / g,
worthy as f64 / g,
feelbad as f64 / g
);
}
// ---------------------------------------------------------------------------------
// Sweep 2: combo asymmetry
// ---------------------------------------------------------------------------------