R-278: raising SCAN_RUNS found that SCAN_RUNS had never been read
Asked to raise the scan until the redcap's pack size stopped flipping. Measured the threshold -- unstable at 3000 and 4000, stable across twenty seeds at 6000 -- raised it, and the re-record took fifteen seconds, which was impossible. winRate takes three parameters and pickSize passed SCAN_RUNS as a fourth. JavaScript discards it, so every scan has always run at RUNS and SCAN_RUNS has never been read by anything. The fix I was asked to make was inert in the same way as the thing it was fixing. winRate takes runs now. The redcap is still n=3 with gain 6.3, arrived at stably rather than luckily; the_arrears drops out of the measurable band at an honest scan, 31 packs to 30; the_committee moves 2 to 6 and stays pinned. Claim check still passes, bestiary regenerated. The only signal was a number being too small. A fifteen-second re-record is good news, and good news is what nobody investigates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
00e5a993cc
commit
a7eca91a9c
@@ -193,7 +193,7 @@ check-creatures: OK — 102 actor specs across 8 sources, every characteristic,
|
||||
check-powers: OK — 41 powers classified, 2 wired and each one measurably changes its fight, 14 recorded as beyond the harness with a reason, 25 not fight rules
|
||||
check-anatomy: OK — 5 body plans, 36 locations, d20 complete in both modes, every location drawn, every kind handled and destructible, 20 plan changes carried 432 wounds with their severity intact, 1 flyer(s) with a survivable landing
|
||||
check-lethality: OK — 47 creatures, every one fighting exactly as recorded against the frozen party
|
||||
check-focus: OK — focus fire measured against 31 packs, helps in all of them, 31 of them by more than their own noise
|
||||
check-focus: OK — focus fire measured against 30 packs, helps in all of them, 30 of them by more than their own noise
|
||||
check-firstblood: OK — the first disabling blow is worth 40 points of wipe rate in the four-player cut, 15.4 in the six-a-side line
|
||||
check-attackers: OK — three of the four-player cut disable on 11.1% of their attacks or better, and one of them on 0.1%
|
||||
check-fight-tail: OK — a fight past 15 rounds wipes the party 1.4x as often in the four-player cut and 2.4x at six a side, and nothing was truncated
|
||||
|
||||
+1
-1
@@ -88,7 +88,7 @@ measured in the simulator and came to nothing distinguishable from noise.
|
||||
|
||||
**What you can do is kill them one at a time.** Against a pack, every agent putting
|
||||
their attacks into the same creature beats spreading them — in **every one** of the
|
||||
31 fights in this book whose outcome was ever in doubt, without exception, and by more
|
||||
30 fights in this book whose outcome was ever in doubt, without exception, and by more
|
||||
than the measurement’s own noise each time. It is worth about
|
||||
**8 points** of win rate against a pair and
|
||||
**11 points** against three or more; the best case in the book is
|
||||
|
||||
@@ -6403,3 +6403,48 @@ fights solo, read the wipe column when the agents-down column was the one that m
|
||||
explained a power by a mechanism that does not hold. Each was found by somebody asking me to
|
||||
act on it. The entry is worth keeping in that state rather than tidying: it is the clearest
|
||||
example in this log of confident, internally consistent, repeatedly wrong.
|
||||
|
||||
## R-278 — raising SCAN_RUNS found that SCAN_RUNS had never been read
|
||||
|
||||
R-277 left the redcap's recorded pack size one seed in eight from being 2 instead of 3, so
|
||||
the instruction was to raise the scan until it stopped flipping. Measured rather than
|
||||
guessed: still unstable at 3000 and 4000, stable across twenty seeds at 6000. Raised it.
|
||||
|
||||
**Then the re-record took fifteen seconds, which was impossible.** Fifteen times the scan
|
||||
work cannot cost what 400 runs cost, so I read the call:
|
||||
|
||||
```js
|
||||
const winRate = (foes, targets, seed) => { for (let i = 0; i < RUNS; i++) ...
|
||||
...
|
||||
const w = winRate(Array(n).fill(spec), "random", seedFor(...), SCAN_RUNS);
|
||||
```
|
||||
|
||||
**Three parameters, four arguments.** JavaScript discards the fourth in silence, so every
|
||||
scan this guard has ever run used `RUNS`, and `SCAN_RUNS` — declared, commented, passed —
|
||||
has never been read by anything. Changing it from 400 to 6000 also did nothing, which is
|
||||
how it surfaced: the fix I was asked to make was inert in exactly the way the thing it was
|
||||
fixing was.
|
||||
|
||||
That is the defect this project names as its own signature — a value computed and never
|
||||
read — living inside a guard, undetected by seventeen other guards, for as long as the
|
||||
scan has existed. **No guard can catch it.** The constant is used at its one call site and
|
||||
the call site looks correct; only the clock disagreed.
|
||||
|
||||
**What the honest scan changed.** `winRate` takes `runs` now, and the re-record costs 49
|
||||
seconds instead of 15:
|
||||
|
||||
- **the redcap is still n=3, gain still 6.3** — the answer R-277 gave was right, and is now
|
||||
arrived at stably rather than luckily.
|
||||
- **the_arrears drops out of the measurable band**, 31 packs to 30. Its best size was chosen
|
||||
on 1000 runs and does not survive 6000, which means it has been carrying a recorded focus
|
||||
gain that the scan would not now stand behind.
|
||||
- **the_committee moves n 2 to 6** and stays pinned either way, so nothing is measured there
|
||||
and nothing changes.
|
||||
|
||||
**The claim check still passes**: focus fire helps in all 30, all of them clear of their own
|
||||
noise. Bestiary regenerated, and it prints 30 where it printed 31.
|
||||
|
||||
**The lesson is not about arity.** It is that the only signal was a number being too small,
|
||||
and I nearly did not look — a fifteen-second re-record is good news, and good news is what
|
||||
nobody investigates. The same instinct that made the README quieter as the build got worse
|
||||
(R-268) made this cheaper as the scan got weaker.
|
||||
|
||||
+14
-4
@@ -51,7 +51,13 @@ const RUNS = 1000;
|
||||
const SEEDS = [11, 4242, 90210];
|
||||
const SEED_MODE = "per-creature";
|
||||
const SIZES = [2, 3, 4, 5, 6];
|
||||
const SCAN_RUNS = 400;
|
||||
/* R-278. 400 was enough to find a size and not enough to find the SAME size twice. For
|
||||
the redcap the criterion is near-tied — |win-50| is about 25 at two of them and 23 at
|
||||
three — so a 400-run scan picked 2 instead of 3 roughly one seed in eight, and the
|
||||
recorded gain moved 6.3 to 4.9 with it. Measured rather than guessed: the pick is still
|
||||
unstable at 3000 and at 4000, and stable across twenty seeds at 6000. This is --update
|
||||
only, so the build pays nothing; re-recording pays about half a minute. */
|
||||
const SCAN_RUNS = 6000;
|
||||
/* A pack whose spread-fire rate falls outside this cannot move enough to measure, and a
|
||||
row that cannot move is a row that would pass this guard whatever happened to it. */
|
||||
const MEASURABLE = [15, 85];
|
||||
@@ -71,12 +77,16 @@ const party = PARTY_KEYS.map(k => {
|
||||
to roll its own LCG, so the two guards described the same game with different dice and
|
||||
no figure here could be reproduced from simulate.mjs. */
|
||||
const rngFor = makeRng;
|
||||
const winRate = (foes, targets, seed) => {
|
||||
/* R-278: `runs` was not a parameter. pickSize has always passed SCAN_RUNS as a fourth
|
||||
argument to a three-argument function, so JavaScript discarded it and every scan ran at
|
||||
RUNS instead — the constant was declared, documented, passed and never read, which is
|
||||
the defect this project keeps finding in itself, sitting inside a guard. */
|
||||
const winRate = (foes, targets, seed, runs = RUNS) => {
|
||||
let won = 0;
|
||||
for (let i = 0; i < RUNS; i++) {
|
||||
for (let i = 0; i < runs; i++) {
|
||||
if (runFight(rngFor(seed + i * 2654435761), party, foes, { partyTargets: targets }).won) won++;
|
||||
}
|
||||
return (100 * won) / RUNS;
|
||||
return (100 * won) / runs;
|
||||
};
|
||||
const mean = a => a.reduce((x, y) => x + y, 0) / a.length;
|
||||
|
||||
|
||||
+13
-16
@@ -94,11 +94,11 @@
|
||||
},
|
||||
"supporter": {
|
||||
"n": 2,
|
||||
"pinned": 4
|
||||
"pinned": 4.7
|
||||
},
|
||||
"hollow_man": {
|
||||
"n": 6,
|
||||
"pinned": 91.8
|
||||
"pinned": 92.3
|
||||
},
|
||||
"the_tenant": {
|
||||
"n": 2,
|
||||
@@ -106,7 +106,7 @@
|
||||
},
|
||||
"the_choir": {
|
||||
"n": 6,
|
||||
"pinned": 99.4
|
||||
"pinned": 99
|
||||
},
|
||||
"precedent": {
|
||||
"n": 3,
|
||||
@@ -138,7 +138,7 @@
|
||||
},
|
||||
"quarantine_unit": {
|
||||
"n": 2,
|
||||
"pinned": 0.2
|
||||
"pinned": 0.1
|
||||
},
|
||||
"cuckoo_array": {
|
||||
"n": 2,
|
||||
@@ -149,7 +149,7 @@
|
||||
},
|
||||
"long_walker": {
|
||||
"n": 2,
|
||||
"pinned": 0.1
|
||||
"pinned": 0.6
|
||||
},
|
||||
"auditor": {
|
||||
"n": 4,
|
||||
@@ -175,7 +175,7 @@
|
||||
},
|
||||
"knockers": {
|
||||
"n": 6,
|
||||
"pinned": 99.7
|
||||
"pinned": 99.8
|
||||
},
|
||||
"spriggan": {
|
||||
"n": 3,
|
||||
@@ -186,11 +186,11 @@
|
||||
},
|
||||
"nuckelavee": {
|
||||
"n": 2,
|
||||
"pinned": 8.2
|
||||
"pinned": 8.5
|
||||
},
|
||||
"hedley_kow": {
|
||||
"n": 6,
|
||||
"pinned": 97.8
|
||||
"pinned": 97.3
|
||||
},
|
||||
"apple_man": {
|
||||
"n": 2,
|
||||
@@ -200,8 +200,8 @@
|
||||
"noise": 0.8
|
||||
},
|
||||
"the_committee": {
|
||||
"n": 2,
|
||||
"pinned": 100
|
||||
"n": 6,
|
||||
"pinned": 99.9
|
||||
},
|
||||
"night_registry": {
|
||||
"n": 4,
|
||||
@@ -244,7 +244,7 @@
|
||||
},
|
||||
"switchboard": {
|
||||
"n": 6,
|
||||
"pinned": 86.9
|
||||
"pinned": 85.9
|
||||
},
|
||||
"the_census": {
|
||||
"n": 3,
|
||||
@@ -269,7 +269,7 @@
|
||||
},
|
||||
"the_stanchion": {
|
||||
"n": 2,
|
||||
"pinned": 5.8
|
||||
"pinned": 5.2
|
||||
},
|
||||
"the_yield": {
|
||||
"n": 2,
|
||||
@@ -287,10 +287,7 @@
|
||||
},
|
||||
"the_arrears": {
|
||||
"n": 2,
|
||||
"spread": 14.7,
|
||||
"focus": 21,
|
||||
"gain": 6.3,
|
||||
"noise": 1.5
|
||||
"pinned": 14.4
|
||||
},
|
||||
"the_margin": {
|
||||
"n": 5,
|
||||
|
||||
Reference in New Issue
Block a user