aboutsummaryrefslogtreecommitdiff
path: root/internal/validate/checks.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/validate/checks.go')
-rw-r--r--internal/validate/checks.go102
1 files changed, 37 insertions, 65 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
}
}