# grid.ludic — Grid.* tile geometry and pathfinding over the Map tilemap. A # tiny map with a wall maze; each assertion that holds prints its number, so a # full run prints: 1 2 3 4 5 6 7 8 9 10 11 12 13 # Pathfinding is A* (runtime/native/grid.ludic), 4-connected, `'#'` = wall. program Grid { property Tag { v: int = 0 } model Marker { Tag } handler Boot phase Start { # The maze is now a loaded Tiled map (issue #69): its `collision` layer — # 1 = wall, 0 = open — projects to the same byte tilemap Map.row used to fill, # so every Grid.* / Path.* query below is byte-identical. # 01234567 # row 0 ######## # row 1 #......# # row 2 #.####.# # row 3 #....#.# # row 4 ######## Tiled.load("assets/tiled-fixtures/grid_maze.tmx") # --- Bresenham line --- let hl = Grid.line(1, 1, 6, 1) if len(hl) == 6 { print(1) } let dl = Grid.line(0, 0, 3, 3) if len(dl) == 4 { print(2) } # --- line of sight (a clear corridor vs one crossing a wall) --- if Grid.line_of_sight(1, 1, 6, 1, '#') { print(3) } if not Grid.line_of_sight(1, 1, 4, 3, '#') { print(4) } # --- flood fill: every floor cell reachable from (1,1) --- if len(Grid.flood(1, 1, '#')) == 13 { print(5) } # --- A* shortest path around the maze --- let p = Grid.a_star(1, 1, 6, 3, '#') if len(p) == 8 { print(6) } if p[0].x == 1 and p[0].y == 1 { print(7) } let last = p[len(p) - 1] if last.x == 6 and last.y == 3 { print(8) } # the path is contiguous (each step 4-adjacent) and never crosses a wall var ok = true var i = 1 while i < len(p) { let dx = p[i].x - p[i - 1].x let dy = p[i].y - p[i - 1].y var ad = dx if ad < 0 { ad = -ad } var ay = dy if ay < 0 { ay = -ay } if ad + ay != 1 { ok = false } if Grid.blocked(p[i].x, p[i].y, '#') { ok = false } i += 1 } if ok { print(9) } # --- unreachable / blocked cases --- if len(Grid.a_star(1, 1, 3, 2, '#')) == 0 { print(10) } # (3,2) is a wall if Grid.blocked(3, 2, '#') { print(11) } if not Grid.blocked(1, 1, '#') { print(12) } if Grid.blocked(-1, 0, '#') { print(13) } # out of bounds } handler Run phase Update { quit() } }