From a152b729204d4f0f7f9b058bdcd82d32e255c2aa Mon Sep 17 00:00:00 2001 From: Guyslain <guyslain.naves@lis-lab.fr> Date: Mon, 9 Oct 2023 15:14:25 +0200 Subject: [PATCH] More refactoring, several simulations added --- build.gradle | 2 +- ...ication.java => SimulatorApplication.java} | 42 ++++++------- src/main/java/controller/Controller.java | 6 +- src/main/java/datastruct/Matrix.java | 2 +- src/main/java/model/CellGrid.java | 2 +- ....java => CellularAutomatonSimulation.java} | 44 ++++++------- ...er.java => NextGenerationInitializer.java} | 4 +- src/main/java/model/states/BiColorState.java | 61 +++++++++++++++++++ .../java/model/states/BriansBrainState.java | 57 +++++++++++++++++ .../model/{ => states}/GameOfLifeState.java | 3 +- src/main/java/model/states/SeedsState.java | 40 ++++++++++++ 11 files changed, 210 insertions(+), 53 deletions(-) rename src/main/java/{GameOfLifeApplication.java => SimulatorApplication.java} (56%) rename src/main/java/model/{CellularAutomataSimulation.java => CellularAutomatonSimulation.java} (62%) rename src/main/java/model/{OneStepMatrixInitializer.java => NextGenerationInitializer.java} (80%) create mode 100644 src/main/java/model/states/BiColorState.java create mode 100644 src/main/java/model/states/BriansBrainState.java rename src/main/java/model/{ => states}/GameOfLifeState.java (96%) create mode 100644 src/main/java/model/states/SeedsState.java diff --git a/build.gradle b/build.gradle index a8007cc..280df72 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 0d8e7c6..fb22b7f 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 fcfd14f..84e73a3 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 645efaf..dbe33a0 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 87dd605..5df1fb5 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 1dd0fb7..8bd51b3 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 5348af2..787a379 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 0000000..de0b791 --- /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 0000000..a90286c --- /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 1956a55..6378a92 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 0000000..0895372 --- /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; + } +} -- GitLab