Spaces:
Paused
Paused
Commit History
-
Tier 1, step 2: make the type layer real (D1, D2, D4)
Claude +1: Claude Opus 5Unverified signatureengine/types.js had drifted from the shapes it claims to model. A tsc probe against the seven typedefs returned 20 errors and zero on the control fields: State.popCap, State.playerAi AiState.strategy, AiState.lastThreatAt Galaxy.lanes, Galaxy.laneSeq GalaxySettings.startId, GalaxySettings.popCap Diplomacy.provokedAt/goodwill/request/lastFavorBucket Order.tx/ty/explore/patrol Archetype.wantsRefinery/turretCount/maxBarracks/garrison State.playerAi is the central field of the self-play feature, left undeclared by the very commit that added it. The drift was invisible because every factory assigns to a const before returning, so TypeScript never runs an excess-property check — and it actively BLOCKED adoption: annotating one function in engine/supply.js with @param {State} produced three errors in correct code, which teaches contributors to distrust the checker. All 20 now resolve; typecheck stays clean. Added a Lane typedef (createLane's real shape) and declared the lazil…6db3533
-
P8 Combat Depth: uphill concealment (real LOS), veterancy ranks, tiered-kill event fields
Claude +1: Claude Sonnet 5Unverified signatureThree combat.js-anchored proposals from Phase 8 of the improvement roadmap, landed as one coherent pass through engine/fog.js and engine/combat.js since a later team's fog-render-perf work depends on this cell/fog-grid contract: 1. Real LOS pass (docs/improvement-proposals.md lines 831-839): fog.js's reveal() now takes the source's own terrain (one sampleTerrain call per source) and caps how far a NON-high-ground source reveals a high-ground CELL at UPHILL_FRAC (0.55) of its normal sight radius — a mesa is a stat pad AND a genuine blind spot for anyone still in the lowland. combat.js's nearestEnemy/spreadEnemy apply the identical rule to acquisition via a shared highGroundCap helper, so a unit can't shell a held ridge blind just because its raw aggro happens to reach that far. Both fog grids (state.fog, state.fogAI) inherit this through the same updateFog/reveal path — no AI-side code needed, verified with a dedicated symmetry test. 2. Veterancy ranks (line…8f89fac
-
Add JSDoc typedefs + // @ts-check for the core sim shapes (no build step)
Claude +1: Claude Opus 4.8Unverified signatureAddress the top architectural-review finding — an untyped mutable `state` bag threaded through ~360 functions, where a mistyped or renamed field reads undefined silently and the same-seed determinism test can't see it. Now that class of bug is a check-time error. Zero runtime cost and no build step: the shipped code stays plain ES modules. - engine/types.js: JSDoc @typedefs for the core shapes (State, Unit, Building, Player, AiState, Resources, Cargo, Order, ResourceNode, GameMap, Fog, Market, Diplomacy, Galaxy, …). No runtime code, never imported — global typedefs the checker and any editor's TS language service resolve project-wide. - jsconfig.json + `npm run typecheck` (tsc, noEmit): checking is OPT-IN per file via a `// @ts-check` pragma, so coverage grows file-by-file. Editors check the annotated files live with no install; the script needs a tsc available. - Enabled + annotated on the core data + hot path: state.js (the factories bind the types to the real objects), …e3658fa -
Tier 4: per-tick sim cost — grid int keys, fog squared-dist, galaxy scan cadence
Claude +1: Claude Opus 4.8Unverified signature- Spatial grid (engine/grid.js): replace the per-unit / per-query "cx,cy" STRING cell key (allocated + hashed every tick on the hot broad-phase path) with a packed integer key (cx+PAD)*STRIDE+(cy+PAD). Cells bucket the same units in the same order and queryNeighbors visits the same cells in the same fixed loop, so candidate lists are byte-identical — the determinism test stays green. - Fog reveal (engine/fog.js): compare squared distances (dx*dx+dy*dy vs sight*sight) instead of a Math.hypot per candidate cell, and hoist the row index + dy out of the inner loop. - Galaxy scans (engine/galaxy.js): the conquest/milestone/relief checks change on a minutes timescale but ran every frame — throttle to ~1/sec (tick 1, then tick % 20) on the same deterministic integer schedule the background round-robin uses. Odyssey-only path. All verified byte-identical: determinism + engine-purity + fog + balance + odyssey + wonder tests green (549 total). No behaviour change on the skirmish r…4559046
-
Ranger scout unit + AI hunts down hidden Command Centers
Claude +1: Claude Fable 5Unverified signatureTwo related recon features. New unit — the Ranger (engine/entities.js, scout.js, render.js): - Trained at the Command Center like a Worker; cheap (45 ore) and fragile (50 hp) with only a token bite for self-defence. Its own role "scout", so it never auto-acquires and is never swept into the combat army. - Its three edges are a scout's: far sight (340, more than any combat unit), all-terrain movement (the existing `allTerrain` flag — ice fields and rough ground never slow it), and an autonomous SCOUT MODE (order "scout", the E key / a panel button). In scout mode it heads for the nearest unexplored ground on its own, its sight reveals it, it re-picks — a frontier that expands from home until the map is charted, then it patrols to keep vision fresh. Persistent and deterministic (a row-major fog scan, no rng), so it's safe in the sim. - Draws as a light recon craft with a sensor ring, distinct from the Skiff. A generic-unit fallback in the renderer keeps the "every enti…c310233 -
Asymmetric world modifiers: two starts that differ
Claude +1: Claude Fable 5Unverified signatureWorld modifiers could only tilt both sides equally. A world may now carry an `asym: { player, ai }` block that overrides a modifier for just ONE side, so the two corners play as a real asymmetric matchup. - map.js: sideMod(state, owner, key) resolves the owner's asym override, then the shared modifier, then the default — so symmetric worlds and map-less test stubs behave exactly as before. Movement (speed), fog (sight), combat (aggro sight), production (build time), and gather (a new per-side gatherMult economy edge) all read it with owner context. - Two asymmetric worlds, telegraphed on the map card: - Oort "Contested frontier": player banks 20% more per haul (rich claim); the AI builds 18% faster (war factory). Out-mine vs out-build. - Nimbus "Storm front": player sees almost normally (0.95) where the AI stays half-blind (0.75), but the AI's units are 12% faster. Out-scout vs out-tempo. Safe for the resolves-to-a-winner guarantee by construction: an AI-side edge…d228ca7 -
Content: terrain — rough ground, high ground, and no-build zones
Claude +1: Claude Fable 5Unverified signatureGives the map spatial identity without any pathfinding rewrite: a static per-cell terrain field (a flat Uint8Array, fog-grid idiom) drives three purely local effects. - map.js: TERRAIN table + generateTerrain (fixed fractional feature specs, mirrored for fairness, scaling self-similarly) + sampleTerrain (O(1), OPEN out of bounds). Consumes ZERO rng draws, so the node layout stays byte-identical and determinism is untouched. Terrain is static for the whole match. Per-world specs on Glacius (rough ice fields flanking a central lane — a soft choke), Forge (rough industrial sprawl), and Pyralis (a high-ground mesa); the original three worlds stay all-open. - movement.js: rough ground slows the step (never to zero — no unit can ever be trapped, so a wave can't deadlock and the resolves-to-a-winner guarantee holds). Honours an optional per-unit allTerrain flag (forward-compat). - fog.js: high ground sees farther (per-source sight scaled by terrain). - combat.js: an attacker …03f71c7 -
Give the AI its own fog of war — it must scout for intel too
Claude +1: Claude Fable 5Unverified signatureThe AI is no longer omniscient. It now plays under its own fog grid (state.fogAI, updated every tick just like the player's), and its intel-dependent decisions are gated by what it has actually seen: - Counter-building only counts player combat units currently in the AI's vision. No sight of your army means no counter-pick — it falls back to its archetype mix until it scouts or fights you. - Worker assignment and expansion only consider nodes the AI has discovered: charted surface deposits always (map knowledge for both sides), hidden caches only once scouted. It can't mine or expand to a cache it hasn't found. To earn that intel it keeps one spare unit scouting once its army can lend one — a box sweep of the contested middle (plain-move waypoints, so it reveals fog rather than diving into a fight), re-issued whenever the scout falls idle or dies, and kept out of the attack wave. The base-march attack is unchanged: in a mirror 1v1 both sides know the enemy start, so that's …816885c -
Add hidden resource caches discovered by exploration
Claude +1: Claude Fable 5Unverified signatureBeyond the charted deposits near each base, every map now seeds eight hidden caches out in the contested middle and along the vertical extremes — extra ore, crystal, and radioactive deposits the survey missed. They stay invisible (main view and minimap) and untargetable until one of your units scouts their cell; the fog's permanent "explored" memory doubles as the discovery record, so a found cache stays on the map. A new isNodeDiscovered(fog, node) in fog.js is the single gate render, minimap, and right-click picking all share. Caches often hold a commodity a world's surface lacks (crystals on Korrath, radioactives on Vesper), so scouting can unlock a resource — and the tech that needs it — you'd otherwise have no access to, giving exploration a real strategic payoff. They're placed after the near-base ore guarantee (so a hidden cache can never satisfy it) at fixed, mirrored, deterministic positions well clear of both bases. The AI's effectiveMix now keys off surface deposits only, …df20f1d
-
Tier 4 AI parity + planet roster with per-world rule modifiers
Claude +1: Claude Fable 5Unverified signatureThe AI now plays all of Tier 4, driven by extended archetype profiles rather than hardcoding: it fortifies with Sentinel Turrets on the approach vector (Economist 2, Balanced 1, Rusher 0), expands to a second Command Center when home ore runs thin (banking for it via an ore reserve that pauses the unit mix instead of starving it), runs one shared production cycle across multiple Barracks (fixing the one-unit-at-a-time ceiling), and folds the Breacher into the patient mixes where the map can pay for it. A spendable-node worker filter keeps a poor-economy world's AI off dead-end commodities. Every new field is read with a use-site default, so a legacy profile still runs. Opens the picker from 3 to 9 curated worlds, each assigned an archetype and up to one data-driven rule modifier threaded symmetrically through the engine: Glacius slows all units, Nimbus/Pyralis shorten/lengthen sight and aggro, Helix adds a crystal cluster, Oort enriches deposits, Forge speeds construction. All modifie…ac7d42f -
Tier 1: camera + zoom, fog of war, and a Skiff-countering unit
Claude +1: Claude Sonnet 5Unverified signatureThree independent features, the biggest-leverage items from a brainstormed improvement list, built in dependency order (fog of war's reveal effect only means something once you can actually pan around to see it). ## Camera + zoom (camera.js, new) The whole 1600x1000 map used to be squeezed into one screen — units were tiny dots. The canvas is now a fixed-resolution viewport with a camera (pan via WASD/arrows, zoom via mouse wheel toward the cursor, clamped to map bounds) instead of a 1:1 map-to-canvas mapping. render.js draws through a camera transform; input.js's toWorld() converts screen->world through it too, so selection/commands keep working exactly as before. Fixed a latent bug while wiring this up: attachInput() was never cleaning up its event listeners, so every "Choose another battlefield" replay stacked a fresh full set on the same canvas/window — added an AbortController-based destroy(). ## Fog of war (engine/fog.js, new) A coarse grid tracks which cells are currently in…34ea8bc