Structures stand on a city's open ground, and the drawn land matches where they can stand #137

Merged
agra merged 39 commits from task/3558-build-inside-the-city-and-a-flat-map into main 2026-10-24 05:11:29 +03:00
Owner

Tracker: #3558

Players build where the ground looks buildable. A structure may stand on a city's ground, on any open tile that is not a street, a standing building's plot, or the monument's block (its tile and 8 capture cells). The server, the client's placement ghost and the AI all call one shared rule in src/shared, and that rule also names which units hold a tile: a live unit that is not an aircraft. Cities grow around what stands there. The drawn ground matches the rule: no tile that refuses a structure draws as open land, and every drawn land layer keeps off the water.

What the plan delivers

  1. Cities grow around structures. Shared growth takes the tiles that structures hold, and never lays a plot, a street run or an avenue paving on them. The server passes its standing structures, so a city keeps growing elsewhere. Growth's replay and house placement stay as they are, because every client and AI brain reads the server's layouts.
  2. One shared site rule.
    • Per tile, it gives a SiteRefusal or null: off the map, terrain, a unit, a street, a plot, the monument's block, or beyond reach. A city's open ground no longer refuses.
    • Aircraft hold no tile. The server already left them out until a reload, and now it leaves them out after one too. The ghost and the AI no longer count them.
    • Game.placeBuilding, the ghost, the AI and the towns bench all call it. clearOfCities, canPlaceFootprint and the replica's nearCity/clearOfCities are removed.
    • One shared preference ranks the sites the rule accepts: a site off a given set of cities' ground comes before one on it. placeNearAnchor and the net bench's churn case rank with every city. The AI ranks with every city too, so it keeps off every city's ground, its own, neutral or enemy, when its search ring has another site, and takes a city's open ground only when it has none.
    • The Nuke yard's setup moves into a module of its own, so that a test can run it.
  3. Trees give way.
    • A land structure may go up over Tree tiles, and placing it fells those trees. They topple and go, as a city's new tile's trees do, in Boomtown and Classic, and leave no logs.
    • The shared preference also puts a site that fells no tree before one that does, and city ground still weighs more. So placeNearAnchor, the churn case and the AI fell trees only where their search offers no tree-free site.
    • The economy and churn cases' structures and the Nuke yard's stand where they do on main at every step, and tests pin them.
  4. Drawn land lies on the land tiles.
    • The waterline runs within 0.15 of a tile inside the land tiles' edge. The land stays flat to there and drops at a narrow lip, so no beach or slope lies over a water tile. On main, the bank reaches 1.15 tiles past a contour that already sits up to 7/16 of a tile off the edge.
    • The waterline and the flat land's edge turn through a vertex at every corner where the shore turns. Where the land wraps a water tile's corner, the vertex keeps the waterline from cutting across the water. At a land tile's convex corner, as at a peninsula's tip, the vertex lies within 0.15 of both edges, so the flat land reaches the corner. Where two land tiles meet only at a corner, their outlines part.
    • The woods cap and the highway's shoulder and paving stay on the flat land too, along their segments as well as at their vertices. A shared helper holds them to the flat cap's edge, corner vertices included.
    • The shore field, the water's foam and the beach paint follow the new waterline.
    • The island's rebuild runs as units explore and as trees fall. With all this new geometry, it still costs a phone at most one frame more than on main.
  5. The monument's block draws as a paved square in every finish. Its 8 capture cells stay closed to structures, and on main they drew as plain grass.
  6. The reach draws while placing. While a structure is armed, the land beyond the player's reach (10 tiles from a held city's ground) draws dimmed.

Every refusal on main, and what becomes of it

Refused on main Plan
Off the map; land under a shipyard; Obstacle; a ground or sea unit or a structure on the tile Drawn plainly already; one shared rule from step 2
Unexplored (client and AI only) Drawn as fog; the server does not check exploration, as on main
An aircraft over the tile (client and AI always, server only after a reload) Opened: aircraft hold no tile (step 2)
Water or lake under a land structure Drawn plainly: the bank, the woods cap and the highway keep inside the land tiles (step 4)
A standing tree Opened (step 3)
A city building's plot, the monument, a city street Stay closed; drawn as themselves
The monument's 8 capture cells Stay closed; drawn as a paved square (step 5)
Any other tile of any city's ground Opened (step 2)
Beyond the reach from a held city Stays closed; drawn while placing (step 6)
  • Trees: opened, not kept closed.
  • The reach: kept and drawn while placing. It is not opened, and not drawn at all times.
  • Whose ground: every city's within reach, not only held cities'.
  • The coast: TILE_TOP stays. The land is flat to within 0.15 of a tile of the water, where a narrow lip drops to it. The land's height is not removed altogether.
  • The AI: builds off every city's ground first, its own, neutral or enemy, not only off its own. Ranking against only its own cities put a churn-128 AI seat's factory on a neutral city's ground and moved churn-128 27–30%, outside the bench bound.
  • The AI and trees: it takes a site that fells no tree when its ring offers one off every city's ground. The alternative was the first site off every city's ground, whether it fells trees or not.
  • Unpaved avenue lanes: open, and growth paves around a structure there.
  • Walling a monument: structures may ring its block.
  • Trees under a structure the player cannot see: they lie as logs, since the client has no structure to tell them by.
  • Exploration: the server does not check it, as on main. The client and the AI refuse unexplored tiles.

Acceptance walk

Walked on this branch at de104c3 (main 672fc28), on runner s32, 2026-10-24.

  • One shared site rule; a city's open ground opens. Met.
    • siteRefusal/siteOpen in src/shared/site.ts are called by Game.placeBuilding and placePreferred, by the ghost per cell and its commit (pointer.ts), by the AI's siteNear through brainSite, and by the towns bench's factorySite. clearOfCities, canPlaceFootprint and the replica's nearCity/clearOfCities no longer exist.
    • site.test.ts pins each closed case with its refusal: off the map, water under a land structure, land and a Tree tile under a shipyard, a ground unit, a structure, a street, a plot, the monument's tile, a capture cell, one tile beyond reach. It also pins one open city tile and the tile under a hovering Gunship. ground.test.ts's agreement test and game.test.ts's roundabout case pass too.
    • A probe on the Nuke yard's live game at seed 11 compared the server's view with a fully informed client's (the same knownSite the AI reads). It covered every origin, for each of the six structure types and both players, with a Gunship hovering over a city tile and a Medium spawned beside it. All 9,604 origins agree, before and after pack/unpack. Red sees 80 open city tiles. The Gunship's tile stays open after the load, and the Medium's tile refuses as Held.
  • Growth goes around structures; towns and traffic hold. Met.
    • cities.test.ts's held-2×2 and Factory cases pass.
    • Probe on seed 11 at 128²: a Factory and Turrets placed by placeBuilding on both home cities' open ground. Over 300 growth steps the cities grew 12 → 309 and 7 → 287 people, with nothing laid on the structures' 11 tiles. Without the structures, 11 of those 12 tiles got streets or plots.
    • npm run towns -- --json, head against main: 20 of its 22 rows are identical, bad, bare and stalled home towns included. In the other two, "No Factory site at founding" falls from 5 to 0 of 2,370 towns at Lakes 0.9 and from 7 to 0 at Lakes 1, because a city's open ground and its trees now take a Factory.
    • traffic.test.ts holds its bounds (15/15) in every gate run below.
  • No drawn land over a tile that refuses a land structure. Met.
    • contour.test.ts, src/mock/island.test.ts and shore.test.ts pass.
    • map.phone.ts's coast-view test passes in every finish: land points draw as land, and water points and wrapped corners draw as water or foam, beside bare land, the woods and the highway.
    • Renders with &grid of the street, woods and highway coast views at 1× and 0.5× in all four finishes, plus 1× and 0.5× against main: 40 loads with no page error.
  • Every refusal on main is listed, and each is opened or drawn plainly. Met. The table above holds the plan's 14 rows. On the head, siteRefusal answers each closed row's kind, and none for a city's open ground, a Tree tile or a tile under an aircraft. The capture cells draw paved (layer.test.ts, city-layers.test.ts), and the reach draws dimmed while placing (reach.test.ts, map.phone.ts).
  • Play checks and phones. Met.
    • The 14 seeded AI matches (small 1–6 with 2 seats, medium 1–4 with 3, large 1–4 with 4) all run to their end against main's. Six end on a winner, the rest at the time limit, each with the same end as main's. 11 traces are identical to main's; medium-1, medium-2 and large-3 differ.

    • npm run bench (seed 11, wan): battle, economy and cities at 128 and 256 are identical to main's in every hash and byte. Churn stays within 2% for every client's raw and deflated bytes a second, the largest move being churn-128 p0 deflated −1.85%. That delta comes only from the remote players skipping the tries that the shared preference ranks below their best site, tries main's server refused. A copy of the head's bench that sends each skipped try as a refused off-map placement matches main exactly in churn-128 and churn-256: frame hash, game hash, 650 and 517 commands, and every client's raw and deflated bytes.

    • The island's rebuild, in the Shore yard in Playwright WebKit (iPhone 15 landscape), 5 sessions of 7 interleaved runs per arm: main 25.47 ms (head's shore.ts over main's modules), head 20.53 ms. The paired delta is −4.93 ms, under main and inside the 2.2 ms budget.

    • The Map Yard bench by the standing method: 5 sessions of 7 interleaved runs, 10 arms (main and head at rest, plus each one's ?doom=battle, ?doom=battle&spare, ?doom=nuke and ?doom=nuke&spare), 1,050 pages with no page error. Paired medians in ms a frame, with the phone estimate at 7.5×:

      view head − main at rest head battle − spare main battle − spare head nuke − spare main nuke − spare
      1× +0.013 (+0.10) +0.083 +0.087 +0.083 +0.073
      0.5× +0.020 (+0.15) +0.097 +0.097 +0.090 +0.093
      0.25× +0.067 (+0.50) +0.120 +0.170 +0.103 +0.117

      At rest the head draws the same calls as main (67, 65 and 41) and fewer triangles (343,124 against 345,538 at 1×). Every session's at-rest delta lay within 0.03 ms of these. The plan leaves the destruction peaks' cost over their spare arms where main has it: head against main, −0.040 to +0.003 ms.

  • MAP_VERSION, changelog and help. Met. No step adds or changes a saved field: Game's state and the world are unchanged, so MAP_VERSION stays 19. changelog.md's Current has a bullet for each step, and the help's Build line and its Cities and Building sections say where a structure stands, that it fells trees, and that the reach dims. No Current bullet or help line still says structures stand off a city's ground.
  • Gates. sak-gate full on de104c3 failed in npm test six times, each only on vitest's 5000 ms timeout in the same heavy tests, while other runs and a browser tab loaded s32:
    • Gate run 1 (01:17): 894/895 in 42.8 s, the craft two-gunship landing test.
    • Gate runs 2–6 (01:19–02:54): 891–892/895 in 56.9–78.6 s, from among hud-css's text contrast, craft's two landing tests and collapses' no-scale test. Run 428's own gate ran beside gate run 4.
    • Each of those files passes alone: hud-css 4/4 in 1.7 s, craft 22/22 in 3.7 s, collapses 4/4 in 1.9 s.
    • Main's suite, run from a git archive of 672fc28 at 02:51 in the same window, failed the same way: craft's two-gunship landing test timed out, 838/839 in 48.8 s.
    • The phone suite, run by hand on de104c3 (vitest run --mode phone), passed 180/180 in 208 s, the Map Yard coast, doom, placing and rebuild checks included.
    • The gate step's job 71 passed full on this same tree (c035227) on s32 at 01:15: 895/895 in 31.0 s, then phone 180/180.
    • So the timeouts are the machine's load, not the head. The final review's own full gate on a quiet runner settles it.
    • The final review's full on de104c3 (run 439, s32): its gate run 1 (02:56) failed npm test 894/895 in 34.1 s, only on craft's "lands two gunships on their helipad in turn" at 5000 ms, a test that takes 1.50 s alone (1.52 s on main). Its gate run 2 (03:22–03:26), started on a quiet runner at a one-minute load of 1.9, passed: npm test 895/895 in 32.8 s, test:phone 180/180 in 197.5 s.
    • Run 445's full on de104c3 (gate run 1, 03:33–03:37 on s32, started at a one-minute load of 1.4) passed: npm test 895/895 in 28.4 s, test:phone 180/180 in 199.8 s.

Checks

  • Every step passes the full gate.
  • Steps 1–3 run the 14 AI matches to their end. They keep npm run bench within 5% of main in the default case and in the economy, cities and churn cases, which place structures near home towns. npm run towns and the traffic bounds hold, and step 1's npm run towns -- --json rows match main's.
  • From step 2, tests pin the economy factories, the churn outposts and the Nuke yard's six structures at main's sites.
  • An agreement test runs the server, the ghost and the AI over two neighbouring towns' grounds, explored and unexplored, and every sweep in it asserts that it tests some site.
  • Steps 3–6 change what draws. They state their cost from the Map Yard WebKit bench by the standing method, with renders per finish as evidence.
  • Step 4 also times the island's rebuild, which the Map Yard bench does not time. The rebuild runs on each terrain change, at most every half second.
    • The Shore yard (src/mock/shore.ts) times landLoops, buildSurface, the shore field and one render on a known 256² map in WebKit.
    • It runs main against the head in 5 sessions of 7 interleaved runs per arm, with the delta's phone estimate stated.
    • The head adds at most one 60 Hz frame by the phone estimate: at most 2.2 ms over main's 24 ms on s32.
    • A phone test in map.phone.ts bounds the head's rebuild at 40 ms, beside the shore field's bounds.
  • Shared and geometry tests check the waterline, the woods cap and the highway along every segment and triangle near the water. Every waterline segment lies off the water tiles and within 0.15 of one, and hand-built maps pin a wrapped water corner, a pinch and a one-tile peninsula. A Map Yard phone test and renders with the tile grid pin the shore against the tiles in every finish, including where the woods wrap a water tile's corner and at the land's convex corners.
  • No step is expected to change the saved game's structure, so MAP_VERSION stays 19 unless one does.
  • changelog.md and the in-game help record each change. Any Current bullet or help line a step makes untrue is rewritten in place.
Tracker: [#3558](https://sak.swipelab.com/tasks/3558) Players build where the ground looks buildable. A structure may stand on a city's ground, on any open tile that is not a street, a standing building's plot, or the monument's block (its tile and 8 capture cells). The server, the client's placement ghost and the AI all call one shared rule in `src/shared`, and that rule also names which units hold a tile: a live unit that is not an aircraft. Cities grow around what stands there. The drawn ground matches the rule: no tile that refuses a structure draws as open land, and every drawn land layer keeps off the water. ## What the plan delivers 1. **Cities grow around structures.** Shared growth takes the tiles that structures hold, and never lays a plot, a street run or an avenue paving on them. The server passes its standing structures, so a city keeps growing elsewhere. Growth's replay and house placement stay as they are, because every client and AI brain reads the server's layouts. 2. **One shared site rule.** - Per tile, it gives a `SiteRefusal` or null: off the map, terrain, a unit, a street, a plot, the monument's block, or beyond reach. A city's open ground no longer refuses. - Aircraft hold no tile. The server already left them out until a reload, and now it leaves them out after one too. The ghost and the AI no longer count them. - `Game.placeBuilding`, the ghost, the AI and the towns bench all call it. `clearOfCities`, `canPlaceFootprint` and the replica's `nearCity`/`clearOfCities` are removed. - One shared preference ranks the sites the rule accepts: a site off a given set of cities' ground comes before one on it. `placeNearAnchor` and the net bench's churn case rank with every city. The AI ranks with every city too, so it keeps off every city's ground, its own, neutral or enemy, when its search ring has another site, and takes a city's open ground only when it has none. - The Nuke yard's setup moves into a module of its own, so that a test can run it. 3. **Trees give way.** - A land structure may go up over Tree tiles, and placing it fells those trees. They topple and go, as a city's new tile's trees do, in Boomtown and Classic, and leave no logs. - The shared preference also puts a site that fells no tree before one that does, and city ground still weighs more. So `placeNearAnchor`, the churn case and the AI fell trees only where their search offers no tree-free site. - The economy and churn cases' structures and the Nuke yard's stand where they do on main at every step, and tests pin them. 4. **Drawn land lies on the land tiles.** - The waterline runs within 0.15 of a tile inside the land tiles' edge. The land stays flat to there and drops at a narrow lip, so no beach or slope lies over a water tile. On main, the bank reaches 1.15 tiles past a contour that already sits up to 7/16 of a tile off the edge. - The waterline and the flat land's edge turn through a vertex at every corner where the shore turns. Where the land wraps a water tile's corner, the vertex keeps the waterline from cutting across the water. At a land tile's convex corner, as at a peninsula's tip, the vertex lies within 0.15 of both edges, so the flat land reaches the corner. Where two land tiles meet only at a corner, their outlines part. - The woods cap and the highway's shoulder and paving stay on the flat land too, along their segments as well as at their vertices. A shared helper holds them to the flat cap's edge, corner vertices included. - The shore field, the water's foam and the beach paint follow the new waterline. - The island's rebuild runs as units explore and as trees fall. With all this new geometry, it still costs a phone at most one frame more than on main. 5. **The monument's block draws as a paved square** in every finish. Its 8 capture cells stay closed to structures, and on main they drew as plain grass. 6. **The reach draws while placing.** While a structure is armed, the land beyond the player's reach (10 tiles from a held city's ground) draws dimmed. ## Every refusal on main, and what becomes of it | Refused on main | Plan | |---|---| | Off the map; land under a shipyard; Obstacle; a ground or sea unit or a structure on the tile | Drawn plainly already; one shared rule from step 2 | | Unexplored (client and AI only) | Drawn as fog; the server does not check exploration, as on main | | An aircraft over the tile (client and AI always, server only after a reload) | Opened: aircraft hold no tile (step 2) | | Water or lake under a land structure | Drawn plainly: the bank, the woods cap and the highway keep inside the land tiles (step 4) | | A standing tree | Opened (step 3) | | A city building's plot, the monument, a city street | Stay closed; drawn as themselves | | The monument's 8 capture cells | Stay closed; drawn as a paved square (step 5) | | Any other tile of any city's ground | Opened (step 2) | | Beyond the reach from a held city | Stays closed; drawn while placing (step 6) | ## Decisions taken on their recommended answers (Agra may override) - **Trees:** opened, not kept closed. - **The reach:** kept and drawn while placing. It is not opened, and not drawn at all times. - **Whose ground:** every city's within reach, not only held cities'. - **The coast:** TILE_TOP stays. The land is flat to within 0.15 of a tile of the water, where a narrow lip drops to it. The land's height is not removed altogether. - **The AI:** builds off every city's ground first, its own, neutral or enemy, not only off its own. Ranking against only its own cities put a churn-128 AI seat's factory on a neutral city's ground and moved churn-128 27–30%, outside the bench bound. - **The AI and trees:** it takes a site that fells no tree when its ring offers one off every city's ground. The alternative was the first site off every city's ground, whether it fells trees or not. - **Unpaved avenue lanes:** open, and growth paves around a structure there. - **Walling a monument:** structures may ring its block. - **Trees under a structure the player cannot see:** they lie as logs, since the client has no structure to tell them by. - **Exploration:** the server does not check it, as on main. The client and the AI refuse unexplored tiles. ## Acceptance walk Walked on this branch at de104c3 (main 672fc28), on runner s32, 2026-10-24. - **One shared site rule; a city's open ground opens. Met.** - `siteRefusal`/`siteOpen` in src/shared/site.ts are called by `Game.placeBuilding` and `placePreferred`, by the ghost per cell and its commit (pointer.ts), by the AI's `siteNear` through `brainSite`, and by the towns bench's `factorySite`. `clearOfCities`, `canPlaceFootprint` and the replica's `nearCity`/`clearOfCities` no longer exist. - site.test.ts pins each closed case with its refusal: off the map, water under a land structure, land and a Tree tile under a shipyard, a ground unit, a structure, a street, a plot, the monument's tile, a capture cell, one tile beyond reach. It also pins one open city tile and the tile under a hovering Gunship. ground.test.ts's agreement test and game.test.ts's roundabout case pass too. - A probe on the Nuke yard's live game at seed 11 compared the server's view with a fully informed client's (the same `knownSite` the AI reads). It covered every origin, for each of the six structure types and both players, with a Gunship hovering over a city tile and a Medium spawned beside it. All 9,604 origins agree, before and after `pack`/`unpack`. Red sees 80 open city tiles. The Gunship's tile stays open after the load, and the Medium's tile refuses as Held. - **Growth goes around structures; towns and traffic hold. Met.** - cities.test.ts's held-2×2 and Factory cases pass. - Probe on seed 11 at 128²: a Factory and Turrets placed by `placeBuilding` on both home cities' open ground. Over 300 growth steps the cities grew 12 → 309 and 7 → 287 people, with nothing laid on the structures' 11 tiles. Without the structures, 11 of those 12 tiles got streets or plots. - `npm run towns -- --json`, head against main: 20 of its 22 rows are identical, bad, bare and stalled home towns included. In the other two, "No Factory site at founding" falls from 5 to 0 of 2,370 towns at Lakes 0.9 and from 7 to 0 at Lakes 1, because a city's open ground and its trees now take a Factory. - traffic.test.ts holds its bounds (15/15) in every gate run below. - **No drawn land over a tile that refuses a land structure. Met.** - contour.test.ts, src/mock/island.test.ts and shore.test.ts pass. - map.phone.ts's coast-view test passes in every finish: land points draw as land, and water points and wrapped corners draw as water or foam, beside bare land, the woods and the highway. - Renders with `&grid` of the street, woods and highway coast views at 1× and 0.5× in all four finishes, plus 1× and 0.5× against main: 40 loads with no page error. - **Every refusal on main is listed, and each is opened or drawn plainly. Met.** The table above holds the plan's 14 rows. On the head, `siteRefusal` answers each closed row's kind, and none for a city's open ground, a Tree tile or a tile under an aircraft. The capture cells draw paved (layer.test.ts, city-layers.test.ts), and the reach draws dimmed while placing (reach.test.ts, map.phone.ts). - **Play checks and phones. Met.** - The 14 seeded AI matches (small 1–6 with 2 seats, medium 1–4 with 3, large 1–4 with 4) all run to their end against main's. Six end on a winner, the rest at the time limit, each with the same end as main's. 11 traces are identical to main's; medium-1, medium-2 and large-3 differ. - `npm run bench` (seed 11, wan): battle, economy and cities at 128 and 256 are identical to main's in every hash and byte. Churn stays within 2% for every client's raw and deflated bytes a second, the largest move being churn-128 p0 deflated −1.85%. That delta comes only from the remote players skipping the tries that the shared preference ranks below their best site, tries main's server refused. A copy of the head's bench that sends each skipped try as a refused off-map placement matches main exactly in churn-128 and churn-256: frame hash, game hash, 650 and 517 commands, and every client's raw and deflated bytes. - The island's rebuild, in the Shore yard in Playwright WebKit (iPhone 15 landscape), 5 sessions of 7 interleaved runs per arm: main 25.47 ms (head's shore.ts over main's modules), head 20.53 ms. The paired delta is −4.93 ms, under main and inside the 2.2 ms budget. - The Map Yard bench by the standing method: 5 sessions of 7 interleaved runs, 10 arms (main and head at rest, plus each one's `?doom=battle`, `?doom=battle&spare`, `?doom=nuke` and `?doom=nuke&spare`), 1,050 pages with no page error. Paired medians in ms a frame, with the phone estimate at 7.5×: | view | head − main at rest | head battle − spare | main battle − spare | head nuke − spare | main nuke − spare | |---|---|---|---|---|---| | 1× | +0.013 (+0.10) | +0.083 | +0.087 | +0.083 | +0.073 | | 0.5× | +0.020 (+0.15) | +0.097 | +0.097 | +0.090 | +0.093 | | 0.25× | +0.067 (+0.50) | +0.120 | +0.170 | +0.103 | +0.117 | At rest the head draws the same calls as main (67, 65 and 41) and fewer triangles (343,124 against 345,538 at 1×). Every session's at-rest delta lay within 0.03 ms of these. The plan leaves the destruction peaks' cost over their spare arms where main has it: head against main, −0.040 to +0.003 ms. - **MAP_VERSION, changelog and help. Met.** No step adds or changes a saved field: `Game`'s state and the world are unchanged, so MAP_VERSION stays 19. changelog.md's Current has a bullet for each step, and the help's Build line and its Cities and Building sections say where a structure stands, that it fells trees, and that the reach dims. No Current bullet or help line still says structures stand off a city's ground. - **Gates.** `sak-gate full` on de104c3 failed in `npm test` six times, each only on vitest's 5000 ms timeout in the same heavy tests, while other runs and a browser tab loaded s32: - Gate run 1 (01:17): 894/895 in 42.8 s, the craft two-gunship landing test. - Gate runs 2–6 (01:19–02:54): 891–892/895 in 56.9–78.6 s, from among hud-css's text contrast, craft's two landing tests and collapses' no-scale test. Run 428's own gate ran beside gate run 4. - Each of those files passes alone: hud-css 4/4 in 1.7 s, craft 22/22 in 3.7 s, collapses 4/4 in 1.9 s. - Main's suite, run from a `git archive` of 672fc28 at 02:51 in the same window, failed the same way: craft's two-gunship landing test timed out, 838/839 in 48.8 s. - The phone suite, run by hand on de104c3 (`vitest run --mode phone`), passed 180/180 in 208 s, the Map Yard coast, doom, placing and rebuild checks included. - The gate step's job 71 passed `full` on this same tree (c035227) on s32 at 01:15: 895/895 in 31.0 s, then phone 180/180. - So the timeouts are the machine's load, not the head. The final review's own `full` gate on a quiet runner settles it. - The final review's `full` on de104c3 (run 439, s32): its gate run 1 (02:56) failed `npm test` 894/895 in 34.1 s, only on craft's "lands two gunships on their helipad in turn" at 5000 ms, a test that takes 1.50 s alone (1.52 s on main). Its gate run 2 (03:22–03:26), started on a quiet runner at a one-minute load of 1.9, passed: `npm test` 895/895 in 32.8 s, `test:phone` 180/180 in 197.5 s. - Run 445's `full` on de104c3 (gate run 1, 03:33–03:37 on s32, started at a one-minute load of 1.4) passed: `npm test` 895/895 in 28.4 s, `test:phone` 180/180 in 199.8 s. ## Checks - Every step passes the `full` gate. - Steps 1–3 run the 14 AI matches to their end. They keep `npm run bench` within 5% of main in the default case and in the economy, cities and churn cases, which place structures near home towns. `npm run towns` and the traffic bounds hold, and step 1's `npm run towns -- --json` rows match main's. - From step 2, tests pin the economy factories, the churn outposts and the Nuke yard's six structures at main's sites. - An agreement test runs the server, the ghost and the AI over two neighbouring towns' grounds, explored and unexplored, and every sweep in it asserts that it tests some site. - Steps 3–6 change what draws. They state their cost from the Map Yard WebKit bench by the standing method, with renders per finish as evidence. - Step 4 also times the island's rebuild, which the Map Yard bench does not time. The rebuild runs on each terrain change, at most every half second. - The Shore yard (src/mock/shore.ts) times `landLoops`, `buildSurface`, the shore field and one render on a known 256² map in WebKit. - It runs main against the head in 5 sessions of 7 interleaved runs per arm, with the delta's phone estimate stated. - The head adds at most one 60 Hz frame by the phone estimate: at most 2.2 ms over main's 24 ms on s32. - A phone test in map.phone.ts bounds the head's rebuild at 40 ms, beside the shore field's bounds. - Shared and geometry tests check the waterline, the woods cap and the highway along every segment and triangle near the water. Every waterline segment lies off the water tiles and within 0.15 of one, and hand-built maps pin a wrapped water corner, a pinch and a one-tile peninsula. A Map Yard phone test and renders with the tile grid pin the shore against the tiles in every finish, including where the woods wrap a water tile's corner and at the land's convex corners. - No step is expected to change the saved game's structure, so MAP_VERSION stays 19 unless one does. - changelog.md and the in-game help record each change. Any Current bullet or help line a step makes untrue is rewritten in place.
agra added 39 commits 2026-10-24 01:15:51 +03:00
siteRefusal answers each tile off the map, closed terrain, held by a unit,
a street, a standing building's plot, the monument's block or beyond reach.
Only live units that are not aircraft hold tiles: the server reads its
pathfinder, which Game.reseat now fills as Game.spawn does, and the client
and the AI read the units they know through heldTiles.

placeBuilding, the client's ghost and commit, the AI's siteNear and the
towns bench call it. preferredSite ranks a site off the given cities' ground
first, so placeNearAnchor, the churn outposts and the AI keep to the sites
off the ground while there are any.
agra changed title from Structures stand on a city's open ground, and the drawn land matches where they can stand to Tracker: [#3558](https://sak.swipelab.com/tasks/3558) 2026-10-24 02:55:15 +03:00
agra changed title from Tracker: [#3558](https://sak.swipelab.com/tasks/3558) to Structures stand on a city's open ground, and the drawn land matches where they can stand 2026-10-24 03:38:31 +03:00
agra merged commit d7b69a7ecb into main 2026-10-24 05:11:29 +03:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
agra/mini-alert!137
No description provided.