Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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])