Commit Graph

5 Commits

Author SHA1 Message Date
prosolis
8cdd64b383 Phase 2 (sweep): lever-tuning sweep, negative result
Sweep the two knobs surfaced by Phases 2a/2b — retreatThreatBump
and clampSurpriseNick's wounded-entrant divisor — across a full
3×4 grid (bump ∈ {2, 5, 10} × divisor ∈ {3, 5, 8, 12}) at 200
trials/cell across every matrix zone.

Wiring is harness-only: clampSurpriseNick keeps its live shape and
delegates to a new clampSurpriseNickD(divisor) variant; the harness
profile gains RetreatThreatBumpOverride/SurpriseNickDivisorOverride
fields threaded onto expeditionHarness; resolvedRetreatBump and
resolvedNickDivisor pick override-or-live. Zero on either field
falls back to the shipped value so live runHarvestInterrupt is
untouched.

Sweep test: TestExpeditionBalance_Phase2_LeverSweep, -short skipped,
mirrors Phase2_CadenceCalibration's per-tier digest shape.

Outcome: across 24,000 trial-cells (12 lever combos × 10 zones
× 200 trials), every cell reports 0.0% completion / ~100% death.
The knobs are inert on the headline metric — even (b=2, d=12)
can't lift any tier off the floor. Confirms the post-2b
tier-lethality trace: remaining deaths are fresh-entry elite
one-shots (Warchief, Hag, Roper, Young Red Dragon), not chained-
interrupt cascades. Justifies Phase 2c (roster dilution) rather
than further tuning of these two levers.

Plan doc updated.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 10:39:45 -07:00
prosolis
04aa887d18 Phase 2a (lever): expedition retreat semantics split
Phase 2 diagnostics named the InterruptElite bracket as the likely
first lever; tier-walking the lethality probe at the matrix cadence
told a different story.  Phase 1's uniform-0% baseline isn't an
elite-bracket calibration issue at all — every tier reads 0% because
the engine's TimedOut contract was being ignored by every expedition
caller.

combat_engine.go:451 says: "Timeout = retreat, not lethal blow.
Caller treats a timeout loss as 'fight ended, no character death'".
But runHarvestInterrupt / tryPatrolEncounter / resolveCombatRoom all
called abandonZoneRun + retireAllRegionRuns on any !PlayerWon —
ending the expedition outright on a retreat.  The retreat flavor line
("X outlasts you. You retreat from the expedition, wounded but alive")
was already in the code, just stapled to an actual run-abort.

Splits the policy by caller:

  • runHarvestInterrupt   — autopilot daytime interrupt.  TimedOut →
    retreat: threat +5, HP carries over, run continues, harvest slot
    forfeit (no kill / loot).  HP<=0 still ends the run + marks dead.
  • tryPatrolEncounter   — !advance pre-room patrol roll.  Same
    retreat policy: patrols don't gate progress, so retreating from
    one and walking into the next room is the right shape.
  • resolveCombatRoom    — !advance room/elite combat.  Unchanged —
    this path gates room progression; a retreat has nowhere to go, so
    any loss still ends the run.  (Manual zone runs were always
    intended to end here.)

Harness mirrored: daytime interrupt timeout → carry HP + threat bump
+ continue day; night-encounter loss → terminate (mirrors
resolveCombatRoom, since live night encounters defer to !advance).

retreatThreatBump = 5 is the per-retreat threat penalty.  Low enough
not to compound brutally with chained retreats, high enough that 3–4
retreats noticeably walks the threat clock toward Stirring.  Easy to
dial in Phase 3 if zones go off-band.

Phase 1 matrix after the change still reads 0% completion at every
cell — but the encounter counts and survival shape are dramatically
different (T4 underdark 3.6→7.5 encs; T3 underforge trial saw 18
encounters across 10 days where the pre-change run died on day 2/3).
Adds TestExpeditionBalance_Phase2_TierLethality, a tier-walking
companion to the T1/rolls=1 probe, that traces every fight at the
matrix cadence across one zone per tier — the actual Phase 2b lever
work picks from this data, not the old T1-only probe.

The remaining 0% is now legibly driven by tier-disproportionate elite
rosters (Hobgoblin Warchief at T1, Green Hag at T2, Roper/Helmed
Horror higher up) that one-shot or two-shot tier-appropriate
fighters.  Phase 2b's lever shortlist:

  1. Roster gate / SpawnWeight tuning to dilute over-tier elites.
  2. Surprise-nick floor reduction on chained interrupts (carryover
     HP + nick is the death-spiral fingerprint at T1 specifically).
  3. Per-day cadence reduction if 1+2 don't carry T1 to band.

Pre-existing test failures (TestAdv2Scenario_ZoneRunGoblinWarrens,
TestMageSpellbookLineInRender) verified to fail identically on HEAD;
no new test regressions.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 10:01:07 -07:00
prosolis
0f09a421bc Phase 2 (diag): cadence sweep, gear-tier fix, lethality probe
Three Phase 2 diagnostic artifacts. No tuning knob has moved on
production code yet — these tests calibrate the harness and surface
the real first lever for next session.

1) Cadence calibration sweep
   TestExpeditionBalance_Phase2_CadenceCalibration sweeps
   HarvestRollsPerDay ∈ {1,2,3,4} across the full Phase 1 matrix and
   logs per-cell + per-tier completion. Required a new
   HarvestRollsPerDay field on expeditionBalanceProfile so cells can
   override the package-default constant. Finding: cadence is NOT the
   dominant lever — at rolls=1 the T1 cell only reaches 2%, with
   bimodal hp_left (100% survivors / 0% deaths). Killed the cadence
   hypothesis from Phase 1's commit message.

2) Gear-tier centerline fix
   phase1TierCenterline bumped for T3/T4/T5 (8→9, 11→13, 15→17). The
   shared gearTier ladder (5/9/13/17 boundaries) was placing T3/T4/T5
   centerlines one gear bracket *below* the zone's tier, so those
   cells fought with under-spec'd weapons/armor. New centerlines are
   the lowest level in each tier's design-doc range where gearTier ==
   tier. All centerlines still inside their design-doc ranges. Effect
   in the sweep at rolls=1: underforge T3 1.0% → 10.5% comp, underdark
   T4 flipped from pure combat-death to 14% starve (i.e. fighter now
   survives combat, runs out of food). Real bug, but small — the
   structural lethality problem remains.

3) Lethality probe + traceFight hook
   TestExpeditionBalance_Phase2_LethalityProbe runs 5 trials at the
   cleanest cell (T1 goblin_warrens L3 fighter, rolls=1) with a new
   optional traceFight hook on expeditionHarness that logs
   monster/AC/atk/HP-pre/HP-post/outcome per fight. Hook is nil in
   production runs, zero cost when unused. Finding: at T1, the
   InterruptElite branch keeps drawing Hobgoblin Warchief (AC 18,
   atk +5) from goblin_warrens' elite roster, and an L3 fighter has
   ~coin-flip odds against a CR-6-ish elite. One bad draw = dead;
   that's the bimodal hp_left fingerprint from the sweep. Non-elite
   draws (Worg, AC 13) play out as normal multi-round combats and
   are winnable.

Next-session lever choices, in order of suspected impact:
  - Roster gate: Hobgoblin Warchief out of (or weighted down in)
    the T1 elite pool — it's tier-disproportionate for goblin_warrens.
  - InterruptElite threshold: rarer elite-bracket draws at low threat
    so a single d20 swing doesn't equal expedition end.
  - Tier-floor cap on already-over-tier bestiary entries.

Plan doc: gogobee_expedition_difficulty.md §Phase 2.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 09:39:31 -07:00
prosolis
881cbfca2f Phase 1: expedition-difficulty full matrix baseline
New permanent test TestExpeditionBalance_Phase1_FullMatrix — every
registered zone × its tier-centerline level, 200 trials/cell, Fighter
fixed (class parity is the class-balance pass's job).

Centerlines per plan doc §Method (median of design-doc level range):
T1=L3, T2=L5, T3=L8, T4=L11, T5=L15.

Gates split: wiring pathologies (zero-day loop, days > cap) are fatal;
"0% at T1 / 100% at T5" land as WARN log lines, not t.Errorf. Phase 1's
deliverable is a logged baseline, not a tuning gate — Phase 2 promotes
those to band assertions once the global levers move.

Baseline numbers (seed 0xE0FFEE1, 200 trials, current Phase 0 harness):

  all 10 cells: 0.0% completion, 100% death, median day 2,
  avg encounters 2.0-3.6, threat at end 5-7.

The uniform 0% says the placeholder harnessHarvestRollsPerDay=4 plus
the tier-floored AC/atk is overwhelming the fighter on day 1 across
all tiers, well before threat drift matters. Per-tier spread is 0.0pp
because every zone is floored — Phase 2 has plenty of headroom and a
clear first lever (encounter cadence).

Plan doc: gogobee_expedition_difficulty.md §Phase 1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 09:09:47 -07:00
prosolis
53d1608490 Phase 0: expedition-difficulty sim harness spike
New file internal/plugin/expedition_balance.go — sibling to
dnd_class_balance.go, no dnd_ prefix per feedback_avoid_dnd_naming.
Plan doc: gogobee_expedition_difficulty.md.

Critical de-risk settled: clock-injection seam. The harness does not
run the wall-clock tickers (expeditionBriefingTicker @ 06:00,
expeditionRecapTicker @ 21:00, expedition_ambient @ 3h) and does not
inject a virtual time.Now. Instead, advanceExpeditionOneDay
reimplements the morning→day→night pipeline by calling the math-pure
helpers directly: applyDailyBurn, dailyThreatDrift,
resolveCombatInterrupt, resolveWanderingCheck, simulateCombatWithRNG.
No DB, no goroutines, no clock.

Phase 0 trade-offs (documented in the file header):
- Boss completion deferred; survive-N-days is the proxy.
- Per-region zones, loot/XP, pardon/Sovereign, babysit, temporal
  stack effects all skipped — Phase 1+ scope.
- Encounter cadence (harnessHarvestRollsPerDay=4) is a placeholder
  to be calibrated against live traces in Phase 1.

Tests:
- TestExpeditionBalance_Phase0_Spike — T2 Crypt Valdris × L5 Fighter,
  100 trials, asserts only degenerate sentinels (no 0%/100%, days>0,
  days<=cap). Runs in <3s.
- TestExpeditionBalance_Phase0_SeedSpread — confirms the RNG seam is
  actually wired by showing two distinct seeds produce different
  trials. Full byte-for-byte reproducibility under same seed is not
  asserted at Phase 0; surpriseRoundNick + pickWanderingMonster
  draw from package-global rand (same caveat as class balance).

Spike numbers (calibration baseline, NOT a target): 1% completion,
99% deaths, median 4 days, ~6 encounters. The headline death rate is
loud but expected — Phase 1's full matrix will diagnose whether it's
HP-carryover punishment, bestiary-vs-abstract-monster delta, or kit
ladder mismatch. Phase 2 is where the centerline gets tuned.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 08:37:23 -07:00