Generation Listeners
React to every generation using GenerationListener and GridSnapshot.
Overview
GenerationListener is a @FunctionalInterface in the core package that lets you
hook into the automaton engine after every completed generation, without modifying
the transition rule itself.
After each generation, the engine:
- Creates an immutable
GridSnapshotof the current grid state. - Calls
onGeneration(generation, snapshot)on every registered listener.
GenerationListener
@FunctionalInterface
public interface GenerationListener {
void onGeneration(int generation, GridSnapshot snapshot);
}
| Parameter | Description |
|---|---|
generation |
1-based index of the completed generation |
snapshot |
Immutable GridSnapshot of the grid after the transition |
Register listeners on any CellularAutomataRule (or CellularAutomataParallelRule)
before calling run:
CellularAutomataRule rule = new GameOfLifeRule();
rule.addGenerationListener((gen, snap) -> {
System.out.println("Generation: " + gen);
});
ca = rule.run(ca);
Multiple listeners can be registered; they are called in registration order.
GenerationListener is a @FunctionalInterface, you can supply a lambda,
a method reference, or an anonymous class.GridSnapshot
GridSnapshot is an immutable view of the grid at a specific generation. It is the
primary way to read grid state from a listener without touching the live grid.
Creating a snapshot manually
GridSnapshot snap = GridSnapshot.of(generation, ca.getGrid());
The engine creates snapshots automatically for every listener call — you only need this for manual inspection outside of a listener.
Reading cell states
2D convenience:
CellState state = snap.getState(col, row);
nD (3D, 4D):
CellState state = snap.getState(new int[]{x, y, z});
Flat list (all cells, row-major order):
List<CellState> allStates = snap.getCellStates(); // unmodifiable
The flat list follows the same row-major ordering as CellGrid.allCoordinates().
Available methods
| Method | Description |
|---|---|
getGeneration() |
The generation index at which the snapshot was taken |
getDimensions() |
The GridDimensions of this snapshot |
getCellStates() |
Unmodifiable flat list of all CellState values (row-major) |
getState(int col, int row) |
2D convenience accessor |
getState(int[] coords) |
nD accessor |
Common Patterns
Count alive cells per generation
CellState ALIVE = new CellState("alive", "1");
rule.addGenerationListener((gen, snap) -> {
long count = snap.getCellStates().stream()
.filter(s -> s.equals(ALIVE))
.count();
System.out.printf("Gen %d: %d alive cells%n", gen, count);
});
Record history
List<GridSnapshot> history = new ArrayList<>();
rule.addGenerationListener((gen, snap) -> history.add(snap));
rule.run(ca);
// history now contains one snapshot per generation
Stop after a condition
Because CellularAutomataRule.run runs on the calling thread, you can interrupt it
from inside a listener:
rule.addGenerationListener((gen, snap) -> {
if (isStableState(snap)) {
Thread.currentThread().interrupt();
}
});
See Also
- Implementing a Rule — writing the transition function.
- UI Visualisation — using
AutomataListenerto drive a Swing window.