Bridges: let cats and roads cross rivers #3

Open
opened 2026-07-05 09:13:30 +00:00 by beasty · 0 comments
Owner

Context

Rivers are hard blockers in the walk grid (lib/game/pathfinding.ts: water is impassable), so cats path around them and roads cannot cross. On seeds where a river separates the village from good hunting/quarry grounds, routes get long and the leader's economics degrade. docs/ROADMAP.md lists bridges as an open item.

Design direction (implementer owns details)

  • Bridge as a buildable on a water tile (new buildings.type: 'bridge'), commissioned by the leader when route analysis shows a high-value crossing — e.g. when the wear-weighted detour around a river segment exceeds a threshold versus the straight corridor (the road director in lib/game/roads.ts already computes cumulative-wear corridors; a bridge is conceptually "a road tile that beats water").
  • Construction like other buildings: a build job, real material cost (materials + refined feels right for era-gating — refined comes from workshops), scaffold + progress rendering.
  • Pathfinding: a completed bridge makes its water tile walkable at road cost (ROAD_COST) — one clean hook in the WalkGrid cost/blocked functions. The per-cat route cache (server/game.ts) must be invalidated when a bridge completes (same two-layer pattern as fence changes: cache clear on completion + per-edge revalidation already exists — verify it covers "tile became walkable", not just "tile became blocked", since revalidation only rejects newly-blocked edges; a new bridge should also trigger re-planning to use it).
  • Placement rules: only on river tiles, orthogonally adjacent to walkable land on both opposing sides (no bridges to nowhere, no diagonal stitching). Rivers are 1-tile wide from the generator (lib/game/terrainGen.ts monotonic-descent rivers) so single-tile spans are the common case; multi-tile spans can be explicitly out of scope for v1.
  • Render: a plank/road sprite over the water diamond is acceptable if the Kenney Miniature pack has no dedicated bridge art — check the source pack first (public/Kenney Game Assets All-in-1 3.5.0/, gitignored; copy what's needed into public/images/iso/). Add a fitting case to /dev/fit.

Tests (per the CLAUDE.md contract — deterministic, seeded)

  • Placement validation: rejects non-water tiles, rejects spans without both banks, accepts a valid crossing.
  • Pathfinding: with a completed bridge, findPath prefers the crossing when cheaper; incomplete bridge still blocks; route cache re-plans after completion (regression test mirroring the fence-invalidation test in tests/integration/serverVillageArea.test.ts).
  • Leader: commissions a bridge only when the detour-vs-crossing economics justify it; boundary test on the threshold constant.
  • Integration on in-memory SQLite: build job lifecycle → walkable tile → a hunt route that crosses.

Acceptance criteria

  • A colony on a river-split seed builds a bridge unaided and traffic visibly funnels over it (in-browser verification, screenshots).
  • No new tick paths; everything inside workerTick's phase order. Full suite + typecheck green.
## Context Rivers are hard blockers in the walk grid (`lib/game/pathfinding.ts`: water is impassable), so cats path around them and roads cannot cross. On seeds where a river separates the village from good hunting/quarry grounds, routes get long and the leader's economics degrade. `docs/ROADMAP.md` lists bridges as an open item. ## Design direction (implementer owns details) - **Bridge as a buildable** on a water tile (new `buildings.type: 'bridge'`), commissioned by the leader when route analysis shows a high-value crossing — e.g. when the wear-weighted detour around a river segment exceeds a threshold versus the straight corridor (the road director in `lib/game/roads.ts` already computes cumulative-wear corridors; a bridge is conceptually "a road tile that beats water"). - Construction like other buildings: a `build` job, real material cost (materials + refined feels right for era-gating — refined comes from workshops), scaffold + progress rendering. - **Pathfinding**: a completed bridge makes its water tile walkable at road cost (`ROAD_COST`) — one clean hook in the WalkGrid cost/blocked functions. The per-cat route cache (`server/game.ts`) must be invalidated when a bridge completes (same two-layer pattern as fence changes: cache clear on completion + per-edge revalidation already exists — verify it covers "tile became walkable", not just "tile became blocked", since revalidation only rejects newly-blocked edges; a new bridge should also trigger re-planning to *use* it). - **Placement rules**: only on river tiles, orthogonally adjacent to walkable land on both opposing sides (no bridges to nowhere, no diagonal stitching). Rivers are 1-tile wide from the generator (`lib/game/terrainGen.ts` monotonic-descent rivers) so single-tile spans are the common case; multi-tile spans can be explicitly out of scope for v1. - **Render**: a plank/road sprite over the water diamond is acceptable if the Kenney Miniature pack has no dedicated bridge art — check the source pack first (`public/Kenney Game Assets All-in-1 3.5.0/`, gitignored; copy what's needed into `public/images/iso/`). Add a fitting case to `/dev/fit`. ## Tests (per the CLAUDE.md contract — deterministic, seeded) - Placement validation: rejects non-water tiles, rejects spans without both banks, accepts a valid crossing. - Pathfinding: with a completed bridge, `findPath` prefers the crossing when cheaper; incomplete bridge still blocks; route cache re-plans after completion (regression test mirroring the fence-invalidation test in `tests/integration/serverVillageArea.test.ts`). - Leader: commissions a bridge only when the detour-vs-crossing economics justify it; boundary test on the threshold constant. - Integration on in-memory SQLite: build job lifecycle → walkable tile → a hunt route that crosses. ## Acceptance criteria - [ ] A colony on a river-split seed builds a bridge unaided and traffic visibly funnels over it (in-browser verification, screenshots). - [ ] No new tick paths; everything inside `workerTick`'s phase order. Full suite + typecheck green.
Sign in to join this conversation.
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
beasty/cat_idler#3
No description provided.