Spaces:
Paused
Paused
Commit History
-
Phase 2 (part 1): per-entrant archetype override (D3) and the competition ledger (D8)
Claude +1: Claude Sonnet 5Unverified signatureengine/state.js's createAiController(planetId, opts) gains an optional opts.archetype: a STRING KEY into engine/aiArchetypes.js's ARCHETYPES table, resolved via ARCHETYPES[opts.archetype] when valid, falling back to archetypeFor(planetId) exactly as before otherwise -- every existing call site (none of which pass it) stays byte-identical. This is what lets a future competition entrant carry its own doctrine instead of both seats sharing whatever temperament the world hands out ("Rusher vs Turtle" becomes a real matchup). elo.js gains a small additive PROVISIONAL_GAMES=10 export (INITIAL_RATING's own precedent) so kFactor and the new ledger's standings read-back share one constant instead of two copies of the number 10. New root-level competitionLedger.js implements D8: a versioned, sanitized, untrusted-input-hardened roster + per-bracket Elo ladder, mirroring engine/persist.js's sanitizeSave/cleanEntity discipline deliberately, not by analogy -- an exact-match COMPETITION_VERSION gate…925767d -
Elo: reject a same-name entrant/candidate pairing instead of silently corrupting its rating
Claude +1: Claude Sonnet 5Unverified signatureBlocker from Phase 1 review (docs/competitions-and-elo.md): elo.js's RatingsTable is a plain object keyed by entrant name, and nothing upstream of it guarded against two entrants/candidates sharing a name. When aName === bName, `ratings[aName]` and `ratings[bName]` in applyResult alias the SAME object -- the function computes A's post-match entry, writes it, then unconditionally overwrites that exact slot with B's post-match entry (whichever assignment runs second always wins) -- A's half silently vanishes and the survivor represents neither side. Reproduced live: the in-game Quick Duel screen with both entrants named "Same Name" ran to completion and showed TWO Elo cards, both reading the identical, corrupted "1200? (0)" -- no error, no warning. Three-layer fix, defense in depth: - elo.js: applyResult now throws a clear Error when aName === bName, before touching `ratings` at all -- the shared backstop every caller (present or future) gets for free, per D1's "one implementation" …87a9642 -
Phase 1 foundations: elo.js, tools/duelCore.js, and an Elo column in ailab's CLI
Claude +1: Claude Sonnet 5Unverified signaturedocs/competitions-and-elo.md's Phase 1 turns the tuning bench into the first slice of an in-game competition: a rating system (D7), one shared between the CLI and (in a later stage of this same phase) the game (D1), plus the fairness-critical duel primitive split out where a browser Worker can reach it next. UI and the Worker itself are out of scope for this stage on purpose -- foundations only. - elo.js (new, root, `// @ts-check`, JSDoc-annotated): INITIAL_RATING, expectedScore, kFactor, applyResult, applySeries -- plain Elo (D7): K=40 while provisional (<10 games, wins over the rating-based rule even at a seeded-high rating), then 20, then 10 above 2400. applyResult creates missing entrants at INITIAL_RATING/0 games rather than throwing, and derives both sides' deltas from ONE shared actual-minus-expected value so they're exact negatives of each other whenever both sides share a K-bucket (verified: two independently-rounded expectedScore(a,b)/expectedScore(b,a) calls are…9af0ddd