Spaces:
Paused
Paused
Commit History
-
Phase 5: the roster can carry a player's own genome
Claude +1: Claude Opus 5Unverified signatureThe keystone here turned out to be a validator, not a UI. Once a genome can arrive from outside the program -- a mirror someone was sent, an imported ladder, an edited file -- it stops being data this project produced and becomes data it is HANDED. And it is not inert: toOverrides writes it straight into the live ARCHETYPES/STRATEGIES tables the engine reads every think cycle. GENOME_SCHEMA is already the exact statement of what a legal gene is, so it is the exact validator. sanitizeGenome is a WHITELIST: a key the schema does not name is dropped rather than passed through, every number is coerced and clamped to its own gene's bounds, every categorical must be a declared allele, the mix is filtered to the known alphabet and capped in length. Nothing throws -- bad input yields a duller AI, never a broken one or an exception mid-tournament, which is the same tolerance mutation already relies on. Probing it with deliberately hostile input caught a real hole: Number([]) is 0, so `"garris…60d3ef9
-
Phase 3 (part 2): the in-game Tournament tab — round-robin, Swiss and knockout
Claude +1: Claude Opus 5Unverified signaturedocs/competitions-and-elo.md's Phase 3 wants real tournaments in the game: a format picker, a field builder, a budget estimate before you commit, round-by-round progress, live standings and a bracket view, with every pairing folding into the ladder. Part 1 landed pairing.js (the shared schedules); this stage wires all three formats into the Worker and the competition screen. Quick Duel, the roster and the standings screen are untouched in behaviour. - competitionWorker.js gains a SECOND job kind: `{ kind: "tournament", format, field, difficulty, worlds, seeds, seedBase, rounds? }` -> runTournamentJob(job, onProgress). A job with no `kind` is still the original single duel, dispatched to runCompetitionJob completely unchanged. The tournament's injected `runPair` is a thin wrapper around runCompetitionJob ITSELF, so a tournament pairing is byte-for-byte the same work a Quick Duel already does — side-swapped both directions, replicate-parity swapAsym, one pinned difficulty for …204faa9 -
Phase 2 (part 2): roster + persistent ladder wired into Quick Duel, archetype threaded end-to-end
Claude +1: Claude Sonnet 5Unverified signatureTurns Phase 1's one-off Quick Duel into the persistent competition mode docs/competitions-and-elo.md Phase 2 describes: Quick Duel / Roster / Standings, one coherent mode behind a small tab row, all three reading and writing the SAME competitionLedger.js ledger. THE KEY UX CHANGE: ad-hoc, free-typed entrants are retired. Each Quick Duel entrant is now either picked "From Roster" (a <select> of competitionLedger's current roster) or drafted "New Entrant" (name + strategy + archetype + faction) -- a new draft joins the roster, via addRosterEntry, the instant startDuel() actually commits to running (after buildJob's own validation passes, before the Worker spins up), never on a keystroke, so an abandoned draft never pollutes the roster. A finished duel's rows are folded into the ledger for the pinned difficulty bracket via recordCompetition alone -- no second, hand-rolled elo.js applySeries call in competition.js. The results view's old "Elo isn't saved" line is gone, replaced by "Elo up…628afc1 -
Phase 1: the in-game Quick Duel screen (competition.js, competitionWorker.js)
Claude +1: Claude Sonnet 5Unverified signaturedocs/competitions-and-elo.md's Phase 1 finishes here: the previous stage on this branch landed elo.js and tools/duelCore.js (the fairness-critical match primitive extracted out of tools/ailab.js); this stage builds the actual screen that uses them. - competitionWorker.js (new, root, module Worker): imports ONLY tools/duelCore.js (pinnedDuelDials/duelSeed/runDuelMatch) -- never tools/ailab.js, whose ~1600 lines of CLI/printing/search/leaderboard code have no business in a browser bundle. Since ailab.js's own runDuel/runSwappedDuel/flipDuelResult aren't reachable from here (and flipDuelResult isn't even exported), the side-swapped world x seed loop is a small, fresh re-implementation of runSwappedDuel's own SHAPE, built only from duelCore's primitives: one shared `dials` object for both sides (the fairness point), swapAsym by replicate parity, both directions per (world, rep) so neither entrant is stuck in one seat, each match posted as `{type:"progress", completed, tota…d2a6f14