# Ship Balance Analysis — Player Races (PvP)

Scope: every buildable player combat ship for races 0–6. NPC races (7+) and the
Corruption event are excluded — they are not balance inputs. Economy and upkeep
are excluded; this is about combat outcomes and build throughput only.

Per-race detail lives in [Races/](Races/). Change list in
[Ship-Balance-Changes.md](Ship-Balance-Changes.md).

> **Revision note.** An earlier version of this document proposed 16 stat
> changes. Nearly all of them have been withdrawn. Two corrections caused it:
> `class` is a display label with no balance meaning, so "vs class mean" was
> measuring nothing; and build rate was missing entirely, so every judgement was
> made on one of the two axes that matter. Both fixes are incorporated below and
> the conclusion is substantially different — see §6.

---

## 1. Method

Three evidence streams, because each alone is misleading.

### 1.1 Observed battles

`gcc_log` battle bodies parsed to per-stack units and casualties, joined to
`gcc.event_attack`, filtered to PvP, even fights (power ratio 0.8–1.5),
2023-02-27 onward — the day after the last player-ship stat change.

**Three confounds make raw win-lift unusable**, all found during this analysis:

- **Empire power ≠ fleet power.** `event_attack` records *user* power, including
  planets and research, so "even" empires can bring wildly unequal fleets.
- **Cross-race fielding measures player skill.** H.Galleon reads +11 in Miner
  fleets and −1.4 in native Collective fleets. The gap is the player.
- **Cross-race hulls move by capture.** Collective `H.*` (capture 100), `R.*`
  (50) and Viral `V.*` (75) take Terran / Marauder / A.Miner hulls off their
  kills, so those fleets legitimately contain other races' ships. Collective
  hulls appearing in *Miner* fleets is still unexplained — Collective is a
  blocked reverse donor and its hulls are not capturable. See
  [Races/Combat-Mechanics.md](Races/Combat-Mechanics.md) §4.

All observed figures quoted here are therefore **within race and band only**.

### 1.2 A faithful battle simulator

`f_com_attack3.cfm` re-implemented in JavaScript. Verified against source:

| Mechanic | Value |
|---|---|
| Stack order | `ORDER BY power DESC` |
| Pairing | positional — attacker stack *k* vs defender stack *k* |
| Firing order | by `range`, **higher** fires first, ties favour the defender (`GTE`) |
| Waves | **2** (`stack=0; wavecount=1`, break at `wavecount GT 2`) |
| Shots | `afire=1`, `dfire=2` for all player ships (confirmed in DB) |
| Damage | `Σ w_k·(1−shield_k) × ceil(stackHull / unitHull)`, capped at target hull |
| Return fire | only if target `returnfire=1` **and** attacker `longrange=0`, at half |
| Win | defender loses ≥30% of power **and** attackerLost ≤ defenderLost × 1.15 |
| Max stacks | 10 |

**Validation** against 191 battles reconstructing exactly (computed power within
2% of recorded):

| Attitude | Outcome match | Mean casualty error |
|---|---|---|
| Careful | 81.2% | 16.3% |
| **Normal** | **88.0%** | **9.7%** |
| Aggressive | 83.8% | 9.7% |

Per-battle attitude and research modifiers are not recorded, so this ranks ships
reliably but does not predict individual fights.

### 1.3 Composition simulation, on two lenses

Random 6-stack fleets per race, 3 seeds × 14 compositions, played identically.
Run twice: once at **equal PR (100M)** and once at **equal turns (2,500)**.

---

## 2. Class does not mean anything

`ship_type.class` is a **display label indicating rough size**. Nothing in the
combat resolver reads it except a starbase upkeep multiplier and the (broken)
reverse-eligibility check.

A Guardian Corvette (G.Amethyst, 18,018 PR) and a Terran Corvette (T.Maru,
51 PR) are 353× apart in unit size. Comparing either "against the corvette mean"
is meaningless. **All comparisons here are roster-wide percentiles or
within-race.** The previous version of this document used class means throughout
and several of its conclusions did not survive their removal.

---

## 3. The turn economy

Turns regenerate **1 per 5 seconds**, capped at a **90-turn bank** — 720/hour,
17,280/day if spent continuously, or 90 per login for everyone else.

`ship_type.reqturn` is the **build rate (BR)**: units produced per turn. The
`buildmod` multiplier on it is a defunct server effect; treat it as 1.

| Metric | Formula |
|---|---|
| **ppt** | unit PR × BR — power score per turn |
| **dpt** | effective damage × BR — combat capability per turn |
| **bank90** | ppt × 90 — PR from one full bank |

`ppt` spans **5,100 → 101,858**, a 20× range. One 90-turn bank buys 459k PR of
C.Aries or 9.17M PR of G.Diamond. Two hulls that look equivalent at a PR
checkpoint can be a full day apart in reality.

Build rate is near-perfectly inverse to unit size (r = −0.982 on log scale) but
**not exactly** — ppt still rises with unit PR (r = +0.63). Capital hulls reach
a given PR faster.

### 3.1 Three levers, cleanly separated

| Lever | Moves dmg/PR | Moves EHP/PR | Moves ppt | Moves dpt |
|---|---|---|---|---|
| Weapon | yes | — | — | yes |
| Unit PR | yes (inverse) | yes (inverse) | yes | — |
| **Build rate** | **no** | **no** | **yes** | **yes** |

Build rate is the only lever that changes build economy without touching
fixed-bracket strength. Confirmed empirically: a BR-only change left equal-PR
standard deviation at exactly 9.3, unmoved.

---

## 4. What the combat system rewards

Correlations against measured win contribution, 84 ships weighted by battles:

| Predictor | r |
|---|---|
| Damage per 1k PR | **+0.35** |
| log₁₀(unit PR) | −0.40 |
| Hull per unit (log) | −0.41 |
| **Effective HP per 1k PR** | **−0.04** |
| Incoming damage multiplier | +0.15 |
| `range` / `longrange` / `returnfire` | +0.06 / −0.07 / +0.08 |
| Weapon concentration | −0.06 |

### 4.1 Durability per PR does nothing

**EHP per PR has no relationship to winning (r = −0.04).** The win condition is
two *ratio* tests — destroy 30%, lose ≤1.15× — and hull raises the denominator
on both sides.

This survives every other revision in this document and is its most robust
finding. K.Hun-Zen has the best defensive profile in the game (+0.99/+0.99 E/K,
incoming multiplier 0.31, nullifying 99% of 60% of all damage) and measures
−3.6. Its shields are spectacular and worthless.

**Consequence: hull and shield buffs cannot fix a weak ship.** Only damage, unit
PR or build rate can.

### 4.2 Unit size is a trade, not a trap

Small units win at a fixed PR checkpoint — monotonically, from +1.8 in the
smallest quintile to −3.4 in the largest. The earlier version of this document
concluded from this that "capital ships are a trap in PvP."

**That was wrong.** It measured only the fixed-PR axis. Capital hulls have
higher ppt, so they reach any given bracket faster and rebuild losses faster.
Guardian's median unit PR is 41,636 against Marauder's 2,052, and Guardian has
the best ppt and dpt in the game while having the worst damage per PR of any
real combat race. That is a deliberate opposition, not a defect.

### 4.3 Range, long-range and return fire barely matter

All three correlate near zero. With 2 waves and `afire=1`, both stacks in a
pairing almost always fire, and return fire is already halved and gated on
`dfire`.

### 4.4 Attitude: Aggressive is a trap

All race pairs, both directions:

| Attitude | a/d efficiency | 0.75 | 1.00 | 1.30 | 1.50 |
|---|---|---|---|---|---|
| Careful | 0.50 / 0.50 | 3% | **60%** | **97%** | **100%** |
| Normal | 0.95 / 1.00 | **13%** | 40% | 90% | 93% |
| Aggressive | 1.75 / 1.99 | 7% | 37% | 63% | 73% |

Aggressive gives the **defender** the larger multiplier (1.99 vs 1.75). Since
victory needs `attackerLost ≤ defenderLost × 1.15`, inflating both sides while
favouring the defender hurts the attacker. It is the worst option at every ratio
at or above parity, by 23 points at parity.

**This is now the clearest live imbalance in the document**, and it is entirely
independent of ship stats.

---

## 5. Race balance depends on which lens you use

Random 6-stack fleets, **Special/Strafez hulls included** (an earlier version of
this table excluded them via a `class >= 1` filter — see §7.7):

| Race | Equal PR | Equal turns | **Average** |
|---|---|---|---|
| Guardian | 94.8 | **121.2** *(first)* | **108.0** |
| Miner | **109.4** *(first)* | 104.6 | 107.0 |
| Marauder | 101.3 | 105.7 | 103.5 |
| Terran | 96.2 | 99.7 | 97.9 |
| Viral | 106.2 | 85.1 *(last)* | 95.7 |
| Collective | 92.2 *(last)* | 83.7 | 87.9 *(last)* |
| | sd **6.2** | sd **12.8** | sd **7.0** |

For comparison, the same run **without** Special hulls — the figures earlier
drafts reported:

| Race | Equal PR | Equal turns | Average |
|---|---|---|---|
| Guardian | 87.8 *(last)* | **130.9** | 109.4 |
| Viral | **117.8** | 86.5 *(last)* | 102.1 |
| Marauder | 103.9 | 98.2 | 101.1 |
| Miner | 95.8 | 101.5 | 98.6 |
| Terran | 99.1 | 92.5 | 95.8 |
| Collective | 95.5 | 90.4 | 93.0 |
| | sd 9.3 | sd 14.7 | sd 5.2 |

Including the six Special hulls **tightens equal-PR balance considerably**
(sd 9.3 → 6.2) because every race draws on the same neutral pool, and it moves
two conclusions:

- **Viral loses its equal-PR lead** (117.8 → 106.2). Its dominance was partly an
  artefact of measuring a pool that excluded the fez line everyone can field.
- **Miner gains most** (95.8 → 109.4) and **Collective becomes the weakest race**
  on the averaged view (87.9).

What survives unchanged is the shape: **Guardian remains last-ish at equal PR
and first at equal turns**, and every race strong on one lens is weaker on the
other.

Neither lens is "the truth". Equal PR is what matchmaking enforces — you fight
people near your power, so damage per PR decides the fight. Equal turns is what
the player actually spends, and it governs how fast you re-enter the fight after
losing a fleet. The PR advantage is partly self-neutralising, since building
power faster moves you into a harder bracket; recovery speed is not.

---

## 6. Conclusion: the roster is closer to balanced than it looks

Every intervention tested made the averaged balance **worse**:

| Configuration | Equal-PR sd | Equal-turn sd | **Average sd** | Spread |
|---|---|---|---|---|
| **Baseline (unchanged)** | 9.3 | 14.7 | **5.2** | **16.4** |
| Viral reverse cap 3 → 2 | 8.0 | 21.8 | 7.3 | 23.8 |
| Dead-hull floor raises | 13.0 | 16.8 | 7.5 | 21.8 |
| Both | 11.4 | 20.4 | 7.4 | 20.6 |
| BR-only fix, worst 2 economy hulls | 9.3 | 15.5 | 5.7 | 17.5 |

> These five rows were all measured on the **no-Special pool**, so they are
> internally consistent with each other but sit on the older baseline (5.2, not
> 7.0). Adding Special hulls tightens equal-PR balance on its own, which if
> anything *weakens* the case for intervention further — several of the
> withdrawn changes were aimed at equal-PR spread. They have not been re-run
> individually; that is the main outstanding piece of work.

The baseline is the best configuration found. **The previous 16-change
recommendation is withdrawn**, along with the reverse-cap proposal that looked
like the highest-leverage item when only the equal-PR lens was in view.

Specific reversals worth recording:

| Earlier position | Why it was wrong |
|---|---|
| Buff five Guardian hulls; Guardian is the weakest race | Guardian is last at equal PR and **first at equal turns**. It is paid for low damage per PR with the best ppt, dpt and bank90 in the game. |
| Nerf Tiger and Manta | Marauder is 6th on ppt and 7th on EHP per PR. It already pays for its damage-per-PR lead. Tiger's ppt and dpt are both below roster median. |
| Buff G.Sapphire, P.Apollo, R.Snow, R.Sovereign | All are Elite, Tempo or Growth-engine hulls with above-median dpt. They looked weak only against a meaningless class mean. |
| Cut G.Rhyolite's PR | Its dpt (29,787) is above median; the PR cut would have *lowered* its already below-median ppt. If anything it wants a BR increase. |
| Neutral buffs are race-neutral and therefore safe | False. They moved Viral +8.5 and Marauder −5.8. Races lean on neutrals in inverse proportion to their own roster strength, so neutral buffs transfer power to whoever is already strongest at a fixed bracket. |
| Viral reverse cap 3 → 2 | Improves equal-PR balance but wrecks equal-turn balance (Viral 86.5 → 61.8). Net worse. |

### What still stands

1. **Aggressive attitude is strictly dominated** at parity and above (§4.4).
   Independent of ships, and the strongest remaining candidate for a change.
2. **Durability per PR buys nothing** (§4.1) — relevant to any future tuning,
   because it rules out the most intuitive lever.
3. **Two code defects**, neither a balance question:
   - Reverse eligibility is a name substring (`P.`/`D.`/`G.`) doing enormous
     silent balance work. Should be an explicit `ship_type` flag.
   - The class block `",9,21," contains "#classname#"` compares a display name
     against numeric ids and can never match, so starbases and scouts are not
     blocked despite the help text saying they are.
4. **A.Hoko** is the only Dead-weight-archetype hull in the game — bottom third
   on damage per PR, durability *and* dpt. The one ship with no defence on
   either lens. See [Ship-Balance-Changes.md](Ship-Balance-Changes.md).
5. **C.Aries and C.Gemini** hold the two worst ppt and dpt figures in the game
   by a factor of two. Flagged as an observation, not a proposal — C.Aries is
   simultaneously the most-fielded ship in the game, because it is an
   accumulation rather than a build, and the BR-only fix still cost balance.
6. **Capture is an untouched strategic axis.** Collective `H.*` (100), Viral
   `V.*` (75) and Collective `R.*` (50) convert eligible kills into their own
   ships. Terran, Marauder and Miner players pay a hidden tax when trading with
   them that Guardian and Neutral fleets do not. None of the analysis above
   prices this in — see [Races/Combat-Mechanics.md](Races/Combat-Mechanics.md)
   §4. It is plausibly a larger effect than any stat change considered here,
   and it is the most promising direction for the next pass.

---

## 7. Risks and caveats

1. **The "average of two lenses" is my construct**, with arbitrary equal
   weighting. It says a race should be roughly equally good whether compared at
   equal investment or equal power. A case can be made for weighting equal-PR
   higher (matchmaking is power-based) or equal-turn higher (it is what players
   experience). The finding that the two lenses disagree systematically and in
   opposite directions does not depend on the weighting; the exact ordering does.
2. **The simulator is 88% accurate on outcomes**, and does not model research or
   per-battle attitude.
3. **Random compositions measure roster depth, not expert ceilings.** Guardian's
   gap between strong observed play and weak random-composition results says it
   has a high skill floor.
4. **Sampling noise is real** — differences under ~5 points should not be acted
   on. All headline figures average 3 seeds.
5. **Viral must be modelled with its 9 reverse slots.** On its 4 native hulls it
   scores 72.8; with the mechanic, 124.5. Any analysis using the native-only
   figure is measuring a race that does not exist.
6. **G.Livid is a mission reward** (`upkeep = 0`, BR 1) and the 13 `UW.*`
   neutrals have BR 0. All are excluded. Buildable roster: **97 hulls**
   (91 class 1–10 plus the 6 Special/Strafez hulls).
7. **Special/Strafez hulls were excluded from earlier drafts** by a `class >= 1`
   filter. §5 now reports both pools. Everything in §4 (the predictor
   correlations, archetypes, per-race medians in [Races/](Races/)) is still
   computed on the class 1–10 pool and has **not** been recomputed with Special
   hulls included. Treat those as class 1–10 statistics.
8. **The intervention table in §6 predates the Special-hull correction** and was
   not re-run. See the note under it.
9. **Capture is not modelled anywhere in this analysis** (caveat 6 in §6).
10. **Flank redirection is approximated.** My engine does not implement the
    reference's `_do_flank` (dead-slot stack redirects to a random live target).
    Its effect on the aggregate race numbers is unmeasured.
11. **Per-ship metrics have almost no explanatory power at high PR.** Measured
    on twelve real 90M–270M PR fleets, a stack's own EHP per PR correlates with
    its casualty rate at **r = 0.076** and its damage per PR at **−0.056**,
    while the PR of the stack it is *paired against* correlates at **−0.560**.
    Battles at that level are ten near-independent duels — 72% of stacks finish
    at 0% or 100% — and fleets are built as ten deliberately-sized stacks rather
    than the 6 random equal-PR stacks every simulation here uses. Everything in
    §4 and §5 is therefore scoped to **who wins a given pairing**, not who wins
    a high-PR battle. See [Races/High-PR-Dynamics.md](Races/High-PR-Dynamics.md).

---

### Sources

**Game code and data**

- `app/f_com_attack3.cfm` — resolution, win condition, attitudes, capture
- `app/f_com_ship_r.cfm` — reverse-engineering rules
- `app/f_com_ship.cfm`, `app/Modules/Ships/Builder.cfm` — build rate, `buildmod`
- `gcc.ship_type`, `gcc.ship_typelog` — stats; last player change 2023-02-26
- `gcc.event_attack` + `gcc.event` — outcomes and power, 2023-02-27 onward

**Reference implementation** — `H:\Coding\eye-of-sauron` (stable checkout
backing a running Docker service; safe to reference and re-run at any time)

- `src/eye_of_sauron/sim/engine.py` — authoritative battle mechanics
- `src/eye_of_sauron/sim/comp_rules.py`, `counter.py`, `patterns.py` — lead
  eligibility, fez handling, composition tuning constants
- Two places it is *not* a GC oracle — `spam_penalty` (a hook for theorising
  future changes, not a live rule) and `_game_victor` (wiki-derived; 94.9%
  against real battles vs 99.8% for the CFML predicate). Detail in
  [Races/Combat-Mechanics.md](Races/Combat-Mechanics.md).

**This repo**

- [Races/](Races/) — per-race knowledge base and combat mechanics
- [Viral-Compendium.md](Viral-Compendium.md) — reverse rules, eligibility bug,
  Viral field data
