diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..1ca2791ac9f020514c76d0f18cf6359445967815 --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# Idea +.idea/ + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + + diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..09a6c289f945ec428a53dbc8f3aab2adec420fd4 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,40 @@ +image: openjdk:17-alpine + +variables: + GRADLE_OPTS: "-Dorg.gradle.daemon=false" + +before_script: + - export GRADLE_USER_HOME=`pwd`/.gradle + +cache: + paths: + - .gradle/wrapper + - .gradle/caches + +stages: + - build + - test + +build: + stage: build + script: ./gradlew --build-cache assemble + cache: + key: "$CI_COMMIT_REF_NAME" + policy: push + paths: + - build + - .gradle + +java: + stage: test + script: ./gradlew test + artifacts: + when: always + reports: + junit: build/test-results/test/**/TEST-*.xml + cache: + key: "$CI_COMMIT_REF_NAME" + policy: pull + paths: + - build + - .gradle \ No newline at end of file diff --git a/.gradle/7.4/checksums/checksums.lock b/.gradle/7.4/checksums/checksums.lock deleted file mode 100644 index df54c1e0932a6aad9b35fe2f5781753d92dd27c2..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/checksums/checksums.lock and /dev/null differ diff --git a/.gradle/7.4/checksums/md5-checksums.bin b/.gradle/7.4/checksums/md5-checksums.bin deleted file mode 100644 index 38740cef473d59e77cc7580e7c7e168c289010c2..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/checksums/md5-checksums.bin and /dev/null differ diff --git a/.gradle/7.4/checksums/sha1-checksums.bin b/.gradle/7.4/checksums/sha1-checksums.bin deleted file mode 100644 index 37d18a5f1d3ac5802ec2e11e6e2ee389d38eabe7..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/checksums/sha1-checksums.bin and /dev/null differ diff --git a/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock b/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock deleted file mode 100644 index 9ea5ddcee208651ce5fe944ac8385c34443ae9f3..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/dependencies-accessors/dependencies-accessors.lock and /dev/null differ diff --git a/.gradle/7.4/dependencies-accessors/gc.properties b/.gradle/7.4/dependencies-accessors/gc.properties deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/.gradle/7.4/executionHistory/executionHistory.bin b/.gradle/7.4/executionHistory/executionHistory.bin deleted file mode 100644 index 8044f8db540d8ca16951815ac998f6158a86ba9c..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/executionHistory/executionHistory.bin and /dev/null differ diff --git a/.gradle/7.4/executionHistory/executionHistory.lock b/.gradle/7.4/executionHistory/executionHistory.lock deleted file mode 100644 index e57456dc4f9a187338a1af68d935fbf5cffe29da..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/executionHistory/executionHistory.lock and /dev/null differ diff --git a/.gradle/7.4/fileChanges/last-build.bin b/.gradle/7.4/fileChanges/last-build.bin deleted file mode 100644 index f76dd238ade08917e6712764a16a22005a50573d..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/fileChanges/last-build.bin and /dev/null differ diff --git a/.gradle/7.4/fileHashes/fileHashes.bin b/.gradle/7.4/fileHashes/fileHashes.bin deleted file mode 100644 index ac4d206fd6144671088bac4e8af72f9d24c99d8f..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/fileHashes/fileHashes.bin and /dev/null differ diff --git a/.gradle/7.4/fileHashes/fileHashes.lock b/.gradle/7.4/fileHashes/fileHashes.lock deleted file mode 100644 index f382ced7e8f10e14ccd723be3c74dae1c97aa72b..0000000000000000000000000000000000000000 Binary files a/.gradle/7.4/fileHashes/fileHashes.lock and /dev/null differ diff --git a/.gradle/7.4/gc.properties b/.gradle/7.4/gc.properties deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/.gradle/buildOutputCleanup/buildOutputCleanup.lock b/.gradle/buildOutputCleanup/buildOutputCleanup.lock deleted file mode 100644 index f7492f18c6ecf67db5af3279f7c8498953313f0a..0000000000000000000000000000000000000000 Binary files a/.gradle/buildOutputCleanup/buildOutputCleanup.lock and /dev/null differ diff --git a/.gradle/buildOutputCleanup/cache.properties b/.gradle/buildOutputCleanup/cache.properties deleted file mode 100644 index 44aa1b1b004c00c3ca26d0558f1cac2374def7cc..0000000000000000000000000000000000000000 --- a/.gradle/buildOutputCleanup/cache.properties +++ /dev/null @@ -1,2 +0,0 @@ -#Thu Nov 03 08:22:32 CET 2022 -gradle.version=7.4 diff --git a/.gradle/buildOutputCleanup/outputFiles.bin b/.gradle/buildOutputCleanup/outputFiles.bin deleted file mode 100644 index 22247e075c97d900294472751d70aa6810330c43..0000000000000000000000000000000000000000 Binary files a/.gradle/buildOutputCleanup/outputFiles.bin and /dev/null differ diff --git a/.gradle/vcs-1/gc.properties b/.gradle/vcs-1/gc.properties deleted file mode 100644 index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000 diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 13566b81b018ad684f3a35fee301741b2734c8f4..0000000000000000000000000000000000000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -# Default ignored files -/shelf/ -/workspace.xml -# Editor-based HTTP Client requests -/httpRequests/ -# Datasource local storage ignored files -/dataSources/ -/dataSources.local.xml diff --git a/.idea/compiler.xml b/.idea/compiler.xml deleted file mode 100644 index 659bf43190d1afbfe1907e2e5e2bd75ad761dfb3..0000000000000000000000000000000000000000 --- a/.idea/compiler.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="CompilerConfiguration"> - <bytecodeTargetLevel target="16" /> - </component> -</project> \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index ba1ec5c7e20c12eb5b893684698eab151c4a8803..0000000000000000000000000000000000000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,16 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="GradleSettings"> - <option name="linkedExternalProjectsSettings"> - <GradleProjectSettings> - <option name="distributionType" value="DEFAULT_WRAPPED" /> - <option name="externalProjectPath" value="$PROJECT_DIR$" /> - <option name="modules"> - <set> - <option value="$PROJECT_DIR$" /> - </set> - </option> - </GradleProjectSettings> - </option> - </component> -</project> \ No newline at end of file diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml deleted file mode 100644 index fdc392fe877c32ab51d532fa67f65ff2e75e9061..0000000000000000000000000000000000000000 --- a/.idea/jarRepositories.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="RemoteRepositoriesConfiguration"> - <remote-repository> - <option name="id" value="central" /> - <option name="name" value="Maven Central repository" /> - <option name="url" value="https://repo1.maven.org/maven2" /> - </remote-repository> - <remote-repository> - <option name="id" value="jboss.community" /> - <option name="name" value="JBoss Community repository" /> - <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" /> - </remote-repository> - <remote-repository> - <option name="id" value="MavenRepo" /> - <option name="name" value="MavenRepo" /> - <option name="url" value="https://repo.maven.apache.org/maven2/" /> - </remote-repository> - </component> -</project> \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 3db7abf849bb2eb6f6e84f40abd01c1aba9033b5..0000000000000000000000000000000000000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,10 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="ExternalStorageConfigurationManager" enabled="true" /> - <component name="FrameworkDetectionExcludesConfiguration"> - <file type="web" url="file://$PROJECT_DIR$" /> - </component> - <component name="ProjectRootManager" version="2" languageLevel="JDK_16" default="true" project-jdk-name="corretto-16" project-jdk-type="JavaSDK"> - <output url="file://$PROJECT_DIR$/out" /> - </component> -</project> \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7f4cb416c083d265558da75d457237d671..0000000000000000000000000000000000000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project version="4"> - <component name="VcsDirectoryMappings"> - <mapping directory="$PROJECT_DIR$" vcs="Git" /> - </component> -</project> \ No newline at end of file diff --git a/build.gradle b/build.gradle index 10ee756a7c605e5a18cf5342e0a07c5c64e02b83..f51eff276a6ecca48a4999b7ecb22f85c5c22429 100644 --- a/build.gradle +++ b/build.gradle @@ -1,26 +1,34 @@ plugins { + id("com.github.johnrengelman.shadow") version "8.1.1" id 'application' - id "org.openjfx.javafxplugin" version "0.0.10" + id 'java' + id "org.openjfx.javafxplugin" version "0.1.0" } javafx { - version = "17" - modules = [ 'javafx.controls', 'javafx.fxml' ] + version = "21" + modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.graphics'] } repositories { mavenCentral() } + dependencies { - testImplementation group: 'junit', name: 'junit', version: '4.12' - testImplementation 'org.hamcrest:hamcrest-library:1.3' + testRuntimeOnly("org.junit.platform:junit-platform-launcher") + testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine") + testImplementation("org.assertj:assertj-core:3.24.2") + testImplementation("org.junit.jupiter:junit-jupiter:5.10.0") } -mainClassName = "App" - - test { useJUnitPlatform() } + +application { + mainClass.set("app.SimulatorMain") +} + + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 41dfb87909a877d96c3af4adccce4c7a301b55a2..1e2fbf0d45879712d4e3ca644375f6109ad81740 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/settings.gradle b/settings.gradle index 634fa3e8dee539db93732d9ac0b186c7290dee54..5d3f861d10877d6653e10fdd716089a5ceffa7d4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,4 @@ -rootProject.name = 'ff' +rootProject.name = 'firefighter' +include 'src:main:aapp' +findProject(':src:main:aapp')?.name = 'aapp' diff --git a/src/main/java/App.java b/src/main/java/App.java deleted file mode 100644 index 10dda1d5b5dac2118958e64a64772f18e7258f18..0000000000000000000000000000000000000000 --- a/src/main/java/App.java +++ /dev/null @@ -1,51 +0,0 @@ -import javafx.application.Application; -import javafx.scene.Group; -import javafx.scene.Scene; -import javafx.scene.control.Button; -import javafx.scene.layout.HBox; -import javafx.scene.layout.VBox; -import javafx.stage.Stage; - -import static java.lang.Thread.sleep; - - -public class App extends Application { - - public static void main(String[] args) { - launch(args); - } - - @Override - public void start(Stage primaryStage) throws InterruptedException { - Group root = new Group(); - Button restart = new Button("Restart"); - VBox buttons = new VBox(); - HBox total = new HBox(); - Grid grid = new Grid(1000,1000,20,20); - - root.getChildren().add(total); - total.getChildren().add(buttons); - total.getChildren().add(grid); - buttons.getChildren().add(restart); - restart.setOnMouseClicked(grid::restart); - primaryStage.setScene(new Scene(root)); - primaryStage.show(); - grid.repaint(); - new Thread(new Runnable() { - @Override - public void run() { - while(true){ - try { - sleep(50); - grid.model.activation(); - grid.repaint(); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - - } - }).start(); - } -} - diff --git a/src/main/java/Grid.java b/src/main/java/Grid.java deleted file mode 100644 index 23fb50d23d9ba0dd308def943a81d8fe898d40a9..0000000000000000000000000000000000000000 --- a/src/main/java/Grid.java +++ /dev/null @@ -1,60 +0,0 @@ -import javafx.scene.canvas.Canvas; -import javafx.scene.canvas.GraphicsContext; -import javafx.scene.input.MouseEvent; -import javafx.scene.paint.Color; - -public class Grid extends Canvas{ - int width, height, colCount, rowCount; - Model model; - - - public Grid(int width, int height, int colCount, int rowCount) { - super(width,height); - this.width = width; - this.height = height; - this.colCount = colCount; - this.rowCount = rowCount; - setFocusTraversable(true); - setOnMousePressed(this::mousePressed); - model = new Model(this); - model.initialisation(3,8); - } - - public void restart(MouseEvent mouseEvent){ - model = new Model(this); - model.initialisation(3,6); - getGraphicsContext2D().clearRect(0,0,width,height); - repaint(); - } - private void mousePressed(MouseEvent mouseEvent) { - model.activation(); - repaint(); - /*double x = mouseEvent.getX(); - double y = mouseEvent.getY(); - model.click((int)x*rowCount/height,(int)y*colCount/width);*/ - } - - void repaint(){ - for(int col=0; col<colCount; col++) - getGraphicsContext2D().strokeLine(0, col*width/colCount,height, col*width/colCount); - for(int row=0; row<rowCount;row++) - getGraphicsContext2D().strokeLine(row*height/rowCount,0,row*height/rowCount,width); - - } - - void paint(int row, int col){ - getGraphicsContext2D().setFill(Color.WHITE); - getGraphicsContext2D().fillRect(row*height/rowCount,col*width/colCount,height/rowCount,width/colCount); - } - - public void paintFF(int row, int col) { - getGraphicsContext2D().setFill(Color.BLUE); - getGraphicsContext2D().fillRect(row*height/rowCount,col*width/colCount,height/rowCount,width/colCount); - } - - public void paintFire(int row, int col) { - getGraphicsContext2D().setFill(Color.RED); - getGraphicsContext2D().fillRect(row*height/rowCount,col*width/colCount,height/rowCount,width/colCount); - } - -} \ No newline at end of file diff --git a/src/main/java/Model.java b/src/main/java/Model.java deleted file mode 100644 index f23d0d13c6a3e847962201dc7c72fd5dedc0a249..0000000000000000000000000000000000000000 --- a/src/main/java/Model.java +++ /dev/null @@ -1,105 +0,0 @@ -import java.util.*; - - -public class Model { - Grid grid; - int colCount, rowCount; - List<Position> firefighters = new ArrayList<>(); - Set<Position> fires = new HashSet<>(); - List<Position> ffNewPositions; - int step = 0; - - public Model(Grid grid) { - this.grid = grid; - colCount = grid.colCount; - rowCount = grid.rowCount; - } - - - public void initialisation(int fireNumber, int fireFighterNumber){ - for(int index=0; index<fireNumber;index++) - fires.add(randomPosition()); - for(int index=0; index<fireFighterNumber;index++) - firefighters.add(randomPosition()); - } - - private Position randomPosition() { - return new Position((int) (Math.random()*rowCount), (int) (Math.random()*colCount)); - } - - - public void activation(){ - ffNewPositions = new ArrayList<>(); - for(Position ff : firefighters){ - Position newPosition = activateFirefighter(ff); - grid.paint(ff.row,ff.col); - grid.paintFF(newPosition.row, newPosition.col); - ffNewPositions.add(newPosition); - } - firefighters = ffNewPositions; - if(step%2==0){ - List<Position> newFires = new ArrayList<>(); - for(Position fire : fires){ - newFires.addAll(activateFire(fire)); - } - for(Position newFire : newFires) - grid.paintFire(newFire.row, newFire.col); - - fires.addAll(newFires);} - step++; - - } - - private List<Position> activateFire(Position position) { - return next(position); - } - - - - private Position activateFirefighter(Position position) { - Position randomPosition = aStepTowardFire(position); - //next(position).get((int) (Math.random()*next(position).size())); - List<Position> nextFires = next(randomPosition).stream().filter(fires::contains).toList(); - extinguish(randomPosition); - for (Position fire : nextFires) - extinguish(fire); - return randomPosition; - } - - private void extinguish(Position position) { - fires.remove(position); - grid.paint(position.row, position.col); - } - - private List<Position> next(Position position){ - List<Position> list = new ArrayList<>(); - if(position.row>0) list.add(new Position(position.row-1, position.col)); - if(position.col>0) list.add(new Position(position.row, position.col-1)); - if(position.row<rowCount-1) list.add(new Position(position.row+1, position.col)); - if(position.col<colCount-1) list.add(new Position(position.row, position.col+1)); - return list; - } - - private Position aStepTowardFire(Position position){ - Queue<Position> toVisit = new LinkedList<>(); - Set<Position> seen = new HashSet<>(); - HashMap<Position,Position> firstMove = new HashMap<>(); - toVisit.addAll(next(position)); - for(Position initialMove : toVisit) - firstMove.put(initialMove,initialMove); - while(!toVisit.isEmpty()){ - Position current = toVisit.poll(); - if(fires.contains(current)) - return firstMove.get(current); - for(Position adjacent : next(current)){ - if(seen.contains(adjacent)) continue; - toVisit.add(adjacent); - seen.add(adjacent); - firstMove.put(adjacent, firstMove.get(current)); - } - } - return position; - } - - public record Position(int row, int col){} -} \ No newline at end of file diff --git a/src/main/java/app/SimulatorApplication.java b/src/main/java/app/SimulatorApplication.java new file mode 100644 index 0000000000000000000000000000000000000000..e88b0e9856086cc6f53f2782175393dca6365eb6 --- /dev/null +++ b/src/main/java/app/SimulatorApplication.java @@ -0,0 +1,59 @@ +package app; + +import controller.Controller; +import javafx.application.Platform; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.stage.Stage; + +import java.io.IOException; +import java.net.URL; + +public class SimulatorApplication extends javafx.application.Application { + private static final String VIEW_RESOURCE_PATH = "/view/view.fxml"; + private static final String APP_NAME = "Firefighter simulator"; + private static final int ROW_COUNT = 20; + private static final int COLUMN_COUNT = 20; + private static final int BOX_WIDTH = 50; + private static final int BOX_HEIGHT = 50; + public static final int INITIAL_FIRE_COUNT = 3; + public static final int INITIAL_FIREFIGHTER_COUNT = 6; + + private Stage primaryStage; + private Parent view; + private void initializePrimaryStage(Stage primaryStage) { + this.primaryStage = primaryStage; + this.primaryStage.setTitle(APP_NAME); + this.primaryStage.setOnCloseRequest(event -> Platform.exit()); + this.primaryStage.setResizable(true); + this.primaryStage.sizeToScene(); + } + + @Override + public void start(Stage primaryStage) throws IOException { + initializePrimaryStage(primaryStage); + initializeView(); + showScene(); + } + + private void initializeView() throws IOException { + FXMLLoader loader = new FXMLLoader(); + URL location = SimulatorApplication.class.getResource(VIEW_RESOURCE_PATH); + loader.setLocation(location); + view = loader.load(); + Controller controller = loader.getController(); + controller.initialize(BOX_WIDTH, BOX_HEIGHT, COLUMN_COUNT, ROW_COUNT, + INITIAL_FIRE_COUNT, INITIAL_FIREFIGHTER_COUNT); + } + + private void showScene() { + Scene scene = new Scene(view); + primaryStage.setScene(scene); + primaryStage.show(); + } + + public static void main(String[] args) { + launch(args); + } +} diff --git a/src/main/java/app/SimulatorMain.java b/src/main/java/app/SimulatorMain.java new file mode 100644 index 0000000000000000000000000000000000000000..f2e49f262ebec4136cf57d3b50fea3987b3b69fc --- /dev/null +++ b/src/main/java/app/SimulatorMain.java @@ -0,0 +1,7 @@ +package app; + +public class SimulatorMain { + public static void main(String[] args){ + SimulatorApplication.main(args); + } +} diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java new file mode 100644 index 0000000000000000000000000000000000000000..2a60897c6eb8ba847cb8589840c16a0f175ce0a3 --- /dev/null +++ b/src/main/java/controller/Controller.java @@ -0,0 +1,141 @@ +package controller; + +import javafx.animation.Animation; +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.util.Duration; +import javafx.util.Pair; +import model.Board; +import model.ModelElement; +import model.FirefighterBoard; +import util.Position; +import view.Grid; +import view.ViewElement; + +import java.util.ArrayList; +import java.util.List; + +import static java.util.Objects.requireNonNull; + +public class Controller { + + public static final int PERIOD_IN_MILLISECONDS = 50; + @FXML + public Button restartButton; + @FXML + public Button oneStepButton; + @FXML + public Label generationNumberLabel; + @FXML + private ToggleButton pauseToggleButton; + @FXML + private ToggleButton playToggleButton; + @FXML + private Grid<ViewElement> grid; + private Timeline timeline; + private Board<List<ModelElement>> board; + + @FXML + private void initialize() { + initializePlayAndPauseToggleButtons(); + initializeTimeline(); + } + + private void initializePlayAndPauseToggleButtons() { + ToggleGroup toggleGroup = new PersistentToggleGroup(); + toggleGroup.getToggles().addAll(playToggleButton, pauseToggleButton); + pauseToggleButton.setSelected(true); + } + + private void setModel(FirefighterBoard firefighterBoard) { + this.board = requireNonNull(firefighterBoard, "firefighter.model is null"); + } + + private void updateBoard(){ + List<Position> updatedPositions = board.updateToNextGeneration(); + List<Pair<Position, ViewElement>> updatedSquares = new ArrayList<>(); + for(Position updatedPosition : updatedPositions){ + List<ModelElement> squareState = board.getState(updatedPosition); + ViewElement viewElement = getViewElement(squareState); + updatedSquares.add(new Pair<>(updatedPosition, viewElement)); + } + grid.repaint(updatedSquares); + updateGenerationLabel(board.stepNumber()); + } + + private void repaintGrid(){ + int columnCount = board.columnCount(); + int rowCount = board.rowCount(); + ViewElement[][] viewElements = new ViewElement[rowCount][columnCount]; + for(int column = 0; column < columnCount; column++) + for(int row = 0; row < rowCount; row++) + viewElements[row][column] = getViewElement(board.getState(new Position(row, column))); + grid.repaint(viewElements); + updateGenerationLabel(board.stepNumber()); + } + + private ViewElement getViewElement(List<ModelElement> squareState) { + if(squareState.contains(ModelElement.FIREFIGHTER)){ + return ViewElement.FIREFIGHTER; + } + if (squareState.contains(ModelElement.FIRE)){ + return ViewElement.FIRE; + } + return ViewElement.EMPTY; + } + + private void initializeTimeline() { + Duration duration = new Duration(Controller.PERIOD_IN_MILLISECONDS); + EventHandler<ActionEvent> eventHandler = + event -> updateBoard(); + KeyFrame keyFrame = new KeyFrame(duration, eventHandler); + timeline = new Timeline(keyFrame); + timeline.setCycleCount(Animation.INDEFINITE); + } + + public void play() { + timeline.play(); + } + + public void pause() { + timeline.pause(); + } + + public void pauseToggleButtonAction() { + this.pause(); + } + + public void playToggleButtonAction() { + this.play(); + } + + public void restartButtonAction() { + this.pause(); + board.reset(); + pauseToggleButton.setSelected(true); + repaintGrid(); + } + + public void initialize(int squareWidth, int squareHeight, int columnCount, + int rowCount, int initialFireCount, int initialFirefighterCount) { + grid.setDimensions(columnCount, rowCount, squareWidth, squareHeight); + this.setModel(new FirefighterBoard(columnCount, rowCount, initialFireCount, initialFirefighterCount)); + repaintGrid(); + } + + public void oneStepButtonAction() { + this.pause(); + updateBoard(); + } + + private void updateGenerationLabel(int value){ + generationNumberLabel.setText(Integer.toString(value)); + } +} \ No newline at end of file diff --git a/src/main/java/controller/PersistentToggleGroup.java b/src/main/java/controller/PersistentToggleGroup.java new file mode 100644 index 0000000000000000000000000000000000000000..7c2c4b5c79d6ff65e0bfbe53c2fb7e9fd5944a1b --- /dev/null +++ b/src/main/java/controller/PersistentToggleGroup.java @@ -0,0 +1,34 @@ +package controller; + +import javafx.collections.ListChangeListener.Change; +import javafx.scene.control.Toggle; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.input.MouseEvent; + +/** + * An extension of {@link ToggleGroup} that ensures that a {@link Toggle} in a group must always be + * selected. + * + */ +class PersistentToggleGroup extends ToggleGroup { + + /** + * Creates a new {@code PersistentToggleGroup}. + */ + PersistentToggleGroup() { + getToggles().addListener((Change<? extends Toggle> change) -> { + while (change.next()) { + for (Toggle toggle : change.getAddedSubList()) { + ToggleButton toggleButton = (ToggleButton) toggle; + toggleButton.addEventFilter(MouseEvent.MOUSE_RELEASED, mouseEvent -> { + if (toggleButton.equals(getSelectedToggle())) { + mouseEvent.consume(); + } + }); + } + } + }); + } + +} diff --git a/src/main/java/model/Board.java b/src/main/java/model/Board.java new file mode 100644 index 0000000000000000000000000000000000000000..bb089a41406d57b0b2fe3d43b3c040cbd640b6b9 --- /dev/null +++ b/src/main/java/model/Board.java @@ -0,0 +1,65 @@ +package model; + +import util.Position; + +import java.util.List; + +/** + * This interface represents a generic board for modeling various state-based systems. + * + * @param <S> The type of state represented on the board. + */ +public interface Board<S> { + + /** + * Get the state of the board at a specific position. + * + * @param position The position on the board for which to retrieve the state. + * @return The state at the specified position. + */ + S getState(Position position); + + /** + * Set the state of a specific position on the board to the specified state. + * + * @param state The state to set for the given position. + * @param position The position on the board for which to set the state. + */ + void setState(S state, Position position); + + /** + * Get the number of rows in the board. + * + * @return The number of rows in the board. + */ + int rowCount(); + + /** + * Get the number of columns in the board. + * + * @return The number of columns in the board. + */ + int columnCount(); + + /** + * Update the board to its next generation or state. This method may modify the + * internal state of the board and return a list of positions that have changed + * during the update. + * + * @return A list of positions that have changed during the update. + */ + List<Position> updateToNextGeneration(); + + /** + * Reset the board to its initial state. + */ + void reset(); + + /** + * Get the current step number or generation of the board. + * + * @return The current step number or generation. + */ + int stepNumber(); +} + diff --git a/src/main/java/model/FirefighterBoard.java b/src/main/java/model/FirefighterBoard.java new file mode 100644 index 0000000000000000000000000000000000000000..c0bd67cc4f444a33c41551779b11f3d619412bef --- /dev/null +++ b/src/main/java/model/FirefighterBoard.java @@ -0,0 +1,147 @@ +package model; + +import util.Position; + +import java.util.*; + + +public class FirefighterBoard implements Board<List<ModelElement>> { + private final int columnCount; + private final int rowCount; + private final int initialFireCount; + private final int initialFirefighterCount; + private final TargetStrategy targetStrategy = new TargetStrategy(); + private List<Position> firefighterPositions; + private Set<Position> firePositions; + private Map<Position, List<Position>> neighbors = new HashMap(); + private final Position[][] positions; + private int step = 0; + private final Random randomGenerator = new Random(); + + public FirefighterBoard(int columnCount, int rowCount, int initialFireCount, int initialFirefighterCount) { + this.columnCount = columnCount; + this.rowCount = rowCount; + this.positions = new Position[rowCount][columnCount]; + for (int column = 0; column < columnCount; column++) + for (int row = 0; row < rowCount; row++) + positions[row][column] = new Position(row, column); + for (int column = 0; column < columnCount; column++) + for (int row = 0; row < rowCount; row++) { + List<Position> list = new ArrayList<>(); + if (row > 0) list.add(positions[row - 1][column]); + if (column > 0) list.add(positions[row][column - 1]); + if (row < rowCount - 1) list.add(positions[row + 1][column]); + if (column < columnCount - 1) list.add(positions[row][column + 1]); + neighbors.put(positions[row][column], list); + } + this.initialFireCount = initialFireCount; + this.initialFirefighterCount = initialFirefighterCount; + initializeElements(); + } + + public void initializeElements() { + firefighterPositions = new ArrayList<>(); + firePositions = new HashSet<>(); + for (int index = 0; index < initialFireCount; index++) + firePositions.add(randomPosition()); + for (int index = 0; index < initialFirefighterCount; index++) + firefighterPositions.add(randomPosition()); + } + + private Position randomPosition() { + return new Position(randomGenerator.nextInt(rowCount), randomGenerator.nextInt(columnCount)); + } + + @Override + public List<ModelElement> getState(Position position) { + List<ModelElement> result = new ArrayList<>(); + for (Position firefighterPosition : firefighterPositions) + if (firefighterPosition.equals(position)) + result.add(ModelElement.FIREFIGHTER); + if (firePositions.contains(position)) + result.add(ModelElement.FIRE); + return result; + } + + @Override + public int rowCount() { + return rowCount; + } + + @Override + public int columnCount() { + return columnCount; + } + + public List<Position> updateToNextGeneration() { + List<Position> modifiedPositions = updateFirefighters(); + modifiedPositions.addAll(updateFires()); + step++; + return modifiedPositions; + } + + private List<Position> updateFires() { + List<Position> modifiedPositions = new ArrayList<>(); + if (step % 2 == 0) { + List<Position> newFirePositions = new ArrayList<>(); + for (Position fire : firePositions) { + newFirePositions.addAll(neighbors.get(fire)); + } + firePositions.addAll(newFirePositions); + modifiedPositions.addAll(newFirePositions); + } + return modifiedPositions; + + } + + @Override + public int stepNumber() { + return step; + } + + private List<Position> updateFirefighters() { + List<Position> modifiedPosition = new ArrayList<>(); + List<Position> firefighterNewPositions = new ArrayList<>(); + for (Position firefighterPosition : firefighterPositions) { + Position newFirefighterPosition = + targetStrategy.neighborClosestToFire(firefighterPosition, + firePositions, neighbors); + firefighterNewPositions.add(newFirefighterPosition); + extinguish(newFirefighterPosition); + modifiedPosition.add(firefighterPosition); + modifiedPosition.add(newFirefighterPosition); + List<Position> neighborFirePositions = neighbors.get(newFirefighterPosition).stream() + .filter(firePositions::contains).toList(); + for (Position firePosition : neighborFirePositions) + extinguish(firePosition); + modifiedPosition.addAll(neighborFirePositions); + } + firefighterPositions = firefighterNewPositions; + return modifiedPosition; + } + + @Override + public void reset() { + step = 0; + initializeElements(); + } + + private void extinguish(Position position) { + firePositions.remove(position); + } + + + @Override + public void setState(List<ModelElement> state, Position position) { + firePositions.remove(position); + for (; ; ) { + if (!firefighterPositions.remove(position)) break; + } + for (ModelElement element : state) { + switch (element) { + case FIRE -> firePositions.add(position); + case FIREFIGHTER -> firefighterPositions.add(position); + } + } + } +} \ No newline at end of file diff --git a/src/main/java/model/ModelElement.java b/src/main/java/model/ModelElement.java new file mode 100644 index 0000000000000000000000000000000000000000..759eee5e54c3a39472d8f7defbbbe6a2b67b8f00 --- /dev/null +++ b/src/main/java/model/ModelElement.java @@ -0,0 +1,5 @@ +package model; + +public enum ModelElement { + FIREFIGHTER, FIRE +} diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java new file mode 100644 index 0000000000000000000000000000000000000000..4c36d97709b342e457203c75d081fc5cc1955c0f --- /dev/null +++ b/src/main/java/module-info.java @@ -0,0 +1,8 @@ +module firefighter { + requires javafx.controls; + requires javafx.fxml; + requires javafx.graphics; + opens controller to javafx.fxml; + exports app; + opens app to javafx.fxml; +} diff --git a/src/main/java/util/Position.java b/src/main/java/util/Position.java new file mode 100644 index 0000000000000000000000000000000000000000..31dc4c1fb6a04b4e96649e133d1d116120d34683 --- /dev/null +++ b/src/main/java/util/Position.java @@ -0,0 +1,5 @@ +package util; + +public record Position(int row, int column) { + +} diff --git a/src/main/java/util/TargetStrategy.java b/src/main/java/util/TargetStrategy.java new file mode 100644 index 0000000000000000000000000000000000000000..583186787d4d8d47173fbe61cc424f2f7ee384c9 --- /dev/null +++ b/src/main/java/util/TargetStrategy.java @@ -0,0 +1,35 @@ +package model; + +import util.Position; + +import java.util.*; + +public class TargetStrategy { + + + /** + * @param position current position. + * @param targets positions that are targeted. + * @return the position next to the current position that is on the path to the closest target. + */ + Position neighborClosestToFire(Position position, Collection<Position> targets, + Map<Position,List<Position>>neighbors) { + Set<Position> seen = new HashSet<Position>(); + HashMap<Position, Position> firstMove = new HashMap<Position, Position>(); + Queue<Position> toVisit = new LinkedList<Position>(neighbors.get(position)); + for (Position initialMove : toVisit) + firstMove.put(initialMove, initialMove); + while (!toVisit.isEmpty()) { + Position current = toVisit.poll(); + if (targets.contains(current)) + return firstMove.get(current); + for (Position adjacent : neighbors.get(current)) { + if (seen.contains(adjacent)) continue; + toVisit.add(adjacent); + seen.add(adjacent); + firstMove.put(adjacent, firstMove.get(current)); + } + } + return position; + } +} \ No newline at end of file diff --git a/src/main/java/view/FirefighterGrid.java b/src/main/java/view/FirefighterGrid.java new file mode 100644 index 0000000000000000000000000000000000000000..4c9041f034ec9a4eb07ce4334de237f1e99ccdc9 --- /dev/null +++ b/src/main/java/view/FirefighterGrid.java @@ -0,0 +1,99 @@ +package view; + +import javafx.scene.canvas.Canvas; +import javafx.scene.paint.Color; +import javafx.util.Pair; +import util.Position; + +import java.util.List; + +public class FirefighterGrid extends Canvas implements Grid<ViewElement>{ + + private void paintElementAtPosition(ViewElement element, Position position) { + paintBox(position.row(), position.column(), element.color); + } + private int boxWidth; + private int boxHeight; + private int columnCount; + private int rowCount; + + @Override + public void repaint(List<Pair<Position, ViewElement>> positionedElements) { + clear(positionedElements); + paint(positionedElements); + paintLines(); + } + + private void clear(List<Pair<Position, ViewElement>> positionedElements) { + for (Pair<Position, ViewElement> positionElement : positionedElements) { + Position position = positionElement.getKey(); + clearBox(position.row(), position.column()); + } + } + + private void paint(List<Pair<Position, ViewElement>> positionedElements) { + for(Pair<Position, ViewElement> pair : positionedElements){ + paintElementAtPosition(pair.getValue(), pair.getKey()); + } + } + + @Override + public void repaint(ViewElement[][] elements) { + clear(); + paint(elements); + paintLines(); + } + + private void clear() { + getGraphicsContext2D().clearRect(0,0,getWidth(), getHeight()); + } + + private void paint(ViewElement[][] elements) { + for(int column = 0; column < columnCount; column++) + for(int row = 0; row < rowCount; row++){ + paintElementAtPosition(elements[row][column], new Position(row, column)); + } + } + + public int columnCount() { + return columnCount; + } + + public int rowCount() { + return rowCount; + } + + @Override + public void setDimensions(int columnCount, int rowCount, int boxWidth, int boxHeight) { + this.boxWidth = boxWidth; + this.boxHeight = boxHeight; + this.columnCount = columnCount; + this.rowCount = rowCount; + super.setWidth(boxWidth * columnCount); + super.setHeight(boxHeight * rowCount); + } + + private void paintLines(){ + paintHorizontalLines(); + paintVerticalLines(); + } + + private void paintVerticalLines() { + for(int column = 0; column < columnCount; column++) + getGraphicsContext2D().strokeLine(column * boxWidth, 0,column * boxWidth, getHeight()); + } + + private void paintHorizontalLines() { + for(int row = 0; row < rowCount; row++) + getGraphicsContext2D().strokeLine(0, row * boxHeight, getWidth(), row * boxHeight); + } + + private void paintBox(int row, int column, Color color){ + getGraphicsContext2D().setFill(color); + getGraphicsContext2D().fillRect(column * boxWidth,row * boxHeight, boxWidth, boxHeight); + } + + private void clearBox(int row, int column){ + getGraphicsContext2D().clearRect(column * boxWidth,row * boxHeight, boxWidth, boxHeight); + } +} \ No newline at end of file diff --git a/src/main/java/view/Grid.java b/src/main/java/view/Grid.java new file mode 100644 index 0000000000000000000000000000000000000000..b95d59f622a86b41f2a41261b8b27aaf2e911dfb --- /dev/null +++ b/src/main/java/view/Grid.java @@ -0,0 +1,55 @@ +package view; + +import javafx.util.Pair; +import util.Position; + +import java.util.List; + +/** + * This interface represents a generic grid structure for displaying two-dimensional data. + * + * @param <E> The type of elements stored in the grid. + */ +public interface Grid<E> { + + /** + * Repaint the grid with a list of elements, each associated with their respective positions. + * + * @param elements A list of pairs, each containing a position and the element to be displayed at that position. + */ + void repaint(List<Pair<Position, E>> elements); + + /** + * Repaint the grid with a two-dimensional array of elements. The array's dimensions should match + * the row and column count of the grid. + * + * @param elements A two-dimensional array of elements to be displayed on the grid. + */ + void repaint(E[][] elements); + + /** + * Set the dimensions of the grid to the specified number of columns, number of rows, square width, + * and square height. + * + * @param columnCount The new number of columns in the grid. + * @param rowCount The new number of rows in the grid. + * @param squareWidth The width of each square within the grid. + * @param squareHeight The height of each square within the grid. + */ + void setDimensions(int columnCount, int rowCount, int squareWidth, int squareHeight); + + /** + * Get the number of columns in the grid. + * + * @return The number of columns in the grid. + */ + int columnCount(); + + /** + * Get the number of rows in the grid. + * + * @return The number of rows in the grid. + */ + int rowCount(); +} + diff --git a/src/main/java/view/ViewElement.java b/src/main/java/view/ViewElement.java new file mode 100644 index 0000000000000000000000000000000000000000..ffb76112e1af543df5af41fa906082ef11be9967 --- /dev/null +++ b/src/main/java/view/ViewElement.java @@ -0,0 +1,11 @@ +package view; + +import javafx.scene.paint.Color; + +public enum ViewElement { + FIREFIGHTER(Color.BLUE), FIRE(Color.RED), EMPTY(Color.WHITE); + final Color color; + ViewElement(Color color) { + this.color = color; + } +} diff --git a/src/main/resources/view/DarkTheme.css b/src/main/resources/view/DarkTheme.css new file mode 100644 index 0000000000000000000000000000000000000000..46b78aaf90972dd435399466c9fd861ca73fc59f --- /dev/null +++ b/src/main/resources/view/DarkTheme.css @@ -0,0 +1,142 @@ +.background { + -fx-background-color: #1d1d1d; +} + +.label { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: white; + -fx-opacity: 0.6; +} + +.label-bright { + -fx-font-size: 11pt; + -fx-font-family: "Segoe UI Semibold"; + -fx-text-fill: white; + -fx-opacity: 1; +} + +.label-header { + -fx-font-size: 32pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-opacity: 1; +} + +.table-view { + -fx-base: #1d1d1d; + -fx-control-inner-background: #1d1d1d; + -fx-background-color: #1d1d1d; + -fx-table-cell-border-color: transparent; + -fx-table-header-border-color: transparent; + -fx-padding: 5; +} + +.table-view .column-header-background { + -fx-background-color: transparent; +} + +.table-view .column-header, .table-view .filler { + -fx-border-width: 0 0 1 0; + -fx-background-color: transparent; + -fx-border-color: + transparent + transparent + derive(-fx-base, 80%) + transparent; + -fx-border-insets: 0 10 1 0; +} + +.table-view .column-header .label { + -fx-font-size: 20pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-alignment: center-left; + -fx-opacity: 1; +} + +.table-view:focused .table-row-cell:filled:focused:selected { + -fx-background-color: -fx-focus-color; +} + +.split-pane:horizontal > .split-pane-divider { + -fx-border-color: transparent #1d1d1d transparent #1d1d1d; + -fx-background-color: transparent, derive(#1d1d1d,20%); +} + +.split-pane { + -fx-padding: 1 0 0 0; +} + +.menu-bar { + -fx-background-color: derive(#1d1d1d,20%); +} + +.context-menu { + -fx-background-color: derive(#1d1d1d,50%); +} + +.menu-bar .label { + -fx-font-size: 14pt; + -fx-font-family: "Segoe UI Light"; + -fx-text-fill: white; + -fx-opacity: 0.9; +} + +.menu .left-container { + -fx-background-color: black; +} + +.text-field { + -fx-font-size: 12pt; + -fx-font-family: "Segoe UI Semibold"; +} + +/* + * Metro style Push Button + * Author: Pedro Duque Vieira + * http://pixelduke.wordpress.com/2012/10/23/jmetro-windows-8-controls-on-java/ + */ +.button { + -fx-padding: 5 22 5 22; + -fx-border-color: #e2e2e2; + -fx-border-width: 2; + -fx-background-radius: 0; + -fx-background-color: #1d1d1d; + -fx-font-family: "Segoe UI", Helvetica, Arial, sans-serif; + -fx-font-size: 11pt; + -fx-text-fill: #d8d8d8; + -fx-background-insets: 0 0 0 0, 0, 1, 2; +} + +.button:hover { + -fx-background-color: #3a3a3a; +} + +.button:pressed, .button:default:hover:pressed { + -fx-background-color: white; + -fx-text-fill: #1d1d1d; +} + +.button:focused { + -fx-border-color: white, white; + -fx-border-width: 1, 1; + -fx-border-style: solid; + -fx-border-radius: 0, 0; + -fx-border-insets: 1 1 1 1, 0; +} + +.button:disabled, .button:default:disabled { + -fx-opacity: 0.4; + -fx-background-color: #1d1d1d; + -fx-text-fill: white; +} + +.button:default { + -fx-background-color: -fx-focus-color; + -fx-text-fill: #ffffff; +} + +.button:default:hover { + -fx-background-color: derive(-fx-focus-color,30%); +} \ No newline at end of file diff --git a/src/main/resources/view/view.fxml b/src/main/resources/view/view.fxml new file mode 100644 index 0000000000000000000000000000000000000000..336ffa315645baacbe25bf59171d2ff6d867b9c5 --- /dev/null +++ b/src/main/resources/view/view.fxml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8"?> + +<?import javafx.scene.control.Button?> +<?import javafx.scene.layout.HBox?> +<?import javafx.scene.layout.VBox?> +<?import view.FirefighterGrid?> + +<?import javafx.scene.control.ToggleButton?> +<?import javafx.scene.control.Separator?> +<?import javafx.scene.control.Label?> +<HBox styleClass="background" stylesheets="@DarkTheme.css" + xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml" + fx:controller="controller.Controller"> + <VBox> + <Separator maxHeight="-Infinity" maxWidth="-Infinity" + prefHeight="24.0" prefWidth="200.0"/> + <Label maxHeight="-Infinity" maxWidth="-Infinity" alignment="CENTER" prefHeight="24.0" prefWidth="200.0" + text="Generation number"/> + <Label fx:id="generationNumberLabel" alignment="CENTER" contentDisplay="TEXT_ONLY" + maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="24.0" prefWidth="200.0"/> + <Separator maxHeight="-Infinity" maxWidth="-Infinity" + prefHeight="24.0" prefWidth="200.0"/> + <Button fx:id="restartButton" maxHeight="-Infinity" maxWidth="-Infinity" + mnemonicParsing="false" onAction="#restartButtonAction" prefHeight="24.0" prefWidth="200.0" + text="Restart"/> + <Button fx:id="oneStepButton" maxHeight="-Infinity" maxWidth="-Infinity" + mnemonicParsing="false" onAction="#oneStepButtonAction" prefHeight="24.0" prefWidth="200.0" + text="One step"/> + <ToggleButton fx:id="playToggleButton" maxHeight="-Infinity" maxWidth="-Infinity" + mnemonicParsing="false" onAction="#playToggleButtonAction" prefHeight="24.0" + prefWidth="200.0" styleClass="button" text="Play"/> + <ToggleButton fx:id="pauseToggleButton" maxHeight="-Infinity" maxWidth="-Infinity" + mnemonicParsing="false" onAction="#pauseToggleButtonAction" prefHeight="24.0" + prefWidth="200.0" styleClass="button" text="Pause"/> + </VBox> + <FirefighterGrid fx:id="grid" + xmlns="http://javafx.com/javafx" + xmlns:fx="http://javafx.com/fxml"> + </FirefighterGrid> +</HBox> diff --git a/src/test/java/model/FirefighterBoardTest.java b/src/test/java/model/FirefighterBoardTest.java new file mode 100644 index 0000000000000000000000000000000000000000..25cc8dbca8acea698879df68a5006a179f281ecc --- /dev/null +++ b/src/test/java/model/FirefighterBoardTest.java @@ -0,0 +1,39 @@ +package model; + +import org.junit.jupiter.api.Test; +import util.Position; + +import java.util.List; + +import static org.assertj.core.api.Assertions.*; + +public class FirefighterBoardTest { + @Test + void testColumnCount(){ + Board<List<ModelElement>> board = new FirefighterBoard(20, 10, 1, 3); + assertThat(board.columnCount()).isEqualTo(20); + } + @Test + void testRowCount(){ + Board<List<ModelElement>> board = new FirefighterBoard(20, 10, 1, 3); + assertThat(board.rowCount()).isEqualTo(10); + } + @Test + void testStepNumber(){ + Board<List<ModelElement>> board = new FirefighterBoard(20, 10, 1, 3); + for(int index = 0; index < 10; index++){ + assertThat(board.stepNumber()).isEqualTo(index); + board.updateToNextGeneration(); + } + assertThat(board.stepNumber()).isEqualTo(10); + } + @Test + void testGetState_afterSet(){ + Board<List<ModelElement>> board = new FirefighterBoard(20, 10, 0, 0); + Position position = new Position(1,2); + assertThat(board.getState(position)).isEmpty(); + board.setState(List.of(ModelElement.FIRE), position); + assertThat(board.getState(position)).containsExactly(ModelElement.FIRE); + } + +} diff --git a/src/test/java/view/FirefighterGridTest.java b/src/test/java/view/FirefighterGridTest.java new file mode 100644 index 0000000000000000000000000000000000000000..4b45ebdca3b936b42c2b322b1294488341d180bb --- /dev/null +++ b/src/test/java/view/FirefighterGridTest.java @@ -0,0 +1,20 @@ +package view; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +public class FirefighterGridTest { + @Test + void testColumnCount(){ + Grid<ViewElement> grid = new FirefighterGrid(); + grid.setDimensions(20,10,10,10); + assertThat(grid.columnCount()).isEqualTo(20); + } + @Test + void testRowCount(){ + Grid<ViewElement> grid = new FirefighterGrid(); + grid.setDimensions(20,10,10,10); + assertThat(grid.rowCount()).isEqualTo(10); + } +}