package model; import controller.Simulation; import datastruct.Coordinate; import javafx.beans.property.ReadOnlyLongProperty; import javafx.beans.property.ReadOnlyLongWrapper; import javafx.scene.paint.Color; import java.util.Iterator; import java.util.function.Supplier; import static java.util.Objects.requireNonNull; /** * {@link CellularAutomataSimulation} instances run <i>The Game of Life</i>. */ public class CellularAutomataSimulation<S extends State<S>> implements Simulation { private final CellGrid<S> grid; private final Supplier<S> supplier; private final S defaultState; private final ReadOnlyLongWrapper generationNumber = new ReadOnlyLongWrapper(); /** * Creates a new {@code GameOfLife} instance given the underlying {@link CellGrid}. * * @param grid the underlying {@link CellGrid} * @param defaultState the state value to use when clearing the grid * @param supplier a {@Link Supplier} to produce values to initialize or reset the grid * @throws NullPointerException if {@code grid} is {@code null} */ public CellularAutomataSimulation(CellGrid<S> grid, S defaultState, Supplier<S> supplier) { this.grid = requireNonNull(grid, "grid is null"); this.supplier = requireNonNull(supplier, "supplier is null"); this.defaultState = requireNonNull(defaultState, "defaultState is null"); grid.fillRandomly(this.supplier); } @Override public int numberOfColumns() { return this.grid.getNumberOfColumns(); } @Override public int numberOfRows() { return this.grid.getNumberOfRows(); } /** * Transitions into the next generationNumber. */ @Override public void updateToNextGeneration() { grid.updateToNextGeneration(); generationNumber.set(getGenerationNumber() + 1); } @Override public void next(Coordinate coordinate) { this.grid.cellAt(coordinate).toggleState(); } @Override public void copy(Coordinate source, Coordinate destination) { S state = this.grid.at(source).get(); this.grid.at(destination).set(state); } @Override public Color getColor(Coordinate coordinate) { return this.grid.at(coordinate).get().getColor(); } @Override public void setChangeListener(Coordinate coordinate, Runnable runnable) { this.grid.cellAt(coordinate).getStateProperty().addListener( (obs,oldV,newV) -> runnable.run() ); } /** * Returns the current generationNumber. * * @return the current generationNumber */ private long getGenerationNumber() { return generationNumber.get(); } /** * Returns the generationNumber {@link ReadOnlyLongProperty}. * * @return the generationNumber {@link ReadOnlyLongProperty} */ public ReadOnlyLongProperty generationNumberProperty() { return generationNumber.getReadOnlyProperty(); } /** * Clears the current game. */ public void clear() { grid.clear(defaultState); generationNumber.set(0); } /** * Clears the current game and randomly generates a new one. */ public void reset() { clear(); grid.fillRandomly(supplier); } @Override public Iterator<Coordinate> iterator() { return this.grid.coordinates().iterator(); } }