diff --git a/build.gradle b/build.gradle index a8007cc98c951e2a814d90a8940d919c60c435d9..280df728924ea79954dc40baa7ab93c7a20f5294 100644 --- a/build.gradle +++ b/build.gradle @@ -23,5 +23,5 @@ test { } application { - mainClassName = "GameOfLifeApplication" + mainClassName = "SimulatorApplication" } \ No newline at end of file diff --git a/src/main/java/GameOfLifeApplication.java b/src/main/java/SimulatorApplication.java similarity index 56% rename from src/main/java/GameOfLifeApplication.java rename to src/main/java/SimulatorApplication.java index 0d8e7c6444e775a57c81131da5041e1765d666a7..fb22b7fa214f754ce32107eeab41b537783adde7 100644 --- a/src/main/java/GameOfLifeApplication.java +++ b/src/main/java/SimulatorApplication.java @@ -5,12 +5,13 @@ import javafx.fxml.FXMLLoader; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.stage.Stage; -import model.CellGrid; -import model.CellularAutomataSimulation; -import model.GameOfLifeState; +import model.CellularAutomatonSimulation; +import model.states.BriansBrainState; +import model.states.SeedsState; import java.io.IOException; import java.net.URL; +import java.util.Random; import static java.util.Objects.requireNonNull; @@ -18,38 +19,33 @@ import static java.util.Objects.requireNonNull; * Entry point for <i>The Game of Life</i> application. * */ -public class GameOfLifeApplication extends Application { +public class SimulatorApplication extends Application { - private static final int NUMBER_OF_ROWS = 40; - private static final int NUMBER_OF_COLUMNS = 70; + public static final int NUMBER_OF_ROWS = 40; + public static final int NUMBER_OF_COLUMNS = 70; + + public static final Random GENERATOR = new Random(); private static final String APP_NAME = "Game of Life"; private static final String VIEW_RESOURCE_PATH = "/view/view.fxml"; - private final CellularAutomataSimulation<GameOfLifeState> gameOfLife; + private final CellularAutomatonSimulation<SeedsState> gameOfLife; private Stage primaryStage; private Parent view; /** * Creates a new {@code GameOfLifeApplication} instance. */ - public GameOfLifeApplication() { - this(new CellularAutomataSimulation<GameOfLifeState>( - new CellGrid<>(NUMBER_OF_COLUMNS, NUMBER_OF_ROWS, GameOfLifeState.ALIVE), - GameOfLifeState.DEAD, - GameOfLifeState::random - )); + public SimulatorApplication() { + this.gameOfLife = + new CellularAutomatonSimulation<>( + NUMBER_OF_COLUMNS, + NUMBER_OF_ROWS, + SeedsState.OFF, + SeedsState::random + ); } - /** - * Creates a new {@code GameOfLifeApplication} instance given a {@link CellularAutomataSimulation} instance. - * - * @param cellularAutomataSimulation the {@link CellularAutomataSimulation} instance - * @throws NullPointerException if {@code gameOfLife} is {@code null} - */ - private GameOfLifeApplication(CellularAutomataSimulation<GameOfLifeState> cellularAutomataSimulation) { - this.gameOfLife = requireNonNull(cellularAutomataSimulation, "game of life is null"); - } @Override public void start(Stage primaryStage) throws IOException { @@ -68,7 +64,7 @@ public class GameOfLifeApplication extends Application { private void initializeView() throws IOException { FXMLLoader loader = new FXMLLoader(); - URL location = GameOfLifeApplication.class.getResource(VIEW_RESOURCE_PATH); + URL location = SimulatorApplication.class.getResource(VIEW_RESOURCE_PATH); loader.setLocation(location); view = loader.load(); Controller controller = loader.getController(); diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java index fcfd14f76c03b86b68223b7e7b1ce59166c6c65d..84e73a3ad497c0ee5a5258d884062d50cb1cb4ce 100644 --- a/src/main/java/controller/Controller.java +++ b/src/main/java/controller/Controller.java @@ -11,7 +11,7 @@ import javafx.scene.control.Label; import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleGroup; import javafx.util.Duration; -import model.CellularAutomataSimulation; +import model.CellularAutomatonSimulation; import view.MatrixPane; import static java.util.Objects.requireNonNull; @@ -52,9 +52,9 @@ public class Controller { /** - * Sets {@link CellularAutomataSimulation} instance. + * Sets {@link CellularAutomatonSimulation} instance. * - * @param simulation {@link CellularAutomataSimulation} instance + * @param simulation {@link CellularAutomatonSimulation} instance * @throws NullPointerException if {@code gameOfLife} is {@code null} */ diff --git a/src/main/java/datastruct/Matrix.java b/src/main/java/datastruct/Matrix.java index 645efaf577bfe29178ead0ba90d81a19797c4811..dbe33a03b2dd82a50856d2626baf196b852e3ec6 100644 --- a/src/main/java/datastruct/Matrix.java +++ b/src/main/java/datastruct/Matrix.java @@ -50,7 +50,7 @@ public class Matrix<T> { public Iterator<T> iterator() { Iterator<Coordinate> coordIterator = this.coordinatesIterator(); - return new MatrixIterator(this, coordIterator); + return new MatrixIterator<>(this, coordIterator); } public Iterable<Coordinate> coordinates() { diff --git a/src/main/java/model/CellGrid.java b/src/main/java/model/CellGrid.java index 87dd605c6aaac8c832b9373f69555e0b87c399e3..5df1fb5322c6368e93a15f2d4bac4c32e4b65f10 100644 --- a/src/main/java/model/CellGrid.java +++ b/src/main/java/model/CellGrid.java @@ -107,7 +107,7 @@ public class CellGrid<S extends State<S>> implements Iterable<Cell<S>> { return new Matrix<>( this.numberOfColumns, this.numberOfRows, - new OneStepMatrixInitializer<>(this) + new NextGenerationInitializer<>(this) ); } diff --git a/src/main/java/model/CellularAutomataSimulation.java b/src/main/java/model/CellularAutomatonSimulation.java similarity index 62% rename from src/main/java/model/CellularAutomataSimulation.java rename to src/main/java/model/CellularAutomatonSimulation.java index 1dd0fb7cc3b584ebb235acae01426e2b883ceeae..8bd51b3b0b3dd8716d78b5e928b8d00054acfe75 100644 --- a/src/main/java/model/CellularAutomataSimulation.java +++ b/src/main/java/model/CellularAutomatonSimulation.java @@ -12,29 +12,31 @@ import java.util.function.Supplier; import static java.util.Objects.requireNonNull; /** - * {@link CellularAutomataSimulation} instances run <i>The Game of Life</i>. + * {@link CellularAutomatonSimulation} instances run <i>The Game of Life</i>. */ -public class CellularAutomataSimulation<S extends State<S>> +public class CellularAutomatonSimulation<S extends State<S>> implements Simulation { private final CellGrid<S> grid; - private final Supplier<S> supplier; + private final Supplier<S> randomState; private final S defaultState; private final ReadOnlyLongWrapper generationNumber = new ReadOnlyLongWrapper(); /** - * Creates a new {@code GameOfLife} instance given the underlying {@link CellGrid}. + * Creates a new {@code CellularAutomataSimulation} instance for a given automaton. * - * @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} + * @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 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); + 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); } @@ -54,8 +56,8 @@ public class CellularAutomataSimulation<S extends State<S>> */ @Override public void updateToNextGeneration() { - grid.updateToNextGeneration(); - generationNumber.set(getGenerationNumber() + 1); + this.grid.updateToNextGeneration(); + this.generationNumber.set(getGenerationNumber() + 1); } @Override @@ -88,7 +90,7 @@ public class CellularAutomataSimulation<S extends State<S>> * @return the current generationNumber */ private long getGenerationNumber() { - return generationNumber.get(); + return this.generationNumber.get(); } /** @@ -97,7 +99,7 @@ public class CellularAutomataSimulation<S extends State<S>> * @return the generationNumber {@link ReadOnlyLongProperty} */ public ReadOnlyLongProperty generationNumberProperty() { - return generationNumber.getReadOnlyProperty(); + return this.generationNumber.getReadOnlyProperty(); } @@ -105,16 +107,16 @@ public class CellularAutomataSimulation<S extends State<S>> * Clears the current game. */ public void clear() { - grid.clear(defaultState); - generationNumber.set(0); + this.grid.clear(this.defaultState); + this.generationNumber.set(0); } /** * Clears the current game and randomly generates a new one. */ public void reset() { - clear(); - grid.fillRandomly(supplier); + this.clear(); + this.grid.fillRandomly(this.randomState); } @Override diff --git a/src/main/java/model/OneStepMatrixInitializer.java b/src/main/java/model/NextGenerationInitializer.java similarity index 80% rename from src/main/java/model/OneStepMatrixInitializer.java rename to src/main/java/model/NextGenerationInitializer.java index 5348af20361bdf0a3663054e498e6f7ee4d4e547..787a3791dc7a69dce1e569cb014b3f1e3fb0a240 100644 --- a/src/main/java/model/OneStepMatrixInitializer.java +++ b/src/main/java/model/NextGenerationInitializer.java @@ -6,11 +6,11 @@ import datastruct.MatrixInitializer; import java.util.ArrayList; import java.util.List; -public class OneStepMatrixInitializer<S extends State<S>> implements MatrixInitializer<S> { +public class NextGenerationInitializer<S extends State<S>> implements MatrixInitializer<S> { private final CellGrid<S> grid; - public OneStepMatrixInitializer(CellGrid<S> grid) { + public NextGenerationInitializer(CellGrid<S> grid) { this.grid = grid; } diff --git a/src/main/java/model/states/BiColorState.java b/src/main/java/model/states/BiColorState.java new file mode 100644 index 0000000000000000000000000000000000000000..de0b791cf798b10b9a27b7d24582a63063b1cfec --- /dev/null +++ b/src/main/java/model/states/BiColorState.java @@ -0,0 +1,61 @@ +package model.states; + +import javafx.scene.paint.Color; +import model.State; + +import java.util.List; +import java.util.Random; + +public enum BiColorState implements State<BiColorState> { + BLUE, RED, DEAD; + + + + @Override + public Color getColor() { + return switch (this) { + case BLUE -> Color.BLUE; + case RED -> Color.RED; + case DEAD -> Color.WHITE; + }; + } + + @Override + public BiColorState next() { + return switch (this) { + case BLUE -> RED; + case RED -> DEAD; + case DEAD -> BLUE; + }; + } + + @Override + public BiColorState update(List<State<BiColorState>> neighbours) { + int countBlue = 0; + int countRed = 0; + for (State<BiColorState> neighbour : neighbours) { + if (neighbour == RED) { + countRed++; + } + if (neighbour == BLUE) { + countBlue++; + } + } + if (this == DEAD) { + return (countBlue + countRed != 3)? DEAD: + countBlue > countRed? BLUE: + RED; + } + return 2 <= countBlue + countRed && countBlue + countRed <= 3? this: + DEAD; + } + + + private final static Random generator = new Random(); + + public static BiColorState random() { + return generator.nextBoolean()? DEAD: + generator.nextBoolean()? RED: + BLUE; + } +} diff --git a/src/main/java/model/states/BriansBrainState.java b/src/main/java/model/states/BriansBrainState.java new file mode 100644 index 0000000000000000000000000000000000000000..a90286c47b4371a30e3ed7aa37a08d07f203250d --- /dev/null +++ b/src/main/java/model/states/BriansBrainState.java @@ -0,0 +1,57 @@ +package model.states; + +import javafx.scene.paint.Color; +import model.State; + +import java.util.List; +import java.util.Random; + +public enum BriansBrainState implements State<BriansBrainState> { + ON, OFF, DYING; + + @Override + public Color getColor() { + return switch (this) { + case ON -> Color.WHITE; + case OFF -> Color.BLACK; + case DYING -> Color.BLUE; + }; + } + + @Override + public BriansBrainState next() { + return switch (this) { + case ON -> DYING; + case OFF -> ON; + case DYING -> OFF; + }; + } + + @Override + public BriansBrainState update(List<State<BriansBrainState>> neighbours) { + return switch (this) { + case ON -> DYING; + case DYING -> OFF; + case OFF -> { + int count = countList(ON, neighbours); + yield count==2 ? ON : OFF; + } + }; + } + + static <T> int countList(T value, List<T> elements) { + int count = 0; + for (T v : elements) { + if (v.equals(value)) { + count++; + } + } + return count; + } + + final static Random GENERATOR = new Random(); + + public static BriansBrainState random() { + return GENERATOR.nextInt(10) == 0 ? ON: OFF; + } +} diff --git a/src/main/java/model/GameOfLifeState.java b/src/main/java/model/states/GameOfLifeState.java similarity index 96% rename from src/main/java/model/GameOfLifeState.java rename to src/main/java/model/states/GameOfLifeState.java index 1956a555244a666a7fc94f93b9ef2e942b840d5e..6378a9224fb3975783bda7b3b67f8b9c0ca39514 100644 --- a/src/main/java/model/GameOfLifeState.java +++ b/src/main/java/model/states/GameOfLifeState.java @@ -1,6 +1,7 @@ -package model; +package model.states; import javafx.scene.paint.Color; +import model.State; import java.util.List; import java.util.Random; diff --git a/src/main/java/model/states/SeedsState.java b/src/main/java/model/states/SeedsState.java new file mode 100644 index 0000000000000000000000000000000000000000..08953724104aace1e7479d609265d7cc1cd4061e --- /dev/null +++ b/src/main/java/model/states/SeedsState.java @@ -0,0 +1,40 @@ +package model.states; + +import javafx.scene.paint.Color; +import model.State; + +import java.util.List; + +import static model.states.BriansBrainState.GENERATOR; +import static model.states.BriansBrainState.countList; + +public enum SeedsState implements State<SeedsState> { + ON, OFF; + @Override + public Color getColor() { + return switch (this) { + case ON -> Color.WHITE; + case OFF -> Color.BLACK; + }; + } + + @Override + public SeedsState next() { + return switch (this) { + case ON -> OFF; + case OFF -> ON; + }; + } + + @Override + public SeedsState update(List<State<SeedsState>> neighbours) { + return switch (this) { + case ON -> OFF; + case OFF -> countList(ON,neighbours) == 2 ? ON: OFF; + }; + } + + public static SeedsState random() { + return GENERATOR.nextInt(10)==0? ON: OFF; + } +}