A structure goes up over trees and fells them #130

Owner

Tracker: #3609

Step 3 of #3558 (D1, D9, D11). A land structure may stand on Tree tiles, and placing it fells the trees under it.

What changes

  • The shared site rule (src/shared/site.ts) counts a Tree tile as open ground for a land structure. A shipyard still needs water. Tree tiles keep blocking movement. The client's ghost, the server and the AI all read this rule, so the ghost shows wooded cells open and the AI accepts the site.
  • Game.placeBuilding fells every Tree tile under the footprint through fell before spawn checks the footprint: each tile turns to Land, loses its wear and joins felled().
  • A site that fells no tree ranks first (D11). siteRank gains a second mark, and city ground weighs more than trees, so sites rank: off the given cities' ground felling no tree (0), off it felling trees (1), on it felling no tree (2), on it felling trees (3). preferredSite now takes the site view and the domain, checks a site is open before it reads its rank, and ranks against every city and tree of the view. placeNearAnchor/placePreferred, the churn case's staging and remote tries, and the AI's siteNear all rank this way.
  • Clients see those trees topple and go, as growth's clearing does, in Boomtown and Classic: fellingOf takes the frame's living units and gives Fate.Goes for a tile under a structure's footprint. It reads the units only for a tile the frame clears, since the forest and PropField.fell ask for a felling once per tile. Trees under a structure the player cannot see lie as logs (D9), since that structure is not among the drawn units.
  • Help and changelog say that a structure on land goes up over trees and fells them; the "Scouts shoot trees down" bullet now covers the trees under a new structure.

Tests

  • src/shared/site.test.ts: a Tree tile is open to a land structure and refused (Terrain) to a shipyard; preferredSite ranks the four kinds of site in order.
  • src/server/trees.test.ts: a Factory placed over four Tree tiles is accepted; the tiles turn to Land, appear in felled(), and their wear entries are gone.
  • src/server/ground.test.ts: a Factory over four explored Tree tiles is open on the server, its ghost shows four open cells, the AI's rule accepts it, and the server places it.
  • src/server/buildings.test.ts: placeNearAnchor takes a tree-free site off the city's ground at the anchor's last step over nearer wooded ones, and fells trees only when no step off the city's ground is tree-free, still keeping off the city's ground.
  • src/ai/player.test.ts: the AI places its factory at a tree-free site off its city's ground over the nearer site in its ring that fells trees.
  • src/client/render/boomtown/forest.test.ts: fellingOf gives Fate.Goes for a tile under a Factory's footprint and Fate.Lies for the same tile under a medium with a bullet's landing; the forest's and Classic's trees on such a tile go as they topple, with no log in the forest's lying index and no Kenney log at FELL_SECONDS.
  • src/server/host.test.ts: a host-fed replica holds the Factory by the frame whose map patch clears its trees.
  • Main's sites hold with Tree tiles open: net.test.ts and src/mock/strike-setup.test.ts pass unchanged. With the tree mark removed, they fail as run 158 found (economy-128 p2 → 73,22, churn-128 p4 → 91,19, churn-256 → 207,194 and 61,26, and the Nuke yard's sites move).

Play checks (base 16cad70, head bundles; seeded, run on s32)

  • 14 AI matches (small 1–6 with 2 seats, medium 1–4 with 3, large 1–4 with 4, Math.random seeded per match): every trace and summary identical to base.
  • Net bench (seed 11, wan): battle, economy and cities at 128 and 256 identical to base (hash, gameHash, commandHash). Churn moves, every client's bytes within 2%:
    • churn-128: 547 → 522 commands; p1 raw +0.01%, deflated −0.02%; p2 raw −0.45%, deflated −0.67%.
    • churn-256: 444 → 400 commands; p1 raw −0.11%, deflated −1.94%; p2 raw +0.24%, deflated +0.74%.
    • The remote players now skip tries at wooded sites that rank below the steps' best (before, those tries went out and the server refused them). A copy of head's bundle that sends each such try as a refused off-map placement matches base exactly in hash, gameHash, command count and every client's bytes. So the churn delta comes only from the dropped tries.
  • Towns bench: all 22 rows identical to base apart from timings.

Draw checks (Map Yard bench, WebKit iPhone 15 landscape, 5 sessions × 7 interleaved runs)

The head's yard bundle is byte-identical when rebuilt from the final head. Pooled medians, ms a frame, with the phone estimate (7.5×) in brackets:

view head − base at rest battle − battle&spare nuke − nuke&spare
1× +0.017 (+0.12) +0.090 (+0.68) +0.087 (+0.65)
0.5× +0.023 (+0.18) +0.033 (+0.25) +0.083 (+0.63)
0.25× −0.003 (−0.03) +0.120 (+0.90) +0.067 (+0.50)
  • At rest, head and base draw the same calls and triangles in every view.
  • A one-run check of ?doom=battle on base and head drew the same calls, and triangles within the arm's own run-to-run jitter. ?doom=battle still fells its trees, and they lie as logs, since no structure stands on them.
  • The felling only runs on frames where a tile clears. The view hands clearGround every tile cleared since the last snapshot, but the forest and PropField.fell call felling only for a stand not yet felled or a Kenney tree not yet hidden, so once per tile, on the frame it clears. The new unit scan sits inside that call.
  • The other run on this runner was busy while the bench ran (load up to 21 on 12 cores), so the medians are noisier than usual. The arms were interleaved, so the noise does not favour either side.

Gates

  • The gate step's full at 8ab27a1 (job 37, on a runner other than m5, vitest start 07:51:17) failed npm test 870/873 in 46.1 s. Its only failures were 5000 ms timeouts in hud-css.test.ts (text contrast), craft.test.ts (two gunships landing in turn) and collapses.test.ts (no part scaled below 1). Run alone on m5 a minute later, these files passed in 0.96 s, 1.54 s and 2.21 s.
  • sak-gate full run 1 passed on 8ab27a1 on m5: npm test 873/873 in 15.6 s, and test:phone 65/65 in 193 s.
  • On s32, sak-gate full run 4 passed on 8ab27a1: npm test 873/873 in 31.95 s, and test:phone 65/65. Runs 1–3 just before it failed with the same three timeouts, and the base tree failed the same way when run at that time.
Tracker: [#3609](https://sak.swipelab.com/tasks/3609) Step 3 of #3558 (D1, D9, D11). A land structure may stand on Tree tiles, and placing it fells the trees under it. ## What changes - **The shared site rule** (src/shared/site.ts) counts a Tree tile as open ground for a land structure. A shipyard still needs water. Tree tiles keep blocking movement. The client's ghost, the server and the AI all read this rule, so the ghost shows wooded cells open and the AI accepts the site. - **`Game.placeBuilding`** fells every Tree tile under the footprint through `fell` before `spawn` checks the footprint: each tile turns to Land, loses its wear and joins `felled()`. - **A site that fells no tree ranks first (D11).** `siteRank` gains a second mark, and city ground weighs more than trees, so sites rank: off the given cities' ground felling no tree (0), off it felling trees (1), on it felling no tree (2), on it felling trees (3). `preferredSite` now takes the site view and the domain, checks a site is open before it reads its rank, and ranks against every city and tree of the view. `placeNearAnchor`/`placePreferred`, the churn case's staging and remote tries, and the AI's `siteNear` all rank this way. - **Clients see those trees topple and go**, as growth's clearing does, in Boomtown and Classic: `fellingOf` takes the frame's living units and gives `Fate.Goes` for a tile under a structure's footprint. It reads the units only for a tile the frame clears, since the forest and `PropField.fell` ask for a felling once per tile. Trees under a structure the player cannot see lie as logs (D9), since that structure is not among the drawn units. - **Help and changelog** say that a structure on land goes up over trees and fells them; the "Scouts shoot trees down" bullet now covers the trees under a new structure. ## Tests - src/shared/site.test.ts: a Tree tile is open to a land structure and refused (Terrain) to a shipyard; `preferredSite` ranks the four kinds of site in order. - src/server/trees.test.ts: a Factory placed over four Tree tiles is accepted; the tiles turn to Land, appear in `felled()`, and their wear entries are gone. - src/server/ground.test.ts: a Factory over four explored Tree tiles is open on the server, its ghost shows four open cells, the AI's rule accepts it, and the server places it. - src/server/buildings.test.ts: `placeNearAnchor` takes a tree-free site off the city's ground at the anchor's last step over nearer wooded ones, and fells trees only when no step off the city's ground is tree-free, still keeping off the city's ground. - src/ai/player.test.ts: the AI places its factory at a tree-free site off its city's ground over the nearer site in its ring that fells trees. - src/client/render/boomtown/forest.test.ts: `fellingOf` gives `Fate.Goes` for a tile under a Factory's footprint and `Fate.Lies` for the same tile under a medium with a bullet's landing; the forest's and Classic's trees on such a tile go as they topple, with no log in the forest's lying index and no Kenney log at `FELL_SECONDS`. - src/server/host.test.ts: a host-fed replica holds the Factory by the frame whose map patch clears its trees. - Main's sites hold with Tree tiles open: net.test.ts and src/mock/strike-setup.test.ts pass unchanged. With the tree mark removed, they fail as run 158 found (economy-128 p2 → 73,22, churn-128 p4 → 91,19, churn-256 → 207,194 and 61,26, and the Nuke yard's sites move). ## Play checks (base 16cad70, head bundles; seeded, run on s32) - **14 AI matches** (small 1–6 with 2 seats, medium 1–4 with 3, large 1–4 with 4, `Math.random` seeded per match): every trace and summary identical to base. - **Net bench** (seed 11, wan): battle, economy and cities at 128 and 256 identical to base (hash, gameHash, commandHash). Churn moves, every client's bytes within 2%: - churn-128: 547 → 522 commands; p1 raw +0.01%, deflated −0.02%; p2 raw −0.45%, deflated −0.67%. - churn-256: 444 → 400 commands; p1 raw −0.11%, deflated −1.94%; p2 raw +0.24%, deflated +0.74%. - The remote players now skip tries at wooded sites that rank below the steps' best (before, those tries went out and the server refused them). A copy of head's bundle that sends each such try as a refused off-map placement matches base exactly in hash, gameHash, command count and every client's bytes. So the churn delta comes only from the dropped tries. - **Towns bench**: all 22 rows identical to base apart from timings. ## Draw checks (Map Yard bench, WebKit iPhone 15 landscape, 5 sessions × 7 interleaved runs) The head's yard bundle is byte-identical when rebuilt from the final head. Pooled medians, ms a frame, with the phone estimate (7.5×) in brackets: | view | head − base at rest | battle − battle&spare | nuke − nuke&spare | |---|---|---|---| | 1× | +0.017 (+0.12) | +0.090 (+0.68) | +0.087 (+0.65) | | 0.5× | +0.023 (+0.18) | +0.033 (+0.25) | +0.083 (+0.63) | | 0.25× | −0.003 (−0.03) | +0.120 (+0.90) | +0.067 (+0.50) | - At rest, head and base draw the same calls and triangles in every view. - A one-run check of `?doom=battle` on base and head drew the same calls, and triangles within the arm's own run-to-run jitter. `?doom=battle` still fells its trees, and they lie as logs, since no structure stands on them. - The felling only runs on frames where a tile clears. The view hands `clearGround` every tile cleared since the last snapshot, but the forest and `PropField.fell` call `felling` only for a stand not yet felled or a Kenney tree not yet hidden, so once per tile, on the frame it clears. The new unit scan sits inside that call. - The other run on this runner was busy while the bench ran (load up to 21 on 12 cores), so the medians are noisier than usual. The arms were interleaved, so the noise does not favour either side. ## Gates - The gate step's `full` at 8ab27a1 (job 37, on a runner other than m5, vitest start 07:51:17) failed `npm test` 870/873 in 46.1 s. Its only failures were 5000 ms timeouts in hud-css.test.ts (text contrast), craft.test.ts (two gunships landing in turn) and collapses.test.ts (no part scaled below 1). Run alone on m5 a minute later, these files passed in 0.96 s, 1.54 s and 2.21 s. - `sak-gate full` run 1 passed on 8ab27a1 on m5: `npm test` 873/873 in 15.6 s, and `test:phone` 65/65 in 193 s. - On s32, `sak-gate full` run 4 passed on 8ab27a1: `npm test` 873/873 in 31.95 s, and `test:phone` 65/65. Runs 1–3 just before it failed with the same three timeouts, and the base tree failed the same way when run at that time.
agra merged commit ea3d2035cc into task/3558-build-inside-the-city-and-a-flat-map 2026-10-22 08:04:20 +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!130
No description provided.