Combat balancing: headless duel simulator (reuses CombatSystem) + balance-invariant tests #105

Open
opened 2026-07-20 18:22:15 +02:00 by marco · 1 comment
Owner

Goal

A tool to tune combat safely: it drives the actual server combat code (CombatSystem / CombatFormulas) in a headless harness, runs many simulated duels for a matchup, and reports the outcome — win-rate, time-to-kill (TTK) distribution, hit-rate, avg damage. It starts from the current production parameters; you tweak one knob (e.g. Swordsmanship skill, STR, weapon damage, swing speed) and immediately see how win-rate/TTK move. Because it reuses the real code, the sim IS production — a formula change shows up here, no drift/reimplementation.

Envisioned use (owner): "parte coi parametri attuali; cambio il livello di skill di sword e l'app mi simula una serie di scontri e mi mostra i risultati."

Why

Balance (especially PvP) is only tractable if you can measure it. Closed-form DPS/EHP/TTK gives first-order intuition; the randomness (hit rolls, damage variance, swing timing) needs Monte Carlo. This turns "is this change mathematically sensible?" into a fast objective loop: change param -> simulate -> read win-rate + TTK -> invariants green -> merge.

Scope (MVP)

  • A headless runner (tools/IsoMmo.CombatSim) that:
    • builds two Mobiles with configurable stats/skills/equipped weapon (defaults from current GameOptions + item catalog),
    • runs the real swing/hit/damage resolution (CombatSystem) to the death with a seeded RNG,
    • repeats N times and aggregates: win-rate, TTK mean/stddev/percentiles, hits-to-kill, hit-rate.
  • Parametric matchup input (CLI flags / small config): e.g. --a str=50 dex=50 sword=70 --b sword=40 --runs=5000 --seed=1.
  • Tabular output; win-rate matrix when sweeping one param over a range (e.g. sword 0..1000 step 100).

Design notes

  • Reuse CombatSystem/CombatFormulas verbatim — do NOT reimplement the math (the whole point). Likely requires making the combat RNG injectable/seedable so runs are reproducible and the harness runs without the tick loop / networking / World.
  • Two Mobile instances + a deterministic clock stepping swing cooldowns; no persistence/AoI needed.
  • Extensible later to spells (SpellSystem) and 1vN.

Companion: balance as automated tests

Encode balance goals as xUnit statistical tests over the sim engine (run in CI):

  • TTK within a target window per matchup;
  • no dominant build (max win-rate vs the field < threshold);
  • mirror matchup ~= 50% (within noise);
  • monotonicity (more skill => not-lower win-rate).

Definition of Done

  • Running the tool with default params prints win-rate + TTK stats for a sword-vs-sword duel over N runs (N configurable).
  • Changing a single knob (e.g. sword=40 vs sword=70) visibly shifts the reported win-rate/TTK — demonstrable with two runs.
  • The sim drives the real CombatSystem: a deliberate tweak to a CombatFormulas constant changes the sim output (proves no reimplementation).
  • Reproducible: same seed => identical results.
  • At least 3 balance-invariant tests run under dotnet test (mirror ~= 50%, bounded TTK, no-dominant-build).
  • Whole solution builds, zero warnings.

Stretch (separate follow-up)

An interactive GUI front-end over the same sim engine (sliders for skill/stat/weapon -> live win-rate/TTK), reusing the headless engine so there is one source of truth.

Notes

Research/foundation for the combat/PvP epic (roadmap #12: M8 combat; fed by #8 M6 stats & skills). The engine is the deliverable; the interactive app is a natural follow-up over the same engine.

## Goal A tool to tune combat safely: it drives the **actual** server combat code (`CombatSystem` / `CombatFormulas`) in a **headless** harness, runs many simulated duels for a matchup, and reports the outcome — win-rate, time-to-kill (TTK) distribution, hit-rate, avg damage. It **starts from the current production parameters**; you tweak one knob (e.g. Swordsmanship skill, STR, weapon damage, swing speed) and immediately see how win-rate/TTK move. Because it reuses the real code, **the sim IS production** — a formula change shows up here, no drift/reimplementation. Envisioned use (owner): "parte coi parametri attuali; cambio il livello di skill di sword e l'app mi simula una serie di scontri e mi mostra i risultati." ## Why Balance (especially PvP) is only tractable if you can measure it. Closed-form DPS/EHP/TTK gives first-order intuition; the randomness (hit rolls, damage variance, swing timing) needs **Monte Carlo**. This turns "is this change mathematically sensible?" into a fast objective loop: change param -> simulate -> read win-rate + TTK -> invariants green -> merge. ## Scope (MVP) - A headless runner (`tools/IsoMmo.CombatSim`) that: - builds two `Mobile`s with configurable stats/skills/equipped weapon (defaults from current `GameOptions` + item catalog), - runs the real swing/hit/damage resolution (`CombatSystem`) to the death with a **seeded** RNG, - repeats N times and aggregates: win-rate, TTK mean/stddev/percentiles, hits-to-kill, hit-rate. - Parametric matchup input (CLI flags / small config): e.g. `--a str=50 dex=50 sword=70 --b sword=40 --runs=5000 --seed=1`. - Tabular output; **win-rate matrix** when sweeping one param over a range (e.g. sword 0..1000 step 100). ## Design notes - **Reuse `CombatSystem`/`CombatFormulas` verbatim** — do NOT reimplement the math (the whole point). Likely requires making the combat RNG **injectable/seedable** so runs are reproducible and the harness runs without the tick loop / networking / `World`. - Two `Mobile` instances + a deterministic clock stepping swing cooldowns; no persistence/AoI needed. - Extensible later to spells (`SpellSystem`) and 1vN. ## Companion: balance as automated tests Encode balance goals as xUnit statistical tests over the sim engine (run in CI): - TTK within a target window per matchup; - **no dominant build** (max win-rate vs the field < threshold); - mirror matchup ~= 50% (within noise); - monotonicity (more skill => not-lower win-rate). ## Definition of Done - Running the tool with **default** params prints win-rate + TTK stats for a sword-vs-sword duel over N runs (N configurable). - Changing a single knob (e.g. `sword=40` vs `sword=70`) visibly shifts the reported win-rate/TTK — demonstrable with two runs. - The sim drives the **real** `CombatSystem`: a deliberate tweak to a `CombatFormulas` constant changes the sim output (proves no reimplementation). - **Reproducible**: same seed => identical results. - At least 3 balance-invariant tests run under `dotnet test` (mirror ~= 50%, bounded TTK, no-dominant-build). - Whole solution builds, zero warnings. ## Stretch (separate follow-up) An interactive GUI front-end over the same sim engine (sliders for skill/stat/weapon -> live win-rate/TTK), reusing the headless engine so there is one source of truth. ## Notes Research/foundation for the combat/PvP epic (roadmap #12: M8 combat; fed by #8 M6 stats & skills). The engine is the deliverable; the interactive app is a natural follow-up over the same engine.
Author
Owner

Interactive UI mockup of the Duel Lab attached (duel-lab.zip — unzip and open the HTML). It shows the envisioned console: two fighters as composited real UO paperdolls (body + worn armor + worn sword layered), live win-rate/TTK, TTK histogram, a win-rate heatmap matrix (Sword A x Sword B), a skill sweep curve, and balance-invariant pass/warn/fail chips. The Fighter-A Swordsmanship slider updates the results live. Mock data for now; the real tool drives CombatSystem.

Interactive UI mockup of the Duel Lab attached (`duel-lab.zip` — unzip and open the HTML). It shows the envisioned console: two fighters as **composited real UO paperdolls** (body + worn armor + worn sword layered), live win-rate/TTK, TTK histogram, a **win-rate heatmap matrix** (Sword A x Sword B), a skill **sweep** curve, and **balance-invariant** pass/warn/fail chips. The Fighter-A Swordsmanship slider updates the results live. Mock data for now; the real tool drives `CombatSystem`.
marco added this to the Alpha milestone 2026-07-20 18:37:59 +02:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
marco/IsoMmo#105
No description provided.