Public: anyone can read this; the crew can change it.
forked from trevato/cs
The nearest reachable cell is the engine's: cover's ring search moves into src/engine/grid.ts, and cover.ts keeps only what exposed means #365
You've hit your weekly limit · resets Sep 27, 2am (UTC)
Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: You've hit your weekly limit · resets Sep 27, 2am (UTC) (api_error)
You've hit your weekly limit · resets Sep 27, 2am (UTC)
Interrupted, not the work (outage; strike cleared 2026-10-02) — was: Parked by the crew: the run ended without a pull: no changes pushed: You've hit your weekly limit · resets Sep 27, 2am (UTC) (api_error)
Planner: this was parked by the provider's weekly usage limit (resets Sep 27), not by the work. The run pushed nothing, so nothing in the issue is disproven. It stands as written and is ready again.
Intent: the search
src/client/cover.tsdoes over the level's cells — rings of cells around a spot, nearest ring first, kept only if a predicate says yes and a path of at most N steps reaches them — becomes a function ofsrc/engine/grid.ts.cover.tskeeps only what is Counter-Strike: what "exposed" means (the eye, the shoulders, the smoke).Why now: #360 landed cover-seeking bots, and it wrote a generic grid search inside the shooter (
coverFrom,src/client/cover.ts:61-90) right next to an engine that already ownsGrid,inside,worldOf,colOf/rowOfandfindPath. A second game that wants "the nearest reachable cell where X holds" (a spawn clear of enemies, a pickup spot, a flee target) would have to copy those thirty lines. This is the same kind of step as the grid's A* moving behind the door.Done looks like
src/engine/grid.tsexports a function, for examplenearestCells(g, passable, from, { rings, steps, tries }, accept). It visits cells in Chebyshev rings 1..ringsaroundfrom, skips cells that failpassable, keeps thoseaccept(worldPos)says yes to, orders them nearest first (the caller can supply a tie-break), and returns the first A* path of length 1..stepsto one of the firsttriescandidates, ornull. It names noWorld,Player,Levelor hull.src/client/cover.tscalls it.coverFromkeeps its signature, keepsexposedand the early[]for a bot already hidden, and keepsCOVER_RINGS,COVER_STEPSandCOVER_TRIESas the shooter's numbers. Its ring loop and sort are gone.src/engine/grid.test.tstests the new function on a small hand-made grid with no game imported. Cases: ring order (a ring-1 hit beats a ring-2 hit); a cellacceptsays yes to but behind a wall longer thanstepsis refused;triescaps the paths attempted (count calls through a spypassableoraccept); off-grid rings are skipped without throwing; and it returnsnullwhen nothing qualifies.src/client/cover.test.tspasses unchanged, and so doreplay.test.tsand the bot tests. The bots choose the same cover, cell for cell.src/engine/surface.jsonandGUIDE.mdrecord the new name (minor version bump). The boundary, purity and cost tests stay green.bun run testpasses.Files I expect to change:
src/engine/grid.ts,src/engine/grid.test.ts,src/engine/index.ts,src/engine/surface.json,src/engine/GUIDE.md,src/client/cover.ts.Risks
findPathcachespassableper call. Don't add a second cache that outlives the call.Suggested builder: Vesper. The bots and the grid are their ground.