v0.4.8 — the Limits bound the whole district, the nine spots really are nine, and an action points at its square

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YQAJ4dND7enLj54eLyiF2C
This commit is contained in:
Jesse
2026-08-19 13:38:06 -04:00
co-authored by Claude Opus 5
parent 9f3b92d08e
commit 461c4d9bac
25 changed files with 3264 additions and 4524 deletions
+20
View File
@@ -77,6 +77,7 @@ import {
opposite,
reachableDestinations,
variantsFor,
withinLimits,
} from './track.ts';
export type ApplyResult =
@@ -1105,6 +1106,9 @@ function checkPlay(
if (placement.row === area.runningRow && !carriesThroughTrack(proto)) {
return 'BREAKS_RUNNING_TRACK';
}
// Same reasoning: a siding reaching past your own sign JOINS perfectly well, and is refused
// because it leaves your territory (§2.1). `canPlaceAt` enforces it too — this only names it.
if (!withinLimits(area, placement)) return 'OUTSIDE_LIMITS';
return canPlaceAt(area, placement, proto) ? null : 'NOT_CONNECTED';
}
@@ -1123,6 +1127,10 @@ function checkPlay(
* main line.
*/
if (placement.row === area.runningRow) return 'ON_RUNNING_TRACK';
// A Facility CARRIES TRACK — "placing a Facility places track" (§11.2) — so it is bounded by
// the Limits exactly as a siding is. An industry outside them is how a district used to leave
// its own territory.
if (!withinLimits(area, placement)) return 'OUTSIDE_LIMITS';
// Q4 — a lockout prevents BUILDING both in one district: no duplicate, and never a producer
// alongside the consumer of the same commodity.
if (isLockedOut(area, card.kind.facility)) return 'FACILITY_LOCKED';
@@ -1133,6 +1141,18 @@ function checkPlay(
case 'modifier': {
if (!placement) return 'NO_PLACEMENT';
if (area.grid.has(coordKey(placement))) return 'NOT_CONNECTED';
/**
* NOT ON THE RUNNING TRACK ROW — AND NOT BOUNDED BY THE LIMITS EITHER. Jesse's call, both
* halves.
*
* A Modifier is not track (§9), so unlike a siding it may hang outside the Limits: a Facility
* standing at the limit has three of its nine spots out there, and refusing them would make
* the card unplayable exactly where the district ends. What it may NOT do is stand in the row
* the Running Track grows along. Inside the Limits that row is always full, so this bites only
* beyond the sign — which is the ground the main extends onto, and a Modifier parked there
* would block your own sign from moving outward (§2.1) with nothing on screen to warn you.
*/
if (placement.row === area.runningRow) return 'ON_RUNNING_TRACK';
// One of a kind per Office Area, as with industries (Q4) — no district gets two Ice Houses.
if (hasModifierInArea(area, card.kind.modifier)) return 'FACILITY_LOCKED';
// §9 — a Modifier is not track. It must sit adjacent to a Facility THAT CAN HOST IT (one of