Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision

Target

Select target project
  • m19023837/firefighter-starter-mansour-chadi-chahine-rami
  • r24025701/firefighterstarter
  • n24026202/firefighterstarter
  • couetoux.b/firefighterstarter
  • b23027938/firefighterstarter
  • z20039716/fire-fighter
  • a18023913/firefighterstarter
  • o22010261/firefighterstarter
  • b22015516/firefighterstarter
  • alaboure/firefighter-template
  • p21211679/firefighter-luis-parra-yanis-lounadi
  • v23014723/firefighter-project
  • k20014011/firefighter-template
  • m23022217/firefighter-template
  • p20006624/firefighter-template
  • l21221596/firefighter-template
  • s21232465/firefighter-template
  • y21224754/firefighter-template
  • w21225935/firefighter-template
  • h22023886/firefighter-template
  • b21221604/firefighter-template-boucenna-yacine-zeghar-mohamed-lamine
  • c23025119/firefighterstarter
  • b20031964/firefighterstarter
23 results
Select Git revision
Show changes
Commits on Source (112)
Showing
with 1207 additions and 302 deletions
...@@ -32,3 +32,10 @@ fabric.properties ...@@ -32,3 +32,10 @@ fabric.properties
.idea/caches/build_file_checksums.ser .idea/caches/build_file_checksums.ser
.gradle/
*.class
bin/
*.bin
*.lock
.gradle/8.10.2/executionHistory/executionHistory.bin
build/
\ No newline at end of file
.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: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
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<?import view.FirefighterGrid?>
<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" />
<ToggleButton fx:id="initFireFighter" maxHeight="-Infinity" maxWidth="-Infinity"
mnemonicParsing="false" onAction="#initializeFireFighter"
prefHeight="24.0" prefWidth="200.0"
styleClass="button" text="Start FireFighter" />
<ToggleButton fx:id="initVirus" maxHeight="-Infinity" maxWidth="-Infinity"
mnemonicParsing="false" onAction="#initializeDoctor"
prefHeight="24.0" prefWidth="200.0"
styleClass="button" text="Start Virus" />
<ToggleButton fx:id="initPfc" maxHeight="-Infinity" maxWidth="-Infinity"
mnemonicParsing="false" onAction="#initializePfc"
prefHeight="24.0" prefWidth="200.0"
styleClass="button" text="Start RockPaperCisor" />
</VBox>
<FirefighterGrid fx:id="grid"
xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml">
</FirefighterGrid>
</HBox>
diagrams/model.png

77.1 KiB

Patients :
- Interactions et mouvement : Se déplacent et interagissent socialement, augmentant ainsi les chances de transmission du virus. Certains peuvent être asymptomatiques.
- Réaction en cas de maladie : S'ils se rendent compte qu'ils sont malades, ils cherchent un docteur pour se faire soigner. Sinon, ils continuent leur vie normalement, pouvant transmettre le virus sans le savoir.
Virus :
- Mouvement et infection : Entité unique sur le plateau qui se déplace aléatoirement, infectant les patients lorsqu'il passe à proximité.
- Le virus infecte les patients lorsqu'il passe à proximité.
- Évolue tous les 30 tours pour donner un nouveau variant, lorsqu'un nouveau variant est détecté, les médecins mettent plus de temps à le soigner au début
Docteurs :
- Traitement des patients : Traitent les patients malades qui se trouvent dans des cases adjacentes à leur position.
- Statique : Ne se déplacent pas entre les tours, ce qui nécessite que les patients viennent à eux pour être soignés.
package app; package app;
import java.io.IOException;
import java.net.URL;
import controller.Controller; import controller.Controller;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.fxml.FXMLLoader; import javafx.fxml.FXMLLoader;
...@@ -7,21 +10,31 @@ import javafx.scene.Parent; ...@@ -7,21 +10,31 @@ import javafx.scene.Parent;
import javafx.scene.Scene; import javafx.scene.Scene;
import javafx.stage.Stage; import javafx.stage.Stage;
import java.io.IOException;
import java.net.URL;
public class SimulatorApplication extends javafx.application.Application { public class SimulatorApplication extends javafx.application.Application {
private static final String VIEW_RESOURCE_PATH = "/view/view.fxml"; private static final String VIEW_RESOURCE_PATH = "/view/view.fxml";
private static final String APP_NAME = "Firefighter simulator"; private static final String APP_NAME = "Firefighter simulator";
private static final int ROW_COUNT = 20; public static final int ROW_COUNT = 40;
private static final int COLUMN_COUNT = 20; public static final int COLUMN_COUNT = 40;
private static final int BOX_WIDTH = 50; public static final int BOX_WIDTH = 25;
private static final int BOX_HEIGHT = 50; public static final int BOX_HEIGHT = 25;
public static final int INITIAL_FIRE_COUNT = 3; public static final int INITIAL_FIRE_COUNT = 8;
public static final int INITIAL_FIREFIGHTER_COUNT = 6; public static final int INITIAL_FIREFIGHTER_COUNT = 12;
public static final int INITIAL_MOTORIZED_FIREFIGHTER_COUNT = 8;
public static final int INITIAL_CLOUD_COUNT = 8;
public static final int INITIAL_MOUNTAIN_COUNT= 6;
public static final int INITIAL_ROCKERTY_COUNT= 3;
public static final int TURNS_FOR_SPAWNING_AIRTANKER = 10;
public static final int PAPER_COUNT = 10;
public static final int ROCK_COUNT = 10;
public static final int CISOR_COUNT = 10;
public static final int DOCTOR_COUNT = 3;
public static final int PATIENT_COUNT = 70;
public static final int VIRUS_COUNT = 6;
public static String[] arguments;
private Stage primaryStage; private Stage primaryStage;
private Parent view; private Parent view;
private void initializePrimaryStage(Stage primaryStage) { private void initializePrimaryStage(Stage primaryStage) {
this.primaryStage = primaryStage; this.primaryStage = primaryStage;
this.primaryStage.setTitle(APP_NAME); this.primaryStage.setTitle(APP_NAME);
...@@ -31,7 +44,7 @@ public class SimulatorApplication extends javafx.application.Application { ...@@ -31,7 +44,7 @@ public class SimulatorApplication extends javafx.application.Application {
} }
@Override @Override
public void start(Stage primaryStage) throws IOException { public void start(@SuppressWarnings("exports") Stage primaryStage) throws IOException {
initializePrimaryStage(primaryStage); initializePrimaryStage(primaryStage);
initializeView(); initializeView();
showScene(); showScene();
...@@ -43,8 +56,28 @@ public class SimulatorApplication extends javafx.application.Application { ...@@ -43,8 +56,28 @@ public class SimulatorApplication extends javafx.application.Application {
loader.setLocation(location); loader.setLocation(location);
view = loader.load(); view = loader.load();
Controller controller = loader.getController(); Controller controller = loader.getController();
controller.initialize(BOX_WIDTH, BOX_HEIGHT, COLUMN_COUNT, ROW_COUNT, try{
INITIAL_FIRE_COUNT, INITIAL_FIREFIGHTER_COUNT);
if(SimulatorApplication.arguments == null){
SimulatorApplication.arguments = new String[1];
SimulatorApplication.arguments[0] = "firefighter";
System.out.println("No argument were provided, starting firefighter");
}
switch(SimulatorApplication.arguments[0]){
case "pfc":
controller.initializePfc();
break;
case "firefighter":
controller.initializeFireFighter();
break;
case "doctor":
controller.initializeDoctor();
break;
}
}catch(ArrayIndexOutOfBoundsException e){
controller.initializeFireFighter();
}
} }
private void showScene() { private void showScene() {
...@@ -54,6 +87,7 @@ public class SimulatorApplication extends javafx.application.Application { ...@@ -54,6 +87,7 @@ public class SimulatorApplication extends javafx.application.Application {
} }
public static void main(String[] args) { public static void main(String[] args) {
arguments = args;
launch(args); launch(args);
} }
} }
package controller; package controller;
import static java.util.Objects.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javafx.animation.Animation; import javafx.animation.Animation;
import javafx.animation.KeyFrame; import javafx.animation.KeyFrame;
import javafx.animation.Timeline; import javafx.animation.Timeline;
...@@ -13,16 +20,15 @@ import javafx.scene.control.ToggleGroup; ...@@ -13,16 +20,15 @@ import javafx.scene.control.ToggleGroup;
import javafx.util.Duration; import javafx.util.Duration;
import javafx.util.Pair; import javafx.util.Pair;
import model.Board; import model.Board;
import model.ModelElement; import model.EmptySquare;
import model.FirefighterBoard; import model.Entity;
import model.EntityFactory;
import model.Model;
import model.Square;
import util.Position; import util.Position;
import view.Grid; import view.Grid;
import view.ViewElement; import view.ViewElement;
import app.SimulatorApplication;
import java.util.ArrayList;
import java.util.List;
import static java.util.Objects.requireNonNull;
public class Controller { public class Controller {
...@@ -40,7 +46,8 @@ public class Controller { ...@@ -40,7 +46,8 @@ public class Controller {
@FXML @FXML
private Grid<ViewElement> grid; private Grid<ViewElement> grid;
private Timeline timeline; private Timeline timeline;
private Board<List<ModelElement>> board; private Board<Square> board;
private Model model;
@FXML @FXML
private void initialize() { private void initialize() {
...@@ -54,15 +61,16 @@ public class Controller { ...@@ -54,15 +61,16 @@ public class Controller {
pauseToggleButton.setSelected(true); pauseToggleButton.setSelected(true);
} }
private void setModel(FirefighterBoard firefighterBoard) { private void setModel(Model model) {
this.board = requireNonNull(firefighterBoard, "firefighter.model is null"); this.board = requireNonNull(model.getBoard(), "board is null");
this.model = model;
} }
private void updateBoard() { private void updateBoard() {
List<Position> updatedPositions = board.updateToNextGeneration(); List<Position> updatedPositions = model.updateToNextGeneration();
List<Pair<Position, ViewElement>> updatedSquares = new ArrayList<>(); List<Pair<Position, ViewElement>> updatedSquares = new ArrayList<>();
for (Position updatedPosition : updatedPositions) { for (Position updatedPosition : updatedPositions) {
List<ModelElement> squareState = board.getState(updatedPosition); Square squareState = board.getStates(updatedPosition);
ViewElement viewElement = getViewElement(squareState); ViewElement viewElement = getViewElement(squareState);
updatedSquares.add(new Pair<>(updatedPosition, viewElement)); updatedSquares.add(new Pair<>(updatedPosition, viewElement));
} }
...@@ -76,25 +84,24 @@ public class Controller { ...@@ -76,25 +84,24 @@ public class Controller {
ViewElement[][] viewElements = new ViewElement[rowCount][columnCount]; ViewElement[][] viewElements = new ViewElement[rowCount][columnCount];
for (int column = 0; column < columnCount; column++) for (int column = 0; column < columnCount; column++)
for (int row = 0; row < rowCount; row++) for (int row = 0; row < rowCount; row++)
viewElements[row][column] = getViewElement(board.getState(new Position(row, column))); viewElements[row][column] = getViewElement(board.getStates(new Position(row, column)));
grid.repaint(viewElements); grid.repaint(viewElements);
updateGenerationLabel(board.stepNumber()); updateGenerationLabel(board.stepNumber());
} }
private ViewElement getViewElement(List<ModelElement> squareState) { private ViewElement getViewElement(Square square) {
if(squareState.contains(ModelElement.FIREFIGHTER)){ if (!square.getEntities().isEmpty()) {
return ViewElement.FIREFIGHTER; for (Entity entity : square.getEntities()) {
if(entity instanceof EmptySquare)continue;
return entity.getViewElement();
} }
if (squareState.contains(ModelElement.FIRE)){
return ViewElement.FIRE;
} }
return ViewElement.EMPTY; return new ViewElement(square.getViewColor());
} }
private void initializeTimeline() { private void initializeTimeline() {
Duration duration = new Duration(Controller.PERIOD_IN_MILLISECONDS); Duration duration = new Duration(Controller.PERIOD_IN_MILLISECONDS);
EventHandler<ActionEvent> eventHandler = EventHandler<ActionEvent> eventHandler = event -> updateBoard();
event -> updateBoard();
KeyFrame keyFrame = new KeyFrame(duration, eventHandler); KeyFrame keyFrame = new KeyFrame(duration, eventHandler);
timeline = new Timeline(keyFrame); timeline = new Timeline(keyFrame);
timeline.setCycleCount(Animation.INDEFINITE); timeline.setCycleCount(Animation.INDEFINITE);
...@@ -119,14 +126,71 @@ public class Controller { ...@@ -119,14 +126,71 @@ public class Controller {
public void restartButtonAction() { public void restartButtonAction() {
this.pause(); this.pause();
board.reset(); board.reset();
System.gc();
pauseToggleButton.setSelected(true); pauseToggleButton.setSelected(true);
repaintGrid(); repaintGrid();
} }
public void initialize(int squareWidth, int squareHeight, int columnCount, public void initializeDoctor() {
int rowCount, int initialFireCount, int initialFirefighterCount) {
System.out.println("Initializing doctor virus patient");
int columnCount = SimulatorApplication.COLUMN_COUNT;
int rowCount = SimulatorApplication.ROW_COUNT;
int squareWidth = SimulatorApplication.BOX_WIDTH;
int squareHeight = SimulatorApplication.BOX_HEIGHT;
grid.setDimensions(columnCount, rowCount, squareWidth, squareHeight);
Map<EntityFactory, Integer> entityCounts = new HashMap<EntityFactory, Integer>();
entityCounts.put((pos, b) -> new model.doctorviruspatient.Patient(pos), SimulatorApplication.PATIENT_COUNT);
entityCounts.put((pos, b) -> new model.doctorviruspatient.Virus(pos), SimulatorApplication.VIRUS_COUNT);
entityCounts.put((pos, b) -> new model.doctorviruspatient.Doctor(pos), SimulatorApplication.DOCTOR_COUNT);
Model model = new model.doctorviruspatient.DoctorVirusPatientScenario(SimulatorApplication.COLUMN_COUNT, SimulatorApplication.ROW_COUNT, entityCounts);
this.setModel(model);
repaintGrid();
}
public void initializePfc() {
System.out.println("Initializing rock paper cisor");
int columnCount = SimulatorApplication.COLUMN_COUNT;
int rowCount = SimulatorApplication.ROW_COUNT;
int squareWidth = SimulatorApplication.BOX_WIDTH;
int squareHeight = SimulatorApplication.BOX_HEIGHT;
grid.setDimensions(columnCount, rowCount, squareWidth, squareHeight);
Map<EntityFactory, Integer> entityCounts = new HashMap<EntityFactory, Integer>();
entityCounts.put((pos, b) -> new model.rockpapercisor.Rock(pos), SimulatorApplication.ROCK_COUNT);
entityCounts.put((pos, b) -> new model.rockpapercisor.Paper(pos), SimulatorApplication.PAPER_COUNT);
entityCounts.put((pos, b) -> new model.rockpapercisor.Cisor(pos), SimulatorApplication.CISOR_COUNT);
Model model = new model.rockpapercisor.RockPaperCisorScenario(SimulatorApplication.COLUMN_COUNT, SimulatorApplication.ROW_COUNT, entityCounts);
this.setModel(model);
repaintGrid();
}
public void initializeFireFighter() {
System.out.println("Initializing firefighters");
int columnCount = SimulatorApplication.COLUMN_COUNT;
int rowCount = SimulatorApplication.ROW_COUNT;
int squareWidth = SimulatorApplication.BOX_WIDTH;
int squareHeight = SimulatorApplication.BOX_HEIGHT;
grid.setDimensions(columnCount, rowCount, squareWidth, squareHeight); grid.setDimensions(columnCount, rowCount, squareWidth, squareHeight);
this.setModel(new FirefighterBoard(columnCount, rowCount, initialFireCount, initialFirefighterCount));
Map<EntityFactory, Integer> entityCounts = new HashMap<EntityFactory, Integer>();
entityCounts.put((pos, b) -> new model.firefighterscenario.Fire(pos), SimulatorApplication.INITIAL_FIRE_COUNT);
entityCounts.put((pos, b) -> new model.firefighterscenario.FireFighter(pos,b), SimulatorApplication.INITIAL_FIREFIGHTER_COUNT);
entityCounts.put((pos, b) -> new model.firefighterscenario.MotorizedFireFighter(pos, b), SimulatorApplication.INITIAL_MOTORIZED_FIREFIGHTER_COUNT);
entityCounts.put((pos, b) -> new model.firefighterscenario.Cloud(pos, b), SimulatorApplication.INITIAL_CLOUD_COUNT);
entityCounts.put((pos, b) -> new model.firefighterscenario.Mountain(pos), SimulatorApplication.INITIAL_MOUNTAIN_COUNT);
entityCounts.put((pos, b) -> new model.firefighterscenario.Rockery(pos), SimulatorApplication.INITIAL_ROCKERTY_COUNT);
Model model = new model.firefighterscenario.FireFighterScenario(SimulatorApplication.COLUMN_COUNT, SimulatorApplication.ROW_COUNT, entityCounts);
this.setModel(model);
repaintGrid(); repaintGrid();
} }
......
package model; package model;
import util.Position;
import java.util.List; import java.util.List;
import java.util.Map;
import util.Position;
/** /**
* This interface represents a generic board for modeling various state-based systems. * This interface represents a generic board for modeling various state-based
* systems.
* *
* @param <S> The type of state represented on the board. * @param <S> The type of state represented on the board.
*/ */
...@@ -17,7 +19,7 @@ public interface Board<S> { ...@@ -17,7 +19,7 @@ public interface Board<S> {
* @param position The position on the board for which to retrieve the state. * @param position The position on the board for which to retrieve the state.
* @return The state at the specified position. * @return The state at the specified position.
*/ */
S getState(Position position); S getStates(Position position);
/** /**
* Set the state of a specific position on the board to the specified state. * Set the state of a specific position on the board to the specified state.
...@@ -25,7 +27,7 @@ public interface Board<S> { ...@@ -25,7 +27,7 @@ public interface Board<S> {
* @param state The state to set for the given position. * @param state The state to set for the given position.
* @param position The position on the board for which to set the state. * @param position The position on the board for which to set the state.
*/ */
void setState(S state, Position position); void setSquare(S square);
/** /**
* Get the number of rows in the board. * Get the number of rows in the board.
...@@ -41,15 +43,6 @@ public interface Board<S> { ...@@ -41,15 +43,6 @@ public interface Board<S> {
*/ */
int columnCount(); 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. * Reset the board to its initial state.
*/ */
...@@ -61,5 +54,27 @@ public interface Board<S> { ...@@ -61,5 +54,27 @@ public interface Board<S> {
* @return The current step number or generation. * @return The current step number or generation.
*/ */
int stepNumber(); int stepNumber();
}
public int getStepNumber();
// Le booléen replaceState permet de forcer le remplacement des cases vides
public void setSquare(S square, boolean replaceStates);
public boolean doesPositionExist(Position position);
public void clearCaseFrom(Entity entity, Position position);
public Position getNearestEntity(Position fromPos, Class<?> entityType, List<Entity> exclusionList);
public boolean doesSquareContainEntity(Position squarePos, Class<?> entityType);
public void addEntityAtSquare(Entity entity, Position position);
public void placeInitialEntities(Map<EntityFactory, Integer> entityCounts);
//Return if the position is completely free
public boolean isPositionEmpty(Position position);
//Return if the position is available for the specified priority
public boolean isPositionFree(Position position, int priority);
}
package model;
import java.util.ArrayList;
import java.util.List;
import javafx.scene.paint.Color;
import model.firefighterscenario.Cloud;
import util.Position;
import view.ViewElement;
public class EmptySquare implements Entity {
private Position position;
private final Color viewColor = Color.WHITE;
private int age;
private final int priotity = -1;
private static javafx.scene.image.Image cloudImage;
static {
try {
cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/fire/img.png").toExternalForm());
} catch (Exception e) {
e.printStackTrace();
}
}
public EmptySquare(Position p) {
this.position = p;
this.age = -999;
}
public EmptySquare(Position p, int age) {
this.position = p;
this.age = age;
}
@Override
public List<Position> nextTurn(Board<Square> board) {
return new ArrayList<Position>();
}
@Override
public Position getPosition() {
return position;
}
@Override
public void setPosition(Position p) {
this.position = p;
}
public Color getViewColor() {
return this.viewColor;
}
@Override
public int getAge() {
return this.age;
}
@Override
public void incrementAge() {
age = age + 1;
}
@Override
public void setAge(int age) {
this.age = age;
}
@Override
public int getPriority(){
return this.priotity;
}
@Override
public ViewElement getViewElement() {
return new ViewElement(cloudImage);
}
}
package model;
import java.util.List;
import javafx.scene.paint.Color;
import util.Position;
import view.ViewElement;
public interface Entity {
/**
* Exécute un tour de jeu, en vérifiant les cases adjacentes pour des instances de Fire.
*
* @param b Le plateau de jeu contenant des entités.
* @return Une liste de positions affectées durant le tour (que la vue doit mettre à jour).
*/
public List<Position> nextTurn(Board<Square> board);
public Position getPosition();
public void setPosition(Position p);
public int getAge();
public void setAge(int age);
public void incrementAge();
public Color getViewColor();
public int getPriority();
public ViewElement getViewElement();
}
package model;
import util.Position;
public interface EntityFactory {
Entity create(Position position, Board<Square> board);
}
package model;
class EntityNotFoundException extends Exception {
public EntityNotFoundException(String message) {
super(message);
}
}
\ No newline at end of file
package model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import util.Direction;
import util.Position;
import util.PositionUtil;
public class EntitySpawner {
private final Board<Square> board;
private final Random random = new Random();
public EntitySpawner(Board<Square> board) {
this.board = board;
}
public void spawnEntities(Map<EntityFactory, Integer> entityCounts) {
Map<EntityFactory, Integer> counts = new HashMap<>();
for (EntityFactory factory : entityCounts.keySet()) {
counts.put(factory, 0);
}
int totalEntitiesToPlace = entityCounts.values().stream().mapToInt(Integer::intValue).sum();
int totalEntitiesPlaced = 0;
int chance = 5;
List<Position> positions = generateAllPositions();
while (totalEntitiesPlaced < totalEntitiesToPlace) {
Collections.shuffle(positions);
for (Position pos : positions) {
if (board.getStates(pos).isEmpty()) {
for (EntityFactory factory : entityCounts.keySet()) {
int desiredCount = entityCounts.get(factory);
int currentCount = counts.get(factory);
if (currentCount < desiredCount && random.nextInt(100) < chance) {
Entity entity = factory.create(pos, board);
board.setSquare(new Square(pos, entity));
counts.put(factory, currentCount + 1);
totalEntitiesPlaced++;
if (totalEntitiesPlaced == totalEntitiesToPlace) {
return;
}
break; // Move to the next position
}
}
}
}
// Increase chance after each full traversal
chance = Math.min(chance + 5, 100);
}
}
private List<Position> generateAllPositions() {
List<Position> positions = new ArrayList<>();
for (int x = 0; x < board.rowCount(); x++) {
for (int y = 0; y < board.columnCount(); y++) {
positions.add(new Position(x, y));
}
}
return positions;
}
public static void generateEntitiesInLine(Board<Square> board, Position anchor, EntityFactory entityFactory) {
int xIncrement = 0;
int yIncrement = 0;
// Determine increments based on which coordinate is zero
if (anchor.x() == 0 && anchor.y() >= 0) {
// Starting from the left edge (x == 0), increment x to move right
xIncrement = 1;
} else if (anchor.y() == 0 && anchor.x() >= 0) {
// Starting from the top edge (y == 0), increment y to move down
yIncrement = 1;
} else {
// If neither x nor y is 0, cannot determine direction
throw new IllegalArgumentException("Anchor position must have x or y equal to 0");
}
int x = anchor.x();
int y = anchor.y();
// Continue until we reach the edge of the board
while (board.doesPositionExist(new Position(x, y))) {
Position pos = new Position(x, y);
// Create a new entity for each position
Entity entity = entityFactory.create(pos, board);
entity.setPosition(pos); // Set the position if not already set in the factory
board.addEntityAtSquare(entity, pos);
x += xIncrement;
y += yIncrement;
}
}
public static List<Position> generateEntitiesInRandomLine(Board<Square> board, Position anchor, int maxStepsInDirection, int minimumRoadLength) {
Random random = new Random();
List<Position> path = new ArrayList<>();
int x = anchor.x();
int y = anchor.y();
// Toutes les directions possibles
List<Direction> allDirections = Arrays.asList(Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
// Choisir une direction initiale aléatoire
Direction initialDirection = allDirections.get(random.nextInt(allDirections.size()));
path.add(new Position(x, y)); // Ajouter la position de l'ancre au chemin
int roadLength = 1;
// Déterminer la direction interdite (opposée à la direction initiale)
Direction forbiddenDirection = PositionUtil.getOppositeDirection(initialDirection);
// Initialiser la direction courante
Direction currentDirection = initialDirection;
int stepsInCurrentDirection = 0;
// Ensemble des directions définitivement exclues (direction initiale)
Set<Direction> permanentlyExcludedDirections = new HashSet<>();
permanentlyExcludedDirections.add(initialDirection);
// Ensemble des directions temporairement exclues (initialement vide)
Set<Direction> temporarilyExcludedDirections = new HashSet<>();
while (true) {
// Calculer la prochaine position dans la direction courante
int nextX = x + getXIncrement(currentDirection);
int nextY = y + getYIncrement(currentDirection);
Position nextPos = new Position(nextX, nextY);
if (board.doesPositionExist(nextPos)) {
// Ajouter la position au chemin
path.add(nextPos);
x = nextX;
y = nextY;
roadLength++;
stepsInCurrentDirection++;
} else {
// La position dans la direction courante est invalide
if (roadLength < minimumRoadLength) {
// Exclure temporairement la direction courante
temporarilyExcludedDirections.add(currentDirection);
// Choisir une nouvelle direction valide
Direction newDirection = chooseNewDirection(allDirections, currentDirection, forbiddenDirection,
permanentlyExcludedDirections, temporarilyExcludedDirections, board, x, y, random);
if (newDirection == null) {
// Aucune direction valide disponible pour atteindre la longueur minimale
break;
}
// Mettre à jour la direction courante
currentDirection = newDirection;
forbiddenDirection = PositionUtil.getOppositeDirection(currentDirection);
stepsInCurrentDirection = 0;
continue; // Recommencer avec la nouvelle direction
} else {
// La longueur minimale est atteinte, arrêter la génération
break;
}
}
// Vérifier s'il est temps de changer de direction
if (stepsInCurrentDirection >= maxStepsInDirection) {
// Choisir une nouvelle direction
Direction newDirection = chooseNewDirection(allDirections, currentDirection, forbiddenDirection,
permanentlyExcludedDirections, temporarilyExcludedDirections, board, x, y, random);
if (newDirection == null) {
// Aucune direction valide disponible
break;
}
// Mettre à jour la direction courante
currentDirection = newDirection;
forbiddenDirection = PositionUtil.getOppositeDirection(currentDirection);
stepsInCurrentDirection = 0;
}
}
return path; // Retourner la liste des positions formant le serpent
}
/**
* Choisit une nouvelle direction valide en tenant compte des exclusions permanentes et temporaires.
*
* @param allDirections Toutes les directions possibles.
* @param currentDirection La direction actuelle.
* @param forbiddenDirection La direction opposée à la direction actuelle (interdite).
* @param permanentlyExcludedDirections Les directions définitivement exclues.
* @param temporarilyExcludedDirections Les directions temporairement exclues.
* @param board Le plateau de jeu.
* @param x La coordonnée X actuelle.
* @param y La coordonnée Y actuelle.
* @param random Une instance de Random pour le choix aléatoire.
* @return La nouvelle direction choisie ou null si aucune direction valide n'est disponible.
*/
private static Direction chooseNewDirection(List<Direction> allDirections, Direction currentDirection, Direction forbiddenDirection,
Set<Direction> permanentlyExcludedDirections, Set<Direction> temporarilyExcludedDirections,
Board<Square> board, int x, int y, Random random) {
// Créer une liste de directions valides en excluant :
// - La direction actuelle
// - La direction interdite (opposée à la direction actuelle)
// - Les directions définitivement exclues
// - Les directions temporairement exclues
List<Direction> validDirections = new ArrayList<>(allDirections);
validDirections.remove(currentDirection);
validDirections.remove(forbiddenDirection);
validDirections.removeAll(permanentlyExcludedDirections);
validDirections.removeAll(temporarilyExcludedDirections);
// Filtrer les directions qui permettent de continuer le chemin
validDirections.removeIf(dir -> !board.doesPositionExist(new Position(x + getXIncrement(dir), y + getYIncrement(dir))));
if (validDirections.isEmpty()) {
// Aucune direction valide disponible
return null;
}
// Choisir une nouvelle direction aléatoirement
return validDirections.get(random.nextInt(validDirections.size()));
}
private static int getXIncrement(Direction direction) {
switch (direction) {
case NORTH: return -1;
case SOUTH: return 1;
default: return 0;
}
}
private static int getYIncrement(Direction direction) {
switch (direction) {
case EAST: return 1;
case WEST: return -1;
default: return 0;
}
}
}
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
package model;
import java.util.List;
import util.Position;
public interface Model {
public List<Position> updateToNextGeneration();
public Board<Square> getBoard();
}
package model;
public enum ModelElement {
FIREFIGHTER, FIRE
}
package model;
import java.util.List;
import javafx.scene.paint.Color;
import model.firefighterscenario.Cloud;
import util.Position;
import view.ViewElement;
public class Road implements Entity{
private int age;
private final int PRIORITY = 0;
private final Color VIEW_COLOR = Color.BLACK;
private Position position;
private static javafx.scene.image.Image cloudImage;
static {
try {
cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/fire/route.png").toExternalForm());
} catch (Exception e) {
e.printStackTrace();
}
}
public Road(Position position){
this.position = position;
}
@Override
public List<Position> nextTurn(Board<Square> board) {
return List.of();
}
@Override
public Position getPosition() {
return this.position;
}
@Override
public void setPosition(Position p) {
this.position = p;
}
@Override
public int getAge() {
return age;
}
@Override
public void setAge(int age) {
this.age = age;
}
@Override
public void incrementAge() {
this.age = age + 1;
}
@Override
public Color getViewColor() {
return this.VIEW_COLOR;
}
@Override
public int getPriority() {
return this.PRIORITY;
}
@Override
public ViewElement getViewElement(){
return new ViewElement(cloudImage);
}
}
package model;
import java.util.List;
import java.util.Map;
import app.SimulatorApplication;
import util.Matrix;
import util.Position;
import util.PositionUtil;
public class Scenario implements Board<Square>{
private Matrix<Square> matrix;
protected int step;
protected int turnsToSpawnAirTanker;
protected Map<EntityFactory, Integer> initialMap;
public Scenario(int columns, int rows, Map<EntityFactory, Integer> initialMap) {
this.matrix = new Matrix<Square>(columns, rows);
initScenario(matrix);
this.turnsToSpawnAirTanker = SimulatorApplication.TURNS_FOR_SPAWNING_AIRTANKER;
this.step = 0;
this.initialMap = initialMap;
placeInitialEntities(initialMap);
}
public void initScenario(Matrix<Square> matrix){
for(int x = 0; x < matrix.getRows(); x++){
for(int y = 0; y < matrix.getColumns(); y++){
Square s = new Square(new Position(x, y), new EmptySquare(new Position(x,y)));
matrix.set(x,y, s);
}
}
}
protected Matrix<Square> getMatrix(){
return this.matrix;
}
public void placeInitialEntities(Map<EntityFactory, Integer> initialMap) {
EntitySpawner spawner = new EntitySpawner(this);
spawner.spawnEntities(initialMap);
}
public Square getStates(Position position) {
if (position.x() > matrix.size() || position.y() > matrix.size()) {
throw new IllegalArgumentException(
"The position x:" + position.x() + " y:" + position.y() + " is out of the board.");
}
return matrix.get(position.x(), position.y());
}
public void setSquare(Square square) {
Position position = square.getPosition();
if (!(getStates(position).isEmpty())) {
return;
}
if (doesPositionExist(position)) {
matrix.set(position.x(), position.y(), square);
}
}
public void setSquare(Square square, boolean replaceStates) {
Position position = square.getPosition();
if (!(getStates(position).isEmpty()) && !replaceStates) {
return;
}
matrix.set(position.x(), position.y(), square);
}
public void addEntityAtSquare(Entity entity, Position position) {
if (doesPositionExist(position)) {
matrix.get(position.x(), position.y()).addEntity(entity);
}
}
public int rowCount() {
return matrix.getRows();
}
public int columnCount() {
return matrix.getColumns();
}
@Override
public void clearCaseFrom(Entity entity, Position position) {
if(!matrix.get(position.x(), position.y()).getEntities().removeIf(element -> element.equals(entity))){
for(Entity e : getStates(position).getEntities()){
System.out.println(e);
}
}
}
public Position getNearestEntity(Position fromPos, Class<?> entityType, List<Entity> exclusionList) {
int rows = matrix.getRows();
int cols = matrix.getColumns();
if(exclusionList == null){
exclusionList = List.of();
}
// Définir la distance maximale possible
int maxDistance = rows + cols;
// Parcourir les distances croissantes à partir de 1
for (int distance = 1; distance < maxDistance; distance++) {
List<Position> positionsAtDistance = PositionUtil.getPositionsAtManhattanDistance(fromPos, distance, rows, cols);
for (Position currentPos : positionsAtDistance) {
if(isPositionEmpty(currentPos))continue;
Square currentSquare = matrix.get(currentPos.x(), currentPos.y());
for (Entity currentEntity : currentSquare.getEntities()) {
if (entityType.isInstance(currentEntity) && exclusionList != null && !exclusionList.contains(currentEntity)) {
// Vérifie si l'entité actuelle n'est pas dans la liste des exclusions
// Dès qu'une entité éligible est trouvée à cette distance, elle est la plus proche possible
return currentPos;
}
}
}
}
return null; // Retourne null si aucune entité éligible n'est trouvée
}
public void reset() {
step = 0;
matrix.clear();
initScenario(matrix);
placeInitialEntities(initialMap);
}
public int stepNumber() {
return this.step;
}
@Override
public boolean doesPositionExist(Position position) {
return matrix.validateIndex(position);
}
@Override
public int getStepNumber() {
return step;
}
@Override
public boolean doesSquareContainEntity(Position squarePos, Class<?> entityType) {
return getStates(squarePos).getEntities().stream().anyMatch(entityType::isInstance);
}
@Override
public boolean isPositionEmpty(Position position) {
return getStates(position).isEmpty();
}
@Override
public boolean isPositionFree(Position position, int priority) {
List<Entity> entities = matrix.get(position.x(), position.y()).getEntities();
for (Entity e : entities) {
if (e.getPriority() == priority) {
return false;
}
}
return true;
}
}
package model;
import java.util.ArrayList;
import java.util.List;
import javafx.scene.paint.Color;
import util.Position;
public class Square {
private List<Entity> entities;
private Position position;
public Square(Position position){
this.entities = new ArrayList<Entity>();
this.position = position;
}
public Square(Position position, Entity entity){
this.entities = new ArrayList<Entity>();
this.entities.add(entity);
this.position = position;
}
public List<Entity> getEntities(){
return this.entities;
}
public Position getPosition(){
return this.position;
}
public void addEntity(Entity entity){
entities.add(entity);
}
public void removeEntity(Entity entity){
entities.remove(entity);
}
public void setEntities(List<Entity> entities){
this.entities = entities;
}
public boolean isEmpty(){
return entities.isEmpty() ||(entities.size() == 1 && entities.get(0) instanceof EmptySquare);
}
public int getMinimalAge(){
int minimalAge = 0;
for(Entity e : entities){
if(e.getAge() < minimalAge){
minimalAge = e.getAge();
}
}
return minimalAge;
}
public int getMaxAge(){
int maxAge = 0;
for(Entity e : entities){
if(e.getAge() > maxAge){
maxAge = e.getAge();
}
}
return maxAge;
}
public void incrementAllAges(){
for(Entity e : entities){
e.incrementAge();
}
}
public Color getViewColor() {
if (entities.isEmpty()) {
return Color.WHITE;
} else {
int maxPriority = -1; // Assuming priorities are non-negative
Color c = Color.WHITE;
// Iterate over entities to find the one with the highest priority
for (Entity e : entities) {
// Check for null entities if necessary
if (e != null && e.getPriority() > maxPriority) {
maxPriority = e.getPriority();
c = e.getViewColor();
}
}
return c;
}
}
}