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 CellularAutomatonSimulation} instances run <i>The Game of Life</i>. */ public class CellularAutomatonSimulation<S extends State<S>> implements Simulation { private final CellGrid<S> grid; private final Supplier<S> randomState; private final S defaultState; private final ReadOnlyLongWrapper generationNumber = new ReadOnlyLongWrapper(); /** * Creates a new {@code CellularAutomataSimulation} instance for a given automaton. * * @param width an {@code int} representing the number of columns * @param height an {@code int} representing the number of rows * @param defaultState a state {@code S} used to fill the grid when using * the clear action * @param randomState a generator of states {@code} used to fill the grid * when using the reset action */ public CellularAutomatonSimulation(int width, int height, S defaultState, Supplier<S> randomState) { this.grid = new CellGrid<>(width, height, defaultState); this.defaultState = defaultState; this.randomState = randomState; grid.fillRandomly(randomState); } @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() { this.grid.updateToNextGeneration(); this.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 this.generationNumber.get(); } /** * Returns the generationNumber {@link ReadOnlyLongProperty}. * * @return the generationNumber {@link ReadOnlyLongProperty} */ public ReadOnlyLongProperty generationNumberProperty() { return this.generationNumber.getReadOnlyProperty(); } /** * Clears the current game. */ public void clear() { this.grid.clear(this.defaultState); this.generationNumber.set(0); } /** * Clears the current game and randomly generates a new one. */ public void reset() { this.clear(); this.grid.fillRandomly(this.randomState); } @Override public Iterator<Coordinate> iterator() { return this.grid.coordinates().iterator(); } }