mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 08:52:42 +00:00
Phase 3a (sweep): elite-bracket + drift sweep, strong partial positive
Wired two harness lever overrides — EliteInterruptThresholdOverride
(live=19) and ThreatDriftBaseOverride (live=3) — into the day-loop in
expedition_balance.go. Live runHarvestInterrupt / dailyThreatDrift are
untouched; the harness re-buckets Standard↔Elite after the live call.
TestExpeditionBalance_Phase3_GlobalLeverSweep walks a 3×3 grid
(elite ∈ {17,19,23} × drift ∈ {1,3,5}) over the Phase 1 matrix at
200 trials/cell. -short skips.
Elite-bracket threshold is the dominant lever for T1–T3. At
e=23/d=1: T1 mean 24.0% (goblin_warrens 40.5%), T2 7.2%
(sunken_temple 14.5%), T3 1.8%. Still well below target bands
(T1 70-90%, T2 62-82%) — the lever moves the needle in the right
direction but cannot land any tier on-band alone.
T4/T5 fingerprint changed but didn't lift. At e=23 dragons_lair
death drops 60% → 24% but starvation climbs to 75% — the fighter
now survives elites long enough to run out of supplies. T4 cells
shift the same way. Indicates a second lever is needed for the
higher tiers (standard-fight survivability or supply margin), to
be swept in Phase 3-B.
Plan doc updated. Renumbered the trailing "per-zone outlier pass"
to Phase 4 and "MAD / second-order" to Phase 5 so the test names
align with phase numbers going forward.
-short suite: same 2 pre-existing failures
(TestAdv2Scenario_ZoneRunGoblinWarrens, TestMageSpellbookLineInRender).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,24 @@ type expeditionBalanceProfile struct {
|
||||
// TestExpeditionBalance_Phase2_LeverSweep.
|
||||
RetreatThreatBumpOverride int
|
||||
SurpriseNickDivisorOverride int
|
||||
|
||||
// Phase 3 global-lever overrides. Zero means "use live":
|
||||
//
|
||||
// EliteInterruptThresholdOverride — total (raw + tier + threat-mod)
|
||||
// at which the harvest-interrupt bracket promotes from Standard
|
||||
// to Elite. Live value is 19 (dnd_expedition_combat.go: bracket
|
||||
// table). Higher = fewer elites; lower = more.
|
||||
//
|
||||
// ThreatDriftBaseOverride — daily threat-clock drift base before
|
||||
// DM-mood mod. Live value is 3 (dnd_expedition_threat.go:
|
||||
// dailyThreatDrift). Lower = slower AC/init creep + slower
|
||||
// bracket promotion via the +1/20-over-40 threat-mod path.
|
||||
//
|
||||
// Both wired into the harness day loop only; live callers go
|
||||
// through the shipped constants. See
|
||||
// TestExpeditionBalance_Phase3_GlobalLeverSweep.
|
||||
EliteInterruptThresholdOverride int
|
||||
ThreatDriftBaseOverride int
|
||||
}
|
||||
|
||||
// expeditionTrialResult is the outcome of one simulated expedition.
|
||||
@@ -243,9 +261,15 @@ func (h *expeditionHarness) advanceExpeditionOneDay() expeditionTrialResult {
|
||||
|
||||
// 3. Daily threat drift — math is in dailyThreatDrift (pure);
|
||||
// applyDailyThreatDrift's DB write is bypassed but the in-memory
|
||||
// mutation is mirrored here.
|
||||
// mutation is mirrored here. Phase 3 sweep can override the base
|
||||
// drift constant via the harness profile.
|
||||
if !h.exp.BossDefeated {
|
||||
delta, _ := dailyThreatDrift(h.exp.DMMood)
|
||||
if h.threatDriftBaseOverride > 0 {
|
||||
// dailyThreatDrift returns base(3) + mood-mod. Swap the
|
||||
// base while preserving the mood-mod component.
|
||||
delta = h.threatDriftBaseOverride + (delta - 3)
|
||||
}
|
||||
h.exp.ThreatLevel += delta
|
||||
if h.exp.ThreatLevel < 0 {
|
||||
h.exp.ThreatLevel = 0
|
||||
@@ -258,9 +282,21 @@ func (h *expeditionHarness) advanceExpeditionOneDay() expeditionTrialResult {
|
||||
|
||||
// 4. Daytime combat-interrupt rolls.
|
||||
for i := 0; i < h.rollsPerDay; i++ {
|
||||
kind, _ := resolveCombatInterrupt(
|
||||
kind, total := resolveCombatInterrupt(
|
||||
h.exp.ThreatLevel, int(zone.Tier), h.char.Class, h.exp.ZoneID, h.rng.d20,
|
||||
)
|
||||
// Phase 3 lever: re-bucket Standard ↔ Elite using the override
|
||||
// threshold instead of the live 19+ cutoff. Patrol (≥22) and
|
||||
// the Noise/None floor are unchanged.
|
||||
if h.eliteInterruptThresholdOverride > 0 &&
|
||||
(kind == InterruptStandard || kind == InterruptElite) &&
|
||||
total < 22 {
|
||||
if total >= h.eliteInterruptThresholdOverride {
|
||||
kind = InterruptElite
|
||||
} else if total >= 15 {
|
||||
kind = InterruptStandard
|
||||
}
|
||||
}
|
||||
switch kind {
|
||||
case InterruptNone:
|
||||
continue
|
||||
@@ -520,6 +556,10 @@ type expeditionHarness struct {
|
||||
// live behavior (retreatThreatBump=5, /5 surprise-nick divisor).
|
||||
retreatThreatBumpOverride int
|
||||
surpriseNickDivisorOverride int
|
||||
// Lever overrides for the Phase 3 sweep. Zero ⇒ live behavior
|
||||
// (eliteInterruptThreshold=19, threatDriftBase=3).
|
||||
eliteInterruptThresholdOverride int
|
||||
threatDriftBaseOverride int
|
||||
// traceFight, if non-nil, is invoked once per fight inside
|
||||
// runHarnessFight with a human-readable summary. Used by the
|
||||
// Phase 2 lethality probe to spot whether the nick, the picked
|
||||
@@ -570,6 +610,8 @@ func runExpeditionBalanceTrial(p expeditionBalanceProfile, seed uint64) expediti
|
||||
rollsPerDay: rolls,
|
||||
retreatThreatBumpOverride: p.RetreatThreatBumpOverride,
|
||||
surpriseNickDivisorOverride: p.SurpriseNickDivisorOverride,
|
||||
eliteInterruptThresholdOverride: p.EliteInterruptThresholdOverride,
|
||||
threatDriftBaseOverride: p.ThreatDriftBaseOverride,
|
||||
}
|
||||
for {
|
||||
res := h.advanceExpeditionOneDay()
|
||||
|
||||
Reference in New Issue
Block a user