AI
astar · choose · distance · flee · floodfill · maze · nearest · neighbors · raycast · seek · wander · weighted
astar
A* pathfinding on a 2D array. Returns an array of coordinate waypoints from start to end. Grid values of 0 are walkable, non-zero are blocked. Supports 8-directional movement with proper diagonal costs. Returns an empty array if no path exists. Its grid is indexed grid(y, x), so a MAZE result can be searched as it comes.
astar(grid, start, finish)
Parameters
- grid - 2D array (rows of rows) where 0 = walkable
- start - Starting coordinate (x, y)
- finish - Goal coordinate (x, y)
Examples
SCREEN 320, 240
// A wall at x = 3, open only on the bottom row.
grid = [[0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 1, 0, 0], [0, 0, 0, 0, 0, 0]]
path = astar(grid, (0, 0), (5, 0))
PRINT "from", path(0), "to", path(LEN(path) - 1), "in", LEN(path), "steps"
SCREEN 320, 240
// A perfect maze connects every open cell.
grid = maze(11, 9, 7)
PRINT LEN(astar(grid, (1, 1), (9, 7))) > 0
choose
Returns a random element from an array. Returns 0 if the array is empty.
choose(array)
Parameters
- array - Array to pick from
Example
SCREEN 320, 240
loot = ["sword", "shield", "potion"]
PRINT "you found a", choose(loot)
PRINT choose([])
distance
Returns the Euclidean distance between two coordinates.
distance(a, b)
Parameters
- a - First coordinate (x, y)
- b - Second coordinate (x, y)
Example
SCREEN 320, 240
PRINT distance((0, 0), (3, 4))
flee
Returns a velocity coordinate pointing away from the threat at the given speed. Opposite of SEEK. Returns (0, 0) if positions are identical.
flee(pos, threat, speed)
Parameters
- pos - Current position coordinate (x, y)
- threat - Position to flee from (x, y)
- speed - Movement speed (magnitude of result)
Example
SCREEN 320, 240
enemy = (100, 100)
player = (103, 104)
// A velocity away from the player, 10 pixels long.
v = flee(enemy, player, 10)
PRINT v.x, v.y
floodfill
Flood fills a 2D grid from the starting position, counting all connected cells that match the given value. Uses 4-directional connectivity. Returns the number of filled cells. Useful for checking area reachability. The fill reads and marks grid(y, x), the cell at coordinate (x, y), on a grid shaped like MAZE’s.
floodfill(grid, start, value)
Parameters
- grid - 2D array to flood fill
- start - Starting coordinate (x, y)
- value - Cell value to match and fill (e.g., 0 for walkable cells)
Example
SCREEN 320, 240
// A full wall down column x = 2.
grid = [[0, 0, 1, 0, 0], [0, 0, 1, 0, 0], [0, 0, 1, 0, 0]]
PRINT floodfill(grid, (0, 0), 0), "cells reachable"
maze
Procedurally generates a maze as a fresh 2D array where 1 = wall and 0 = floor. The array is exactly width x height, indexed maze(x, y) like every array: maze(x, y) is the cell at coordinate (x, y). Serialized, it is a list of rows ([[row 0], [row 1], ...]), as other JSON readers expect. The output drops straight into ASTAR, FLOODFILL, RAYCAST and NEIGHBORS, which take (x, y) grids too, so you can generate a maze and pathfind across it in two lines, or read it back to build a tilemap. The outer border is always wall; even width/height values leave an extra wall strip on the far edge. width/height are clamped up to a minimum of 3. The trailing arguments are optional and matched by type, so MAZE(w, h), MAZE(w, h, “CAVE”), MAZE(w, h, 42) and MAZE(w, h, 42, “PRIM”, 0.3) all work; when two numbers are supplied the first is the seed and the second is braid.
maze(width, height, seed = NULL, style = NULL, braid = NULL)
Parameters
- width - Number of columns in the returned array (minimum 3)
- height - Number of rows in the returned array (minimum 3)
- seed = NULL - Optional. 0 or omitted = a different maze each call; any nonzero value = reproducible
- style = NULL - Optional. “BACKTRACKER” (winding corridors, default), “PRIM” (bushier, many short dead-ends), “DIVISION” (straight walls, room-like), or “CAVE” (organic cellular caverns)
- braid = NULL - Optional, 0.0 to 1.0. 0.0 = perfect maze with exactly one path between any two cells (default); higher values remove that fraction of dead-ends, adding loops. Ignored for the CAVE style
Examples
SCREEN 320, 240
grid = maze(7, 5, 42)
FOR y = 0 ... 4
row = ""
FOR x = 0 ... 6
IF grid(y, x) = 1 THEN row = row + "#" ELSE row = row + "."
END FOR
PRINT row
END FOR
SCREEN 320, 240
grid = maze(21, 15, 7, "PRIM", 0.3)
PRINT LEN(grid), LEN(grid(0))
PRINT "the corner is wall:", grid(0, 0)
nearest
Returns the index (0-based) of the nearest coordinate in the array to the given position. Returns -1 if the array is empty.
nearest(pos, points)
Parameters
- pos - Reference coordinate (x, y)
- points - Array of coordinates to search
Example
SCREEN 320, 240
pickups = [(50, 50), (5, 5), (90, 0)]
i = nearest((0, 0), pickups)
PRINT i, pickups(i)
PRINT nearest((0, 0), [])
neighbors
Returns an array of coordinate positions for valid non-blocked neighbors of a position on a 2D grid. Uses 4-directional adjacency (up, right, down, left). Only includes cells where the grid value is 0. The grid is read as grid(y, x); each neighbour returned is an (x, y) coordinate into it, as for a MAZE grid.
neighbors(grid, pos)
Parameters
- grid - 2D array where 0 = walkable, non-zero = blocked
- pos - Position to check neighbors of (x, y)
Example
SCREEN 320, 240
// Block the cell above the centre, (1, 0).
grid = [[0, 1, 0], [0, 0, 0], [0, 0, 0]]
PRINT neighbors(grid, (1, 1))
raycast
Line-of-sight check on a 2D grid using Bresenham’s line algorithm. Returns 1 if the line from start to end is clear (no blocked cells), 0 if blocked. Start and end positions are not checked (only cells between them). Cells are looked up as grid(y, x) along the line, the same orientation MAZE returns.
raycast(grid, start, finish)
Parameters
- grid - 2D array where 0 = clear, non-zero = blocked
- start - Starting coordinate (x, y)
- finish - Target coordinate (x, y)
Example
SCREEN 320, 240
grid = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
PRINT raycast(grid, (0, 2), (4, 2))
// A pillar in the way, at (2, 2).
grid(2, 2) = 1
PRINT raycast(grid, (0, 2), (4, 2))
seek
Returns a velocity coordinate pointing from pos toward target at the given speed. Useful for basic “move toward” AI behavior. Returns (0, 0) if already at target.
seek(pos, target, speed)
Parameters
- pos - Current position coordinate (x, y)
- target - Target position coordinate (x, y)
- speed - Movement speed (magnitude of result)
Example
SCREEN 320, 240
enemy = (0, 0)
player = (30, 40)
// A velocity from the enemy toward the player, 5 pixels long.
v = seek(enemy, player, 5)
PRINT v.x, v.y
wander
Returns a new position for gentle random wandering (Craig Reynolds style). Projects a circle at ‘distance’ ahead in the direction of ‘angle’ (degrees), then picks a random point on that circle. The angle is jittered randomly each call for natural movement.
wander(pos, angle, radius, distance)
Parameters
- pos - Current position coordinate (x, y)
- angle - Current heading in degrees
- radius - Wander circle radius (larger = more erratic)
- distance - How far ahead the wander circle is projected
Example
SCREEN 320, 240
heading = 0
pos = (100, 100)
FOR i = 1 ... 3
pos = wander(pos, heading, 5, 10)
END FOR
PRINT "wandered to", pos
weighted
Returns a random element from the values array, weighted by the corresponding weights array. Higher weights mean higher probability of selection. Weights should be non-negative numbers.
weighted(values, weights)
Parameters
- values - Array of items to choose from
- weights - Array of numeric weights (same length as values)
Example
SCREEN 320, 240
// "common" nine times in ten.
PRINT weighted(["common", "rare"], [9, 1])
PRINT weighted(["never", "always"], [0, 1])