aboutsummaryrefslogtreecommitdiff
path: root/internal/validate
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-29 18:13:33 -0400
committerhistoria <[not public]>2026-06-29 18:13:33 -0400
commit7d76673fa0707d204c7d084c8f90c8133c22a31a (patch)
tree6fc832b9f3fde6ed455abb535f6e28b0e5db1fd9 /internal/validate
parentfeb80b7dde200d4121cd9f9c583a08f9869c5588 (diff)
downloadthehouseoficarus-7d76673fa0707d204c7d084c8f90c8133c22a31a.tar.gz
feat: migrated map (and bfs) to full 3D instead of individual 2D maps on different planes.
Diffstat (limited to 'internal/validate')
-rw-r--r--internal/validate/checks.go102
-rw-r--r--internal/validate/grid_test.go61
2 files changed, 93 insertions, 70 deletions
diff --git a/internal/validate/checks.go b/internal/validate/checks.go
index a41ad86..d835a53 100644
--- a/internal/validate/checks.go
+++ b/internal/validate/checks.go
@@ -870,20 +870,29 @@ func validateRoomWiring(s Source) []Issue {
}
-// validateRoomGrid lays each reachable horizontal plane on a 2D grid starting
-// from the configured root rooms and reports when the exit layout cannot be
-// embedded without conflict:
+// validateRoomGrid embeds the entire reachable world on a single 3D grid
+// starting from the configured root rooms and reports when the exit layout
+// cannot be embedded without conflict:
// - overlap: two distinct rooms land on the same grid cell.
// - twist: one room is forced onto two different grid cells.
//
-// Up/Down exits don't move on the grid; each leads to a new plane that is laid
-// out independently (fresh origin), so one root validates every reachable
-// floor. Exit conditions are ignored (geometry is independent of gating), and
+// All 10 exits (including Up/Down) participate in coordinate assignment:
+// horizontal exits shift (x,y); Up/Down shift z at the same (x,y).
+// Exit conditions are ignored (geometry is independent of gating), and
// exits to nonexistent rooms are skipped (covered by referential checks).
func validateRoomGrid(s Source) []Issue {
var issues []Issue
roomIndex := s.World.RoomIndex()
+ load := func(id int) (*world.Room, bool) {
+ r, err := s.World.LoadRoom(id)
+ if err != nil {
+ return nil, false
+ }
+ return r, true
+ }
+ include := func(id int) bool { return roomIndex[id] }
+
placed := make(map[int]bool)
var seeds []int
for _, r := range s.RootRooms {
@@ -892,71 +901,34 @@ func validateRoomGrid(s Source) []Issue {
}
}
- for len(seeds) > 0 {
- origin := seeds[0]
- seeds = seeds[1:]
+ for _, origin := range seeds {
if placed[origin] {
continue
}
- coordOf := map[int][2]int{origin: {0, 0}}
- roomAt := map[[2]int]int{{0, 0}: origin}
- placed[origin] = true
- queue := []int{origin}
-
- for len(queue) > 0 {
- rid := queue[0]
- queue = queue[1:]
- room, err := s.World.LoadRoom(rid)
- if err != nil {
- continue
+ grid := world.BuildGrid(origin, load, include, func(c world.GridConflict) {
+ switch c.Kind {
+ case "twist":
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "integrity",
+ Message: fmt.Sprintf(
+ "Grid twist: room %d (via %d %s) maps to (%d,%d,%d) but was already placed at (%d,%d,%d) [origin %d]",
+ c.Target, c.From, c.Dir, c.Want[0], c.Want[1], c.Want[2], c.Existing[0], c.Existing[1], c.Existing[2], origin),
+ })
+ case "overlap":
+ issues = append(issues, Issue{
+ Level: "ERROR",
+ Type: "integrity",
+ Message: fmt.Sprintf(
+ "Grid overlap: room %d (via %d %s) wants (%d,%d,%d), already used by room %d [origin %d]",
+ c.Target, c.From, c.Dir, c.Want[0], c.Want[1], c.Want[2], c.Occupier, origin),
+ })
}
- c := coordOf[rid]
- for _, dir := range world.ExitOrder {
- exit, ok := room.Exits[dir]
- if !ok || exit.Room <= 0 || !roomIndex[exit.Room] {
- continue
- }
- target := exit.Room
-
- if dir == world.Up || dir == world.Down {
- if !placed[target] {
- seeds = append(seeds, target)
- }
- continue
- }
-
- d := world.DirectionDeltas[dir]
- want := [2]int{c[0] + d[0], c[1] + d[1]}
-
- if existing, ok := coordOf[target]; ok {
- if existing != want {
- issues = append(issues, Issue{
- Level: "ERROR",
- Type: "integrity",
- Message: fmt.Sprintf(
- "Grid twist: room %d (via %d %s) maps to grid (%d,%d) but was already placed at (%d,%d) [plane origin %d]",
- target, rid, dir, want[0], want[1], existing[0], existing[1], origin),
- })
- }
- continue
- }
- if occupier, ok := roomAt[want]; ok && occupier != target {
- issues = append(issues, Issue{
- Level: "ERROR",
- Type: "integrity",
- Message: fmt.Sprintf(
- "Grid overlap: room %d (via %d %s) wants grid (%d,%d), already used by room %d [plane origin %d]",
- target, rid, dir, want[0], want[1], occupier, origin),
- })
- continue
- }
+ })
- coordOf[target] = want
- roomAt[want] = target
- placed[target] = true
- queue = append(queue, target)
- }
+ for rid := range grid.Coord {
+ placed[rid] = true
}
}
diff --git a/internal/validate/grid_test.go b/internal/validate/grid_test.go
index b3f834a..716cec1 100644
--- a/internal/validate/grid_test.go
+++ b/internal/validate/grid_test.go
@@ -87,9 +87,8 @@ func TestGridTwist(t *testing.T) {
}
func TestGridMultiPlaneViaUpDown(t *testing.T) {
- // Plane A is just room 1. Going up seeds plane B (origin 10), which has an
- // overlap. This proves up/down crosses planes and frames are independent
- // (room 1 and room 10 both sit at (0,0) without conflicting).
+ // Room 1 at (0,0,0) goes up to room 10 at (0,0,1). Rooms 13 and 14 both
+ // resolve to (1,1,1) — 3D overlap on the upper level.
rooms := map[int]string{
1: "exits:\n up: 10\n",
10: "exits:\n south: 12\n east: 11\n",
@@ -99,8 +98,60 @@ func TestGridMultiPlaneViaUpDown(t *testing.T) {
14: "name: fourteen\n",
}
issues := runGridCheck(t, rooms, 1)
- if !containsMsg(issues, "plane origin 10") {
- t.Errorf("expected an overlap in plane origin 10, got: %+v", issues)
+ if !containsMsg(issues, "Grid overlap") {
+ t.Errorf("expected a 3D grid overlap, got: %+v", issues)
+ }
+}
+
+func TestGrid3DCleanViaUpDown(t *testing.T) {
+ // Clean 3D layout: up/down maintain same (x,y) across z-levels.
+ // 1(0,0,0) up→2(0,0,1) east→3(1,0,1) down→4(1,0,0) south→5(1,1,0)
+ rooms := map[int]string{
+ 1: "exits:\n up: 2\n",
+ 2: "exits:\n east: 3\n down: 1\n",
+ 3: "exits:\n down: 4\n west: 2\n",
+ 4: "exits:\n south: 5\n up: 3\n",
+ 5: "name: five\n exits:\n north: 4\n",
+ }
+ if issues := runGridCheck(t, rooms, 1); len(issues) != 0 {
+ t.Errorf("expected no grid issues, got: %+v", issues)
+ }
+}
+
+func TestGrid3DOverlapUpDown(t *testing.T) {
+ // Two rooms land on the same 3D cell via up/down paths.
+ // 1(0,0,0) up→2(0,0,1).
+ // 1(0,0,0) east→3(1,0,0) up→4(1,0,1) west→5 wants (0,0,1) — already room 2.
+ rooms := map[int]string{
+ 1: "exits:\n up: 2\n east: 3\n",
+ 2: "name: two\n",
+ 3: "exits:\n up: 4\n west: 1\n",
+ 4: "exits:\n west: 5\n down: 3\n",
+ 5: "name: five\n",
+ }
+ issues := runGridCheck(t, rooms, 1)
+ if !containsMsg(issues, "Grid overlap") {
+ t.Errorf("expected a 3D grid overlap, got: %+v", issues)
+ }
+}
+
+func TestGrid3DTwistUpDown(t *testing.T) {
+ // Room 4 forced to two different coordinates via up/down paths.
+ // path1: 1 up→2(0,0,1) south→3(0,1,1) east→4 places 4 at (1,1,1)
+ // path2: 1 east→A(1,0,0) up→B(1,0,1) east→C(2,0,1) south→4 wants (2,1,1)
+ // 4 already at (1,1,1) from path1 → twist.
+ rooms := map[int]string{
+ 1: "exits:\n up: 2\n east: 6\n",
+ 2: "exits:\n south: 3\n down: 1\n",
+ 3: "exits:\n east: 4\n north: 2\n",
+ 4: "name: four\n",
+ 6: "exits:\n up: 7\n west: 1\n",
+ 7: "exits:\n east: 8\n down: 6\n",
+ 8: "exits:\n south: 4\n west: 7\n",
+ }
+ issues := runGridCheck(t, rooms, 1)
+ if !containsMsg(issues, "Grid twist") {
+ t.Errorf("expected a 3D grid twist, got: %+v", issues)
}
}