Spaces:
Paused
Paused
Commit History
-
Tier 3c: the AI's found-a-building protocol, and its decision predicatesβ
Claude +1: Claude Opus 5Unverified signatureThirteen sites across aiEconomy.js and aiIndustry.js hand-rolled the same four steps to found a building β findPlacement, canAct, issueBuild, spend β and two of those four fail silently when dropped. Omit `spend` and the AI founds buildings for free, its APM dial quietly no longer throttling it; omit `canAct` and it ignores the budget outright. Neither shows up as a crash, and neither moves a determinism fingerprint on the default archetype, because the default AI has `apm == null` and both calls are then no-ops. Only the APM-limited difficulties and self-play can see the difference β and five of the thirteen sites were already relying on a `canAct` in the guard three lines up rather than at the call itself. aiCommon.js gains tryBuild/tryBuildAt, and the thirteen sites become one line each. The builder stays a parameter rather than baked in: two sites (the Habitat and the first Barracks) have always founded with workers[0] instead of the nearest free worker, so they pass [workers[0]] β¦ca9c751 -
Tier 1, step 2b: expand // @ts-check coverage from 10 files to 20 (D3)β
Claude +1: Claude Opus 5Unverified signatureAdopted in the order docs/code-improvement-tiers.md sets out, cheapest and highest bug-class value first: supply, aiCommon, colliders, victory, production, scout, the three AI leaves (aiStrategy, aiDifficulty, aiArchetypes), and finally persist.js β the prize, because every save field is a State/Unit/Building field read by name, so annotating it makes save-shape drift a check-time error. Each file's exports are annotated with the shared typedefs, not just given the pragma: per the guard added in the previous commit, a pragma without annotations checks nothing. Doing it surfaced three more genuine drifts in engine/types.js, all found by the checker rather than by reading: - Building.rally was typed {x, y} but carries nodeId β the rally-to-resource feature, where a rally dropped on a live node makes a produced unit spawn already mining (engine/production.js). - Building.queue entries were typed {unitType, progress} but carry `alt`, which records that a job was charged the unit'sβ¦7e764b0
-
Make the AI controller owner-parametric: true two-sided self-playβ
Claude +1: Claude Sonnet 5Unverified signaturerunAI(state, dt, owner = "ai") and every AI phase module now resolve their controller, opponent, and fog grid from `owner` instead of hardcoding "ai"/ state.fogAI β 100% backward compatible (every pre-existing call site keeps the default and behaves byte-for-byte as before). A new, independently configured controller slot (state.playerAi, null by default) lets a second real AI drive the "player" seat in a skirmish, headless-only (tools/ selfplay.js) β nothing in setup.js/boot.js/the shipped game changed. Independent adversarial review (fairness lens + correctness lens) caught a real bug before this was accepted: Hard difficulty's economic edge (hardEdge) was seeded onto state.players.ai only, never a self-play "player" controller configured for Hard. Fixed via seedDifficultyEdge (engine/state.js), called for "player" once state.playerAi exists. engine/persist.js's save/load extension is purely additive (no SAVE_VERSION bump) β old saves round-trip unaffected. Co-Authored-By: Claude β¦916c3ad
-
Halve AI actions-per-minute for background (non-active) worldsβ
Claude +1: Claude Sonnet 5Unverified signatureA background colony (state.background, engine/galaxy.js) is a world the player isn't currently seated on. Nobody's actively watching it, so it now accrues its APM action budget at half its configured rate instead of managing its economy as briskly as the player's active seat. The attack/defense commit itself stays fully exempt from the budget, so a background world under attack still always throws whatever it has and the game always resolves. state.ai.apm itself is never rewritten - only the accrual rate (and its derived burst cap) changes. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LPPqQ7gWGMtrSJDB7nCy1re818247
-
Split engine/ai.js god file into cohesive phase modulesβ
Claude +1: Claude Opus 4.8Unverified signatureengine/ai.js was a 1147-line god object holding the whole AI. Decompose it into five cohesive sibling modules along an acyclic import graph, leaving ai.js as a 110-line orchestrator that keeps exporting runAI (so sim.js and the AI tests are unchanged): - aiCommon.js β the APM action budget (accrueActionBudget/canAct/spend), reserve-aware canAffordKeeping, and the pickBuilder worker pick. A leaf with no engine imports, shared by every phase. - aiWorkers.js β idle-worker logistics + gather steering (assignAiLogistics/ assignIdleWorkers) and the map-aware mix filters (effectiveMix, affordableOnSurface, aiDoctrine). - aiMilitary.js β defend/attack waves (aiMilitary/aiOffense), focus-fire, the scout sweep, and the target/mix picks (chooseAttackTarget, raidTarget, pickNextUnitType, counterToPlayerArmy). - aiEconomy.js β base build-out, expansion, the Foundry/Arsenal tech gates, β¦240c1d3