mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-15 08:32:41 +00:00
The class-identity audit (98ba416) wired Extra Attack via the new
resolvePlayerSwings helper, but only SimulateCombat (auto-resolve)
called it. The turn-based engine — every !fight/!attack and every
elite/boss gate the sim drives via autoResolveCombat — still called
single-swing resolvePlayerAttack, so Fighter L11+ got 1 swing/turn at
the gates instead of 3. The audit close-out was correct in spirit but
half-applied.
J1 baseline matrix surfaced it: Fighter L12 cleared 100% of T2 forest
but 2% of T3 manor and 7% of T4 underdark, with %boss_reached at 100%
across the board. The wall was the boss-room damage exchange, not
mid-zone attrition. Trace dump on a sample fight: Fighter dealt 79
dmg in 14 rounds (7 hits / 9 swings) — exactly one swing per round —
versus 167 enemy dmg. With multi-swing wired in, the same fight ends
in 7 rounds with the boss dead, Fighter at 87/168 HP, 16 hits in 19
swings.
n=100 matrix after the fix:
Fighter L12 manor: 2% → 100% clr
Fighter L12 underdark: 7% → 98% clr
Fighter L12 forest: 94% → 100% (no leader regression)
Mage cells unchanged (J2 territory). Rogue cells within noise.
Sim infra changes that landed alongside (needed to read the J1
signal):
* expedition_sim auto-arms class-default defensive abilities
(Second Wind / Healing Word) via the new simAutoArmEnabled toggle
+ trySimAutoArm helper, hooked before applyArmedAbility in both
combat builders. Production code paths untouched (toggle stays
off). Without this the sim simulated a player who never types
!arm, which under-counts class survival.
* SimResult.Combats captures per-fight turn-log summaries (rounds,
hits/misses, damage by side, AC values inferred from RollAgainst)
so future J-phase questions can dig into the engine without
re-running the matrix.
* sim_results/run_matrix.sh fans the matrix across (class,level,zone)
cells via xargs -P (one process per cell — each owns its global
sqlite handle). ~6× wall-clock speedup on a 14-core box; n=100
matrix runs in ~3min.
* sim_results/summarize.sh gains p50_yld_clr + %boss_reached columns
so future sweeps don't conflate "reaches boss" with "clears zone".
Baselines:
sim_results/baseline_j0_n100.jsonl — pre-fix (1350 rows)
sim_results/baseline_j1_extra_attack.jsonl — post-fix (4500 rows)
Phase J state: J0 baseline locked, J1 done. T5 dragons_lair still
0% clear universally (J3). Mage T2+ wall still real (J2).
52 lines
2.1 KiB
Bash
Executable File
52 lines
2.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Aggregate a sim corpus JSONL into a per-(class,level,zone) table.
|
|
# Columns: class, level, zone, n, p50 yield (all), p50 yield among cleared,
|
|
# mean yield, mean rooms, %cleared, %boss-reached, %tpk, %fled, %extracted.
|
|
#
|
|
# boss-reached counts runs whose StopCode is "boss" or "complete" — i.e.,
|
|
# the autopilot entered the boss room, whether the run died there or
|
|
# finished the zone.
|
|
set -euo pipefail
|
|
|
|
infile=${1:-baseline_j0.jsonl}
|
|
cd "$(dirname "$0")"
|
|
|
|
jq -rs '
|
|
def p50(xs): if (xs|length)==0 then 0 else (xs|sort|.[(length/2|floor)]) end;
|
|
|
|
group_by([.Class, .Level, .Zone])
|
|
| map({
|
|
class: .[0].Class,
|
|
level: .[0].Level,
|
|
zone: .[0].Zone,
|
|
n: length,
|
|
yields: [.[] | .YieldCount],
|
|
yields_cleared: [.[] | select(.Outcome=="cleared") | .YieldCount],
|
|
rooms: [.[] | .Rooms],
|
|
outcomes: [.[] | .Outcome],
|
|
stops: [.[] | .StopCode]
|
|
})
|
|
| map(. + {
|
|
p50_yield: p50(.yields),
|
|
p50_yield_cleared: p50(.yields_cleared),
|
|
mean_yield: ((.yields | add) / .n),
|
|
mean_rooms: ((.rooms | add) / .n),
|
|
pct_cleared: (([.outcomes[] | select(. == "cleared")] | length) * 100 / .n),
|
|
pct_boss_reached: (([.stops[] | select(. == "boss" or . == "complete")] | length) * 100 / .n),
|
|
pct_tpk: (([.outcomes[] | select(. == "tpk")] | length) * 100 / .n),
|
|
pct_fled: (([.outcomes[] | select(. == "fled")] | length) * 100 / .n),
|
|
pct_extracted: (([.outcomes[] | select(. == "extracted")] | length) * 100 / .n)
|
|
})
|
|
| sort_by(.zone, .class, .level)
|
|
| (["class","level","zone","n","p50_yld","p50_yld_clr","mean_yld","mean_rms","%clr","%boss","%tpk","%fled","%ext"] | @tsv),
|
|
(.[] | [.class, .level, .zone, .n,
|
|
.p50_yield, .p50_yield_cleared,
|
|
(.mean_yield * 10 | round / 10),
|
|
(.mean_rooms * 10 | round / 10),
|
|
(.pct_cleared | round),
|
|
(.pct_boss_reached | round),
|
|
(.pct_tpk | round),
|
|
(.pct_fled | round),
|
|
(.pct_extracted | round)] | @tsv)
|
|
' "$infile" | column -t -s $'\t'
|