Complex Cellular Automata
Model advanced phenomena using the refinements hook and custom state objects.
A Complex Cellular Automaton (CCA) extends the standard CA model with a pre-processing step that runs before the transition function reads its neighbors. This refinement step gives each cell the opportunity to update its internal state (for example, accumulate incoming flows or decrement a counter) before those updated values are seen by neighboring cells in the same generation.
When to use a CCA
Use the refinements hook when:
- A cell’s next state depends on accumulated quantities that must be resolved before the transition function is applied (e.g., heat diffusion, lava flow, erosion).
- Your simulation requires cells to perform bookkeeping between generations — such as decrementing a lifetime counter or normalizing a fractional flow value.
- The transition function alone is insufficient to model the phenomenon correctly.
Data flow per generation
for each generation:
1. refinements(cell) — applied to every cell [optional CCA hook]
2. snapshot the grid — clone the map before any mutation
3. transition(cell, neighbors) — per cell, reads the snapshot, returns next state
4. copy results back — update the main map with all new states
Implementing refinements
Override refinements(DefaultCell cell) in your executor class. Return the modified cell.
public class MyComplexRule extends CellularAutomataRule {
@Override
public DefaultCell refinements(DefaultCell cell) {
// Example: normalize accumulated flow before neighbor lookup
MyStatus status = (MyStatus) cell.getCurrentStatus();
status.normalizeFlow();
cell.setCurrentStatus(status);
return cell;
}
@Override
public DefaultCell transition(DefaultCell cell, List<DefaultCell> neighbors) {
// Standard transition logic — neighbors already have refined state
MyStatus current = (MyStatus) cell.getCurrentStatus();
// ... compute next state ...
return new DefaultCell(nextStatus, cell.getCol(), cell.getRow());
}
}
Combining with a custom status
CCA implementations almost always pair the refinements hook with a
custom state object that carries the extra per-cell data the
refinement step needs to update.
Example pattern: heat diffusion
public class HeatStatus extends DefaultStatus {
public double temperature;
public HeatStatus(double temperature) {
super("heat", temperature);
this.temperature = temperature;
}
}
public class HeatDiffusionRule extends CellularAutomataRule {
@Override
public DefaultCell refinements(DefaultCell cell) {
// Clamp temperature to physical bounds before sharing with neighbors
HeatStatus s = (HeatStatus) cell.getCurrentStatus();
s.temperature = Math.max(0.0, Math.min(s.temperature, 1000.0));
cell.setCurrentStatus(s);
return cell;
}
@Override
public DefaultCell transition(DefaultCell cell, List<DefaultCell> neighbors) {
double avgTemp = neighbors.stream()
.mapToDouble(n -> ((HeatStatus) n.getCurrentStatus()).temperature)
.average()
.orElse(0.0);
double current = ((HeatStatus) cell.getCurrentStatus()).temperature;
double next = (current + avgTemp) / 2.0; // simple diffusion
return new DefaultCell(new HeatStatus(next), cell.getCol(), cell.getRow());
}
}
See also
- Custom State Objects — how to extend
DefaultStatus. - Implementing a Rule — the standard executor pattern.
- Configuration Reference — builder options.