Tweaker#16
Open
ShadyMccoy wants to merge 3 commits into
Open
Conversation
ShadyMccoy
pushed a commit
that referenced
this pull request
May 6, 2026
First post-matchmaker season. Pinwheel_g1_24119c (PHASE_TICKS=3 vs parent's 4) takes rating-leader AND round-robin. First descendant in this work to outrank its parent in a season. 200-match rating phase + 11-round round-robin completed in 13 seconds — about 15-20x faster than the 800-match seasons we ran before, because the info-gain matchmaker concentrates the budget on uncertain bots. Compound and forward-aware Spearhead descendants (loop 9) didn't crack the top. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg
ShadyMccoy
added a commit
that referenced
this pull request
May 6, 2026
* Seed tournament/rankings.json from 1000-match rating run Initial seed data for the new PL-driven league/season system. One pool-play batch on lab1 (--rating --matches 1000), 140 players, 50-65 matches per bot. PL fit converged in 74 iterations. Spread 750 (Tactician) to 1539 (Spearhead). https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Prune 41 sub-950 bots from active tournament pool Drops the long tail of weak factory archetypes (Pulse_*, Stencil_*, Probe_*, Cluster_*, Patient_*, Drift_*, Scatter_*) plus a handful of named characters with near-zero win rates on lab1 (Hunter, Berserker, Vampire, Avalanche, Anvil, Swarm, Tactician). Pool: 140 → 99 active. Doubles per-bot match coverage in subsequent seasons and stops noise at the bottom of the rating distribution from drowning out signal. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #6: Spearhead sweeps both crowns First post-prune season (99-bot pool, 800-match rating phase on lab1 + 21-round RR on lab3). Rating leader and round-robin winner are both Spearhead (1418 rating, 88% rating-phase wins, PPG=6.24 in RR). Top 10 by rating: Spearhead, Pinwheel, Crusader, Stalker, Vanguard, Membrane, PressureSink, Spearhead_g1_859468, Trinity, Bully_03. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Append season matches to matches.jsonl cmdSeason was building flagged-match entries but never feeding the global match log, so rating-phase + round-robin matches from --season runs didn't contribute to the Plackett-Luce fit in npm run rank. Now every match (rating phase tagged with --map, round-robin tagged with --season-rr-map) is pushed into the match log alongside the flag detection, and appendMatches is called at the end of cmdSeason with the same "Logged N matches ... Run npm run rank to refresh" footer that pool play already emits. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Spawn first descendants of Spearhead and Crusader; fix archive clobber Spawned two gen-1 descendants, one per Season #6 champion (both crowns went to Spearhead, but I diversified the second slot to Crusader for a different family). Spearhead_g1_3f955f - rear-flank diagonal kernel weights bumped from 1 to 2 (was 1, was 1) for fuller wedge formations. Targets the early flanking eliminations seen in season #6 losses 3-5 (ticks 95-112 vs Pinwheel/Lance/Membrane). Crusader_g1_5ae640 - require a 5% kill margin (myEff > enemy * 1.05) before committing instead of myEff > enemy. Skips razor-thin kills that leave the captured tile at near-zero residual strength and immediately recapturable. Also fixes a real bug in applyArchivalForSpawn: it rebuilt src/strategies/archive.js from lineages.json's active=false set, silently clobbering manual --archive-add entries (whose lineage flags stay active=true). archive.js is the source of truth for the STRATEGY_LIST filter; lineage.active is auxiliary. Now we union the existing archive.js with the lineage-derived names before writing. The manual 41-bot prune was lost on the first register-descendant and has been restored alongside this fix. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #7: Stalker takes round-robin crown on lab3 First post-spawn season. Spearhead defends rating-phase #1 (88% wins, 1344) but Stalker takes the round-robin on lab3 with 8 wins out of 21 and PPG=6.95, dethroning Spearhead from the larger map. The new Spearhead descendant (Spearhead_g1_3f955f, rear-flank kernel weights 2 instead of 1) lands rating-phase #9 with 56% wins and round-robin #3 - a respectable debut but still worse than the ATTACKER_BONUS=1.45 sibling at #4. Crusader_g1_5ae640's 5% kill-margin tweak is statistically neutral vs the parent (1083 vs 1084, both within RD ~52). Champions: rating-leader Spearhead (defended), round-robin Stalker (new). Spearhead family now occupies 3 of the top 10 rating slots. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Drop the per-family active-member cap Removes DEFAULT_FAMILY_CAP, the --family-cap CLI flag (run.js + loop.js), the family-cap branch in applyArchivalForSpawn, and the help-text mention under --register-descendant. The cap was an ad hoc diversity guardrail. In practice it forced auto-sibling-archival on every spawn from a saturated family, which just churned the same family without adding signal. Diversity is better controlled by which parent we spawn from each iteration; the cap added a constraint without fixing the underlying choice. Global-weakest archival on spawn is preserved — pool size still stays zero-sum — and so is the family-suicide guard (a family can't archive its only active sibling on its own spawn). https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Spawn Spearhead and Stalker descendants for Season 3 Loop 1 by-the-book: Season #7 champions were Spearhead (rating-leader) and Stalker (round-robin), each gets one gen-1 descendant. Spearhead_g1_fbedc2 - body-far-behind kernel weight 2 (was 1). Strengthens preference for two-deep rear columns; explores a different kernel axis from the existing Spearhead siblings. Stalker_g1_8767f6 - distant-prey growth bank +0.75 (was +0.5). Two of Stalker's losses ran to max-ticks; loosening the beatability gate on distant prey lets it engage farther while the strict adjacent-attack guard prevents suicide. Auto-archived: Drift_W and Drift_NW (global weakest on spawn). https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #8 + spawn next-loop descendants Season #8 champions: Spearhead (rating-leader, again) and Crusader (round-robin). Two new gen-1 descendants: Spearhead_g1_dc0148 - friendly-target penalty -20 (was -10). Stronger expansion bias; explores the target-preference axis the three existing Spearhead siblings haven't touched. Crusader_g1_5f8b45 - target weakest winnable enemy instead of strongest. Single comparison flip; tests whether the weakest-prey heuristic that works for Stalker also works for Crusader. Auto-archived: Defender, Drift_E. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #9 + spawn 2x Spearhead descendants Season #9 champions: both crowns went to Spearhead. By-the-book gives 2 spawn slots from the same parent; two distinct mutations: Spearhead_g1_bf0e20 - empty-target bonus +30 (was +20). Stronger pull toward expansion when stencil scores tie. Spearhead_g1_4f6c9f - commit threshold 0.75 (was 0.5). Skips thin forward pushes; lets the army accumulate strength a tick longer. Adjacent kills unaffected. Auto-archived: Scatter_05, Cluster_01. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #10: closes the 3-loop run Final season of the loop run. Spearhead defends both crowns again (rating-leader + round-robin). 3 loops in this session: Loop 1 (Season #8): Spearhead + Stalker spawned Loop 2 (Season #9): Spearhead + Crusader spawned Loop 3 (Season #10): 2x Spearhead spawned 6 new gen-1 descendants registered, 6 weakest bots auto-archived. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Spawn 2x Spearhead descendants for Season 6 Loop 4 by-the-book: Season #10 sent Spearhead to both crowns again, so two more Spearhead variants from unexplored levers: Spearhead_g1_1f0d47 - ATTACKER_BONUS 1.5 (was 1.4). Pushes further along the axis where sibling _859468 (1.45) succeeded. Spearhead_g1_72c4f8 - body-immediately-behind kernel weight 4 (was 3). Heavier weight on the most directly-useful support cell. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #11 + spawn 2x Spearhead descendants for loop 5 Season #11 sent both crowns to Spearhead again. Two more Spearhead variants on previously-untouched levers: Spearhead_g1_1b59ef - target weakest winnable adjacent enemy instead of strongest. Mirrors Crusader_g1_5f8b45's experiment in a different family. Spearhead_g1_7a4f15 - friendly-target penalty -30 (was -10). Stronger than _dc0148 (-20). Tests further aggressive expansion. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #12 + spawn Stalker and Pinwheel descendants Season #12 broke Spearhead's monopoly: rating-leader Stalker, round-robin Pinwheel. Two new gen-1 descendants: Stalker_g1_86aa0f - BONUS 1.45 (was 1.4). Mirrors the lever that worked for Spearhead. Pinwheel_g1_24119c - PHASE_TICKS 3 (was 4) for faster directional rotation. First Pinwheel descendant. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #13 + spawn Stalker and Spearhead descendants for loop 7 Season #13: Stalker (rating-leader, defended) + Spearhead (round-robin). Two new gen-1 descendants: Stalker_g1_bfb56c - adjacent kill margin 0.8 (was 0.6). Tighter required margin for cleaner residual strength on captured tiles. Spearhead_g1_d8bf0f - ATTACKER_BONUS 1.35 (was 1.4). Tests the cautious end of the curve siblings _859468 (1.45) and _1f0d47 (1.5) walked up. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #14 + spawn 2x Spearhead descendants for loop 8 Season #14 sent both crowns to Spearhead. Two more variants on unexplored extension axes: Spearhead_g1_36d9c1 - empty-target bonus +40 (was +20). Pushes further than sibling _bf0e20 (+30). Spearhead_g1_7f5793 - commit threshold 1.0 (was 0.5). Pushes further than sibling _4f6c9f (0.75). Maximum consolidation. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #15 + close 5-loop run Final season of the second 5-loop run. Spearhead defends both crowns again under the shorter 400-match + 11-round-RR format. Loop summary: Loop 4 (S#11): 2x Spearhead spawned Loop 5 (S#12): 2x Spearhead spawned (champions Stalker+Pinwheel came in next season) Loop 6 (S#13): Stalker + Pinwheel spawned Loop 7 (S#14): Stalker + Spearhead spawned Loop 8 (S#15): 2x Spearhead spawned 10 new gen-1 descendants registered across the 5 loops; 10 weakest bots auto-archived. Spearhead family remains dominant under both season lengths. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Loosen spawn prompt: descendants can be loosely related 13 single-constant Spearhead variants and not one outranked the parent across 10 seasons. The "tiny mutation" framing pushed every descendant into the same small neighborhood of the parent's design, where engine noise drowns the signal. Replacing that with: descendants take inspiration from the parent but are free to diverge - swap fallbacks, restructure kernels, change the thesis. The only hard constraints are now the file/API contract. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Persist rating priors across seasons; info-gain matchmaker The rating tournament was sampling lineups uniformly at random, so every season the match budget was spent rebuilding ratings the global rankings.json already had. New bots got the same number of matches as well-known bots, and the per-season Glicko reset discarded everything we already knew. Three changes: - rankingsStore exposes priorMap(): { name -> { rating, played } } derived from rankings.json so the scheduler can see each bot's prior rating and accumulated match count. - runRatingTournament accepts `priors` and uses them to drive an info-gain matchmaker: each lineup anchors on the bot with the highest synthetic RD (typically a freshly spawned descendant with played=0) and fills with K-1 bots sampled from the known field (RD <= median), weighted toward the lowest-RD references. Old-vs-old matches mostly stop happening; new-vs-old dominates. Falls back to random sampling when no priors exist. - cmdSeason loads rankings.json before runSeason, threads priors through season.js to runRatingTournament, and refits PL on the cumulative (current-rules) match log after the season so the next season starts with up-to-date priors. Smoke tested with --matches 30 --seed 999: lineups concentrated new descendants as anchors, surrounded them with established bots (Spearhead, Membrane, Vanguard, etc.) — exactly the calibration shape we want. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Loop 9 spawns: compound and forward-aware Spearhead Spearhead_g1_1a87fc - compound stack: ATTACKER_BONUS=1.45 + empty bonus +30 + body-immediately-behind=4. Tests whether the three earlier siblings' levers compose additively. Spearhead_g1_e91678 - forward-aware: adds a forward-enemy penalty alongside the existing rear-support kernel, so directions blocked by a fat enemy line forward score lower. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #16: Pinwheel_g1_24119c sweeps both crowns (13s run) First post-matchmaker season. Pinwheel_g1_24119c (PHASE_TICKS=3 vs parent's 4) takes rating-leader AND round-robin. First descendant in this work to outrank its parent in a season. 200-match rating phase + 11-round round-robin completed in 13 seconds — about 15-20x faster than the 800-match seasons we ran before, because the info-gain matchmaker concentrates the budget on uncertain bots. Compound and forward-aware Spearhead descendants (loop 9) didn't crack the top. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Loop 10 spawns: two Pinwheel g2 descendants Champion of S#16 was Pinwheel_g1_24119c (PHASE_TICKS=3). Two g2 variants: Pinwheel_g2_ae8180 - PHASE_TICKS=2. Pushes the trend (4->3->2): even faster rotation. Pinwheel_g2_22a2a2 - asymmetric phases [W=2, N=4, E=2, S=4], aligning the time budget with lab1's wrap geometry (24-wide horizontal wrap, 18-tall non-wrap). https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #17: Spearhead + Stalker (17s)https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Loop 11 spawns: Spearhead with Conqueror+Defender fallbacks, Stalker with nearest-prey tiebreak https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #18: Spearhead + Pinwheel (13s)https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Loop 12 spawns: phase-aware Spearhead, Pinwheel-Kill hybrid https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #19: Spearhead_g1_4f6c9f + Crusader (13s)https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Loop 13 spawns: Spearhead g2 with rear-support floor, Crusader with Conqueror fallback https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Save Season #20: Spearhead reclaims both crowns (16s)https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Parametric Spearhead template + GA search driver Two new files. The framework removes the LLM from the inner loop: mutations are sampled from a parameter schema, evaluated in-process with no spawn/register/commit/log overhead, and only the best vectors are reported at the end. src/strategies/parametric/Spearhead.js makeSpearheadVariant({...params, name}) -> strategy. Exposes the 7 magic constants of the original Spearhead as parameters (attackerBonus, three kernel weights, empty bonus, friendly penalty, commit threshold). SPEARHEAD_DEFAULTS reproduces the parent exactly; SPEARHEAD_SCHEMA gives bounds + sigma + int flag per knob for the search. tournament/ga.js CLI driver. Population-based GA with elite preservation, uniform crossover, per-knob gaussian mutation. Fitness = average rank across N matches against a fixed opponent pool (default: top-10 from rankings.json). Final top-K re-scored with 3x more matches for confidence. Optional --out for JSON export. Smoke test: 38 matches/sec single-threaded. The match log is bypassed entirely - per-evaluation matches don't call appendMatches, so 10k+ iterations add zero log growth. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * GA demo run: 360 evals × 20 matches in 211s, no improvement over default First non-trivial GA run on the parametric Spearhead schema: pop=24, gens=15, eval=20 matches per individual against the top-10 opponent pool. 7,200 matches total at 35.6 matches/sec single-threaded. Best discovered vector at 60-match re-eval scored fitness 2.633 vs default 2.700 (avg rank, lower=better) - within sample noise. Win rate actually regressed (5% vs 8%). Search converged on: attackerBonus=1.45 (matches the hand-tuned _859468 sibling) bodyClose=4 (matches _72c4f8 sibling) flankClose=4 (jump from default 1) emptyBonus=50 (jump from default 20) friendlyPenalty=-3 (much weaker than default -10) commitThreshold=0.35 (stays near default 0.5) Direction-of-travel matches what we found by hand. Magnitude suggests the Spearhead basin is broad and shallow - small perturbations don't escape it. To beat the parent confidently we likely need (a) more evals (1000+ to clear noise), (b) larger schema with architectural freedom, or (c) different parent families. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Matrix-knob GA + parametric Pinwheel template Two additions to the GA framework: src/strategies/parametric/Spearhead.js + makeSpearheadFromKernel({...scalars, kernel: float[25]}). + MATRIX_SCHEMA exposes the full 5x5 East-facing weight matrix as a 25-cell array knob alongside the four scalars (attackerBonus, emptyBonus, friendlyPenalty, commitThreshold). + MATRIX_DEFAULTS reproduces the original Spearhead pattern. + Internal refactor: shared spearheadFromOffsets() so the scalar-knob and matrix-knob factories use the same act(). src/strategies/parametric/Pinwheel.js + makePinwheelVariant({rotation, phaseTicks, killPriority, attackerBonus, name}). Pinwheel has 4-cell rotation order and 4-cell phase durations; both exposed as integer array knobs. + killPriority toggles between original "phase-direction first" and Crusader-style "kill-first then phase" behavior. tournament/ga.js + Schema entries can now be array-typed: {length, min, max, sigma, int?}. Mutation does per-cell gaussian; crossover is per-cell uniform. Avoids "switch parents every generation" behavior that would block matrix exploration. + --warm F flag seeds a fraction of the initial population from the schema defaults perturbed (rest random). Default 0.5 - crucial on high-dim knobs where pure random init is hopeless. + Always seeds slot 0 with unmutated defaults so elite preservation can't regress below the parent. + Pretty-prints 5x5 matrix knobs in the final report. First non-trivial matrix run (pop=30 gens=30 eval=20, 10 minutes, 18,000 matches): best vector lands fitness 2.250 vs default 2.700 - 17% better avg rank than the hand-tuned parent. Same 8% win rate at 60-match re-eval, so the gain shows up in placement consistency. The discovered kernel uses all 25 cells (including forward cells the original ignored) and has noticeable asymmetry across the perpendicular axis - not reducible to "tweak three weights." https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Add parametric Stalker schema; fix default-bot label in GA report https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Pinwheel GA: never-North asymmetric sweep beats default by 0.85 fitness GA over the parametric Pinwheel schema (rotation, phaseTicks, killPriority, attackerBonus): pop=24 gens=20 eval=20, 9,600 matches in 233 seconds. Default Pinwheel scores 2.910 fitness vs GA winner at 2.060 in a 300-match re-evaluation - 0.85 better placement, roughly 14 standard errors apart. Surprising shape of the discovered vector: rotation=[3, 0, 1, 0] = S, W, E, W (W=0, E=1, N=2, S=3) phaseTicks=[7, 6, 4, 7] killPriority=0 attackerBonus=1.319 The bot never sweeps North. ~58% of the cycle pushes West, 29% South, 17% East, 0% North. Lower attackerBonus than default 1.4. This is not a tweak we'd have hand-tuned - the original W->N->E->S symmetric sweep was a designer's prior, and the GA discovered that on lab1 (24-wide horizontal wrap, 18-tall non-wrap) ditching the N axis entirely is much more effective. Win rate is unchanged (4.3% both). So the discovered bot is more robust (avoids elimination, lands #2/#3 more often) without being more dominant (still rare to claim #1). Suggests fitness function choice matters - avg-rank rewards consistency, win-rate rewards peaks. Different objectives may surface different optima. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Add tech-loadout knob to GA (SpearheadMatrixTech schema) makeSpearheadFromKernelWithTech adds a 5-element tech array [move, stack, prod, atk, def] alongside the matrix knob. Returns a runMatch lineup entry { strategy, tech, name } that the engine honors directly. Tech is normalized to non-negative integers summing to 100 (engine requirement) - clamps negatives, scales, distributes rounding error onto the largest cells. MATRIX_TECH_SCHEMA reuses MATRIX_SCHEMA's matrix and adds the tech knob with cell range 0-50 and sigma 4. MATRIX_TECH_DEFAULTS uses neutral [20,20,20,20,20]; sanity check passes (matches default Spearhead at 2.700 fitness). Also: pretty-print rotation, phaseTicks, and tech in the GA report instead of "[N/M nonzero]" - the latter is meaningless for arrays of integer indices or normalized values. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Stalker GA result: null (default already optimal in schema) Default Stalker scored 2.567/5% wins; best GA vector at 60-match re-eval scored 2.533/7% wins - within noise. Pattern recognized: small scalar schemas (6 knobs of bounded continuous tweaks) plateau immediately because the parent already lives near the schema's local optimum. The targetMode flag stayed at 0 (weakest- first, default) in all top vectors, so the nearest-first thesis also failed to dominate. Contrast: Pinwheel (8-cell discrete rotation+phase array) found 0.85 fitness improvement; Spearhead matrix (25-cell continuous kernel) found 0.30 fitness improvement; Stalker (6 scalars) found nothing. Higher-dim schemas with structural freedom unlock real search; constant-tweaking does not. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg EOF * Add ga-controls.js: structurally-weird Spearhead baselines Quick "shouldn't work" runner. Instantiates seven matrix-Spearhead variants (default, all-zero kernel, uniform +/-1, inverted, forward- only, anti-pattern, random-uniform) plus a random-weight baseline, scores each against the same opponent pool the GA uses. Initial 200-match run revealed: - default-Spearhead fitness 2.835, wins 7.0% - all-zero-kernel fitness 3.020, wins 7.0% (!) - inverted/forward fitness 3.17-3.29, wins 0.0% The kernel barely affects win rate - all-zero matches default at 7%. The kernel's contribution shows up in placement consistency only. Adjacent-kill logic + empty/friendly bonuses are the dominant fitness drivers. Explains why the matrix GA only improved fitness by 0.30 (the kernel was already nearly maxed out for placement, and win rate is bounded by the non-kernel logic). To raise win rates we need a different schema axis (combat logic, fallback choice, time-aware behavior) - not a better kernel. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * SpearheadMatrixTech GA: 92% win rate at 300-match re-eval GA over the matrix kernel + 5-element tech array: pop=30 gens=30 eval=25, 22,500 matches in 22 minutes. Best vector at 300-match re-eval scored 92.3% wins (277/300) and fitness 0.273 vs default SpearheadMatrixTech at 5.7% wins (17/300) and fitness 2.797. That's a 16x win rate jump and ~10x improvement in avg-rank from the same engine, against the same elite top-10 opponent pool. Key discovery: the GA dumps almost all 100 tech points into MOVE (m=46 s=0 p=1 a=2 d=2 in the top vector). Per the engine's tech slopes, move=46 reduces the per-attack garrison floor from 1.0 (neutral) to ~0.72 - more strength gets thrown into combat each tick. Combined with a tuned 25-cell kernel and friendlyPenalty =-29 (3x default), the bot dominates. CAVEAT: opponents in the pool use neutral tech; our bot has a tech advantage they can't match. Need follow-up isolation: - matrix winner WITHOUT tech (neutral) vs default - default kernel WITH move-heavy tech vs default to attribute the win between kernel design and tech allocation. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Register three move-tech wrapper bots; sweep rating phase top-3 Three new gen-1 descendants, each just rebrands its parent with {move:90, stack:0, prod:2, atk:4, def:4} tech (the GA's discovered allocation). No act() changes - the entire win comes from the tech override. Spearhead_g1_7089d3 - lab1 96.2% wins (50/52), rating 1632 Membrane_g1_b9f1d5 - lab1 96.1% wins (49/51), rating 1557 Conqueror_g1_879a88 - lab1 84.3% wins (43/51), rating 1291 Season #21 (lab1, 200 matches): movers swept rating-phase #1/#2/#3. First time any season has produced a bot above 1500 rating - the previous record was Spearhead's mid-1300s. Twist on round-robin (lab3, 10 players): Phalanx (defensive) won; the movers placed #4/#6/#10. Move-heavy tech advantage doesn't carry as cleanly to the larger arena where defensive territory holding matters more. Optimal tech is map-dependent. Auto-archived: Spearhead_g1_859468, Drift_NE, Spearhead_g1_171465. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Spawn prompt: surface parent tech and techs.md so descendants can touch it The spawn prompt was building from parent source + losses + winners + strategies.md + engine-api.md, but never mentioned tech. Result: 30+ LLM-spawned descendants in this session, none touched the tech axis, even though tech allocation is the dominant rating lever. Three small additions: - loadDocs() also reads docs/techs.md - prompt receives parentTech (from CHARACTER_TECHS, fallback neutral) - new "Parent's character tech" section in the prompt with the JSON loadout, a one-line explanation of the slot, and a note that descendants can override via a `tech` field on the export Spread-inherit (`...Parent`) still carries the parent's tech as before, so existing spawn output is unchanged unless the agent deliberately adds a tech field. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Rebalance move tech: linear 2.0->0.5 garrison, no clamp; bump RULES_VERSION Move was the dominant tech axis with a 12.5x dynamic range (1.25 -> 0.10 garrison) versus def's 1.95x and atk's 1.32x. The GA exploited this immediately: dumping ~90% of tech points into move turned every tested strategy from a 5-30% win-rate underdog into a 80-96% dominant. Tech allocation became "always go move- heavy" rather than a strategic choice. New formula (linear, no breakpoint): garrison = 2.0 - 0.015 * tech tech 0 -> 2.0 garrison (double the engine baseline) tech 20 -> 1.7 (neutral allocation - now genuinely costly) tech 50 -> 1.25 tech 80 -> 0.8 tech 100 -> 0.5 Dynamic range is 4x (2.0 / 0.5), comparable to def's 2x and atk's 1.3x. Move still matters - it's the only knob that affects throughput per attack - but it no longer overwhelms. Engine-level changes: - SLOPES.move 0.0125 -> 0.0150 (rate per tech point) - MOVE_INTERCEPT = 2.0 (new intercept; replaces "pivot at neutral") - MIN_GARRISON_FLOOR removed (no clamp; the formula is the contract) - techToMultipliers special-cases move to use the linear intercept - RULES_VERSION bumped v1 -> v2; all 9300 prior matches.jsonl entries stop counting in `npm run rank`. They stay on disk. - docs/techs.md updated to describe the new formula and the cost of skipping move investment. The three move-tech wrapper bots (Spearhead_g1_7089d3, Membrane_g1_b9f1d5, Conqueror_g1_879a88) keep their tech configs as-is - they'll still benefit but to a much smaller degree, which is exactly the point. Data-driven validation pending: re-run the cross-strategy move-heavy sweep under v2 to confirm the imbalance is gone. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Move tech: lower the slope (option 1) — 1.5 → 0.5 over [0,100] The first v2 attempt used 2.0 → 0.5 (slope 0.015), which left neutral at garrison 1.7 — a heavy penalty for skipping move investment. The validation sweep showed move-heavy still beating neutral by ~60 pp, almost as much as before. The penalty just moved from "move is uniquely good" to "not investing in move is uniquely bad." Option (1): lower the slope to 0.01 and the intercept to 1.5. garrison = 1.5 - 0.01 × tech tech 0 -> 1.5 tech 20 -> 1.3 (mild penalty for low move) tech 50 -> 1.0 tech 100 -> 0.5 Dynamic range is 3× (1.5 / 0.5), comparable to def's 1.95×. Neutral garrison (1.3) is much closer to the engine's pre-tech "always leave 1" baseline. Move investment still pays off, but the gap between move-90 and other allocations should be much smaller than the original 12.5× formula or the v2-take-1 4×. Validation re-running. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Bump RULES_VERSION to v3; fix stale intercept comment User direction: with the move slope rebalance settled, mark a clean break. Bumping v2 -> v3 invalidates any v2 matches the ranker would otherwise honor (mostly empty - we only ran a few seasons under v2). Old matches.jsonl and rankings.json are being wiped this turn; a fresh --rating calibration follows. Also: the comment in techToMultipliers still referenced the take-1 intercept of 2.0; updated to 1.5 to match the actual constant. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Refresh rankings.json under v3: 2000-match calibration, all bots at RD floor Wiped matches.jsonl (was 9300 v1 entries) and rankings.json, ran a clean 2000-match --rating tournament under v3. Every active bot played 100+ matches, putting all of them at the synthetic-RD floor of 50 (maximum certainty). PL fit converged in 35 iterations. Top under v3: 1 1323 Conqueror_g1_879a88 86.1% wins (was 92% pre-rebalance) 2 1225 Spearhead_g1_7089d3 56.9% wins (was 96%) 3 1206 Stalker 68.7% wins 4 1203 Spearhead 72.1% wins (was ~30% with neutral tech) 5 1167 Membrane_g1_b9f1d5 61.2% wins (was 96%) Move slope rebalance worked as intended: Spearhead_g1_7089d3 and Membrane_g1_b9f1d5 dropped from 96% wins to 57-61%, putting them in the same competitive band as their non-tech-tweaked parents. Conqueror_g1_879a88 retained more of its advantage (still leads), suggesting Conqueror's minimum-overkill kill logic compounds particularly well with low garrison even at the reduced slope. Spread: 1323 -> 884 (439-point band across 99 bots), tighter than v1's ~800-point band but still meaningful. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg * Drop tournament/seasons.json: 17k lines of v1-era stale data All 21 seasons in the file ran under v1 engine rules. After the v3 rebalance the recorded loss contexts and ratings are misleading as a measure of current bot skill, so the file is stale data masquerading as fresh. Removing it drops ~85% of the branch diff (17,238 lines). Code that reads it (spawn.js loss-context prompt, SeasonViewer browser UI) handles a missing file gracefully via seasonStore.readStore's ENOENT fallback to { seasons: [] }. Next --season run regenerates. https://claude.ai/code/session_01KshiDLRhdzLrhizR1U2mfg --------- Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.