Grid:get
methodGrid:get(x: integer, y: integer) -> integer?Cell value at (x, y), or nil when out of bounds.
handle13 methods, 2 fields
A 2D integer grid with pathfinding, FOV, flood-fill, cellular automata, and procedural fills. Cells are 0-based, addressed as (x, y) = (column, row) with a top-left origin and Y increasing downward — the vertical mirror of TileMap's bottom-left Y-up frame, so copying cells into a tilemap flips the row: ty = rows - 1 - gy.
datumhue.grid.newPathAgent:set_target, PathAgent:advance, PathAgent:drive, Voxel:heightmap, Voxel:stamp_gridThe examples on this page start from:
-- An 8 x 6 room. A wall (1) runs down column 3 from the top row,
-- leaving the bottom row open.
local grid = datumhue.grid.new(8, 6)
grid:fill_rect(3, 0, 1, 5, 1)| Field | Type | Access | Description |
|---|---|---|---|
width | integer | read-only | Grid width in cells. |
height | integer | read-only | Grid height in cells. |
Grid:get(x: integer, y: integer) -> integer?Cell value at (x, y), or nil when out of bounds.
Grid:set(x: integer, y: integer, value: integer)Set cell (x, y) to value; no-op when out of bounds.
Grid:fill(value: integer)Set every cell to value.
Grid:fill_rect(x: integer, y: integer, w: integer, h: integer, value: integer)Fill a rectangle with value; no-op when w or h is non-positive.
-- (x, y) is the rectangle's top-left cell; y grows downward.
grid:fill_rect(5, 3, 2, 2, 7)
print(grid:get(5, 3), grid:get(6, 4)) --> 7 7
print(grid:get(5, 2), grid:get(7, 5)) --> 0 0Grid:find_path(x1: integer, y1: integer, x2: integer, y2: integer, opts: grid.FindPathOptions) -> grid.Cell[]?A* path from (x1,y1) to (x2,y2), or nil if unreachable.
local path = grid:find_path(0, 0, 7, 0, { blocked = { 1 } })
-- Both ends are included; it goes round the wall's open end.
print(#path, path[1].x, path[#path].x) --> 11 0 7
print(path[6].x, path[6].y) --> 3 5
-- Close the gap and nothing gets through.
grid:set(3, 5, 1)
print(grid:find_path(0, 0, 7, 0, { blocked = { 1 } })) --> nilopts grid.FindPathOptions options 1 key
| Key | Type | Default | Description |
|---|---|---|---|
| blocked required | integer[] | no default | Cell values treated as impassable walls. |
returns grid.Cell record 2 keys
| Key | Type | Description |
|---|---|---|
| x | integer | Cell column. |
| y | integer | Cell row. |
Grid:agent(opts: grid.AgentOptions) -> PathAgentCreate a PathAgent starting at the given position: a mover that owns the walk-the-waypoints bookkeeping against this grid (or any grid passed to its methods later).
-- Positions are in cells: 0.5 is the middle of cell 0.
local agent = grid:agent({
x = 0.5,
y = 0.5,
speed = 4,
blocked = { 1 },
repath = 0.5,
})
agent:set_target(grid, 7, 0)
print(agent.x, agent.y) --> 0.5 0.5opts grid.AgentOptions options 7 keys
| Key | Type | Default | Description |
|---|---|---|---|
| x required | number | no default | Starting position, in cells. |
| y required | number | no default | Starting position, in cells. |
| speed required | number | no default | Movement speed, in cells per second. |
| blocked | integer[] | no cell value blocks the agent | Cell values the agent will not path through. |
| repath | number | the agent paths once per set_target, and whenever it has no path | Repath cadence in seconds while a target is set. |
| arrive_radius | number | 0 | Stop this many cells short of the target center instead of standing on it -- for follow-at-distance movers; 0 arrives on the cell. |
| stop_before | integer[] | the agent walks into every cell it paths through | Cell values the agent paths through but will not walk into: the walk halts flush before such a cell and the advance result carries it as stopped. Handle the cell (open the door, clear the line) and the walk resumes on its own once the cell's value changes. |
Grid:slide(x: number, y: number, dx: number, dy: number, w: number, h: number, opts: grid.SlideOptions) -> number, numberMove a w x h axis-aligned box centered at (x, y) by (dx, dy), sliding against cells whose value is in solid: the x move applies and clamps first, then the y move — the classic tile character controller. Units are cells (1.0 = one cell); a clamped box stops flush on the cell face. Cells outside the grid are open, and a box already overlapping a solid cell collides only with cells it newly enters. Returns the final center.
-- A 1 x 1 box centered in cell (1, 0) moves 3 cells right.
local x, y = grid:slide(1.5, 0.5, 3, 0, 1, 1, { solid = { 1 } })
-- It stops flush against the wall in column 3.
print(x, y) --> 2.5 0.5opts grid.SlideOptions options 1 key
| Key | Type | Default | Description |
|---|---|---|---|
| solid required | integer[] | no default | Cell values the box collides with. |
Grid:dijkstra(opts: grid.DijkstraOptions) -> GridA new Grid of distance scores from weighted sources (-1 unreachable).
local steps = grid:dijkstra({
sources = { { x = 7, y = 0 } },
blocked = { 1 },
})
-- Steps from the source; -1 where it cannot reach.
print(steps:get(7, 0), steps:get(4, 0), steps:get(3, 0)) --> 0 3 -1opts grid.DijkstraOptions options 2 keys
| Key | Type | Default | Description |
|---|---|---|---|
| sources required | grid.DijkstraSource[] | no default | Weighted source cells the distance field flows out from. |
| blocked required | integer[] | no default | Cell values treated as impassable walls. |
Grid:field_of_view(x: integer, y: integer, opts: grid.FieldOfViewOptions) -> grid.Cell[]Visible cells from (x,y).
local seen = grid:field_of_view(1, 2, { range = 4, opaque = { 1 } })
local visible = {}
for _, cell in ipairs(seen) do
visible[cell.x .. "," .. cell.y] = true
end
-- The wall is seen; what is behind it is not.
print(visible["3,2"], visible["4,2"]) --> true nilopts grid.FieldOfViewOptions options 2 keys
| Key | Type | Default | Description |
|---|---|---|---|
| range required | integer | no default | Maximum sight radius in cells. |
| opaque required | integer[] | no default | Cell values that block line of sight. |
returns grid.Cell record 2 keys
| Key | Type | Description |
|---|---|---|
| x | integer | Cell column. |
| y | integer | Cell row. |
Grid:flood_fill(x: integer, y: integer, opts: grid.FloodFillOptions) -> grid.Cell[]8-connected flood fill from (x,y).
-- Spreads through matching cells, diagonals included.
local room = grid:flood_fill(0, 0, { match = { 0 } })
-- Every open cell: 48 cells less the 5 of the wall.
print(#room) --> 43opts grid.FloodFillOptions options 1 key
| Key | Type | Default | Description |
|---|---|---|---|
| match required | integer[] | no default | Cell values the fill is allowed to spread into. |
returns grid.Cell record 2 keys
| Key | Type | Description |
|---|---|---|
| x | integer | Cell column. |
| y | integer | Cell row. |
Grid:count_neighbors(x: integer, y: integer, opts: grid.CountNeighborsOptions) -> integerCount of the 8 neighbors whose value is in match.
-- Of the 8 cells around (2, 2), 3 are wall.
print(grid:count_neighbors(2, 2, { match = { 1 } })) --> 3opts grid.CountNeighborsOptions options 1 key
| Key | Type | Default | Description |
|---|---|---|---|
| match required | integer[] | no default | Cell values counted as matching neighbors. |
Grid:automata_step(opts: grid.AutomataStepOptions)Advance one cellular-automata generation in place.
local life = datumhue.grid.new(5, 5)
life:fill_rect(1, 2, 3, 1, 1)
-- Conway's rules: the row of three turns into a column.
life:automata_step({
alive = { 1 },
birth = { 3 },
survival = { 2, 3 },
})
print(life:get(2, 1), life:get(2, 3), life:get(1, 2)) --> 1 1 0opts grid.AutomataStepOptions options 5 keys
| Key | Type | Default | Description |
|---|---|---|---|
| alive required | integer[] | no default | Cell values considered alive this generation. |
| birth required | integer[] | no default | Live-neighbor counts that turn a dead cell alive. |
| survival required | integer[] | no default | Live-neighbor counts that keep a live cell alive. |
| alive_value | integer | 1 | Value written to cells alive next generation. |
| dead_value | integer | 0 | Value written to cells dead next generation. |
Grid:fill_noise(sampler: NoiseSampler, opts?: grid.FillNoiseOptions)Bucket a noise sampler into integer cell values.
local hills = datumhue.noise.perlin({ seed = 1, frequency = 0.2 })
-- Below -0.2 is water (0), up to 0.3 grass (1), above that rock (2).
grid:fill_noise(hills, {
thresholds = { -0.2, 0.3 },
values = { 0, 1, 2 },
})
print(grid:get(0, 0)) --> 1opts grid.FillNoiseOptions options 4 keys
| Key | Type | Default | Description |
|---|---|---|---|
| thresholds | number[] | no cutoffs; with values also omitted, one cutoff at 0 | Ascending noise cutoffs partitioning samples into buckets. |
| values | integer[] | no values; with thresholds also omitted, 0 for samples below 0 and 1 for the rest | Cell value per bucket; length must equal len(thresholds)+1. |
| offset_x | number | 0 | Horizontal offset added to each cell's sample coordinate. |
| offset_y | number | 0 | Vertical offset added to each cell's sample coordinate. |