From d3a4bcd96a93f2fa39770cea834499d8a673f282 Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Sat, 29 Aug 2026 02:47:47 -0400 Subject: [PATCH] docs(features): why value-weighting would deepen the collinearity, not fix it `value_destroyed` is already `P(destroyed) x Location::value()`. Weighting criticals by the same function gives the two columns a shared basis on top of a shared driver, which aligns them rather than separating them. The orthogonal half is the through-armour term, and by construction: four LB-X firing cluster into whole armour score 0.393, all of it the natural 2, while `value_destroyed` is exactly 0.00000 - a critical through intact armour destroys nothing, so no weighting lets the other column see it. The 0.77 comes from the stripped regime, where the two measure the same event from two sides. Recorded with two cautions: a corpus predating the through-armour term carries only the collinear half, and the recording bot never priced the column, so no decision in it was taken to fish for criticals. If decorrelation is wanted, the column splits along a seam that is already in the arithmetic. --- plan/features.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/plan/features.md b/plan/features.md index 1a3a07c..58730fd 100644 --- a/plan/features.md +++ b/plan/features.md @@ -2150,3 +2150,70 @@ no damage on a target. - [x] Give `InfantryWeaponHandler` a declared exclusion off the handler rather than off the name. 648 pairs, one wire field, and the silent zeros fell from 402 to 44 + +## On value-weighting `expected_criticals`, and the +0.77 collinearity + +The ablation on the 45-match corpus put `expected_criticals` at **+0.77 +collinear with `value_destroyed`**, r² +0.0042 (an upper bound, since least +squares cannot do worse with an extra column) and agreement **−0.0044**, which +is the informative half and went the wrong way. The suggested remedy is to +weight criticals by `Location::value()` so the column becomes independent rather +than collinear. + +**I think value-weighting would make the collinearity worse, not better**, and +the reason is what `value_destroyed` already is. + +`value_destroyed` is `sum over locations of P(destroyed) x value(location)`. It +is *already* the value-weighted column. Weighting criticals by the same function +would give the two columns a shared weighting basis on top of a shared driver - +both would be "damage getting into locations, priced by `Location::value()`" and +differ only in whether the location dies. That aligns them; it does not separate +them. + +**What is actually orthogonal is the through-armour term**, and it is orthogonal +by construction rather than by choice. Four LB-X firing cluster, measured: + +| target | `expected_criticals` | of which TAC | `value_destroyed` | +|---|---|---|---| +| whole armour | 0.393 | **100%** | **0.00000** | +| half armour | 0.494 | 79.5% | **0.00000** | +| stripped | 10.160 | 3.9% | 0.00573 | + +Where this column is entirely the natural 2, the other column is exactly zero - +a critical through intact armour destroys nothing, so `value_destroyed` cannot +see it at any weighting. The 0.77 has to be coming from the stripped regime, +where the internal term dominates and the two are measuring the same event from +two sides. That part is *meant* to correlate: the volley that destroys a +location is the volley that got inside it. + +**Two things worth checking before anyone fits to that number.** + +The ablation may have measured a column without its orthogonal half. The +through-armour term landed late in this branch; a corpus recorded before it +carries the internal term alone, which is precisely the half collinear with +`value_destroyed`. Whoever repeats this at ~190 matches should check which +version the corpus holds - the answer is in whether a volley against intact +armour scores anything at all. + +And the corpus was recorded by a bot whose weights did not price the column, so +it contains no decision that was *taken* to fish for criticals. A feature that +exists to enable a behaviour the recording bot never exhibited will look +uninformative on that corpus close to by construction. That is not an argument +that the column is good; it is an argument that this corpus cannot say. + +**If decorrelation is wanted, split the column rather than weighting it.** The +two terms are already separate in the arithmetic, they have different drivers - +one scales with internal damage, the other with the *count* of hit-location +rolls - and they mean different things a player would name differently: +exploiting a hole, and fishing for one. Splitting costs nothing but a column and +would let the fit price them apart. Value-weighting costs the thing the feature +was built to be. + +- [ ] jmm's original objection to value-weighting stands and is about + calibration rather than collinearity: `Location::value()`'s own + documentation says its figures are round numbers chosen to rank locations + and that nothing downstream should read them as calibrated. Importing them + into a column whose purpose *is* calibration is the trade to argue about, + and it has not changed +- [ ] Re-run the ablation at ~190 matches against a corpus that carries the + through-armour term, and split the column before weighting it -- 2.54.0