Skip to content
SN-X
All research
PostApril 2, 2025

Coord-Aware Neighbor Lookup in Tile-Based Cellular Automaton Simulators: A Note on the width=100 Heuristic

A short note on a class of bug common in tile-based cellular-automaton engines: the use of a fixed stride when mapping a (dx, dy) neighbour offset to a linear tile id. We describe the failure mode, the conditions under which it is silent, and the minimal fix in terms of explicit (x, y) coordinates on the tile.

M. TaylorSN-X
View on github · ooze-engine

1. Introduction

Tile-based CA simulators typically identify tiles by a linear id derived from row-major coordinates: id = (y · W + x). A neighbour lookup is then neighbor_id = id + dx + dy · W. The stride W is the row width. When W is encoded as a literal, the literal is a parameter that does not appear in the type system. When the actual width does not equal the literal, the lookup is silently wrong.

2. The Bug

The OOZE engine's DiffusionRule uses neighbor_id_val = tile_id.0 as i64 + (dx as i64) + (dy as i64) * 100. The 100 is a stand-in for the row width.

3. When It Is Silent

The bug only manifests when (dx, dy) ≠ (0, 0) and the target row is not the same as the source row. On a 100×100 grid the bug never fires. On any other width it fires on every dy ≠ 0 lookup, silently reading from the wrong tile.

4. The Minimal Fix

Make (x, y) part of the tile's identity:

Tile {
    id: TileId,
    coords: (u32, u32),
    material: MaterialId,
    ...
}

fn neighbor_of(tile: &Tile, dx: i32, dy: i32, width: u32, height: u32) -> Option<TileId> {
    let nx = tile.coords.0 as i32 + dx;
    let ny = tile.coords.1 as i32 + dy;
    if nx < 0 || ny < 0 || nx >= width as i32 || ny >= height as i32 {
        None
    } else {
        Some(TileId::new((ny as u32 * width + nx as u32) as u64))
    }
}

5. The General Lesson

When a physical quantity is encoded as a derived value and a neighbour lookup is performed in derived coordinates, something has to carry the row width. If that something is a literal, the literal is a parameter that does not appear in the type system. A small change to make coordinates explicit eliminates the entire class of bug.

6. Why We Wrote This Up

The bug is silent, common, and easy to miss. The first symptom is diffusion that does not quite work on a non-100×100 grid.

References

Chopard & Droz (1998). Cambridge UP. / Vichniac (1984). In Cellular Automata. North-Holland. / Wolfram (1986). World Scientific.