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
  • main
  • melissa
  • variant
  • yanis
4 results

Target

Select target project
No results found
Select Git revision
  • main
  • variant
2 results
Show changes
58 files
+ 4227
377
Compare changes
  • Side-by-side
  • Inline

Files

+8 −0
Original line number Original line Diff line number Diff line
@@ -32,3 +32,11 @@ fabric.properties
.idea/caches/build_file_checksums.ser
.idea/caches/build_file_checksums.ser




.gradle/
*.class
bin/
.gradle
*.bin
*.lock
.gradle/8.10.2/executionHistory/executionHistory.bin
build/
 No newline at end of file
+142 −0
Original line number Original line Diff line number Diff line
.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
+46 −0
Original line number Original line Diff line number Diff line
<?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" />
  </VBox>
  <FirefighterGrid fx:id="grid"
                   xmlns="http://javafx.com/javafx"
                   xmlns:fx="http://javafx.com/fxml">
  </FirefighterGrid>
</HBox>
+89 −89

File changed.

Contains only whitespace changes.

scenariovirus.txt

0 → 100644
+15 −0
Original line number Original line Diff line number Diff line
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.
Original line number Original line Diff line number Diff line
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,23 @@ 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;
  private static final int ROW_COUNT = 50;
  private static final int COLUMN_COUNT = 20;
  private static final int COLUMN_COUNT = 50;
  private static final int BOX_WIDTH = 50;
  private static final int BOX_WIDTH = 15;
  private static final int BOX_HEIGHT = 50;
  private static final int BOX_HEIGHT = 15;
  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 = 6;
  public static final int INITIAL_MOTORIZED_FIREFIGHTER_COUNT = 8;
  public static final int INITIAL_CLOUD_COUNT = 20;
  public static final int INITIAL_MOUNTAIN_COUNT= 20;
  public static final int TURNS_FOR_SPAWNING_AIRTANKER = 10;


  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 +36,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();
@@ -44,7 +49,7 @@ public class SimulatorApplication extends javafx.application.Application {
    view = loader.load();
    view = loader.load();
    Controller controller = loader.getController();
    Controller controller = loader.getController();
    controller.initialize(BOX_WIDTH, BOX_HEIGHT, COLUMN_COUNT, ROW_COUNT,
    controller.initialize(BOX_WIDTH, BOX_HEIGHT, COLUMN_COUNT, ROW_COUNT,
            INITIAL_FIRE_COUNT, INITIAL_FIREFIGHTER_COUNT);
        INITIAL_FIRE_COUNT, INITIAL_FIREFIGHTER_COUNT, INITIAL_MOTORIZED_FIREFIGHTER_COUNT, INITIAL_CLOUD_COUNT, INITIAL_MOUNTAIN_COUNT, TURNS_FOR_SPAWNING_AIRTANKER);
  }
  }


  private void showScene() {
  private void showScene() {
Original line number Original line Diff line number Diff line
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,17 +20,18 @@ 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.Entity;
import model.FirefighterBoard;
import model.EntityFactory;
import model.Model;
import model.Square;
import model.doctorviruspatient.Doctor;
import model.doctorviruspatient.DoctorVirusPatientScenario;
import model.doctorviruspatient.Patient;
import model.doctorviruspatient.Virus;
import util.Position;
import util.Position;
import view.Grid;
import view.Grid;
import view.ViewElement;
import view.ViewElement;


import java.util.ArrayList;
import java.util.List;

import static java.util.Objects.requireNonNull;

public class Controller {
public class Controller {


  public static final int PERIOD_IN_MILLISECONDS = 50;
  public static final int PERIOD_IN_MILLISECONDS = 50;
@@ -40,7 +48,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 +63,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 +86,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()) {

          return new ViewElement(entity.getImage());
      }
      }
    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);
@@ -124,9 +133,32 @@ public class Controller {
  }
  }


  public void initialize(int squareWidth, int squareHeight, int columnCount,
  public void initialize(int squareWidth, int squareHeight, int columnCount,
                                int rowCount, int initialFireCount, int initialFirefighterCount) {
      int rowCount, int initialFireCount, int initialFirefighterCount, int initialMotorizedFirefightersCount, int initialcloudCount, int initialmountaincount, int turnsForSpawningAirTanker) {
    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 Fire(pos), initialFireCount);
    entityCounts.put((pos, b) -> new FireFighter(pos,b), initialFirefighterCount);
    entityCounts.put((pos, b) -> new MotorizedFireFighter(pos, b), initialMotorizedFirefightersCount);
    entityCounts.put((pos, b) -> new Cloud(pos, b), initialcloudCount);
    entityCounts.put((pos, b) -> new Mountain(pos), initialmountaincount);
    entityCounts.put((pos, b) -> new Rockery(pos), 3);
    */
    
    /*
    entityCounts.put((pos, b) -> new Rock(pos), 10);
    entityCounts.put((pos, b) -> new Cisor(pos), 10);
    entityCounts.put((pos, b) -> new Paper(pos), 10);
    */

      
    entityCounts.put((pos, b) -> new Patient(pos), 70);
    entityCounts.put((pos, b) -> new Virus(pos), 6);
    entityCounts.put((pos, b) -> new Doctor(pos), 3);
    
    Model model = new DoctorVirusPatientScenario(columnCount, rowCount, entityCounts);
    this.setModel(model);
    repaintGrid();
    repaintGrid();
  }
  }
  
  
Original line number Original line Diff line number Diff line
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> {
   * @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> {
   * @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> {
   */
   */
  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> {
   * @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);

  
}
+79 −0
Original line number Original line Diff line number Diff line
package model;

import java.util.ArrayList;
import java.util.List;

import javafx.scene.paint.Color;
import model.firefighterscenario.Cloud;
import util.Position;

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/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 javafx.scene.image.Image getImage() {
        return cloudImage;
    }
}
+25 −0
Original line number Original line Diff line number Diff line
package model;
import javafx.scene.image.Image;
import java.util.List;

import javafx.scene.paint.Color;
import util.Position;


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 Image getImage();
}
Original line number Original line Diff line number Diff line
package model;

import util.Position;

public interface EntityFactory {
    Entity create(Position position, Board<Square> board);
}
Original line number Original line Diff line number Diff line
package model;

class EntityNotFoundException extends Exception {
    public EntityNotFoundException(String message) {
        super(message);
    }
}
 No newline at end of file
+262 −0
Original line number Original line Diff line number Diff line
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;
        }
    }
    

    
    
    

    
    

    
}
+0 −147
Original line number Original line Diff line number Diff line
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
+10 −0
Original line number Original line Diff line number Diff line
package model;

import java.util.List;

import util.Position;

public interface Model {
    public List<Position> updateToNextGeneration();
    public Board<Square> getBoard();
}
+0 −5
Original line number Original line Diff line number Diff line
package model;

public enum ModelElement {
  FIREFIGHTER, FIRE
}
+74 −0
Original line number Original line Diff line number Diff line
package model.firefighterscenario;

import java.util.List;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Position;

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/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 javafx.scene.image.Image getImage() {
        return cloudImage;
    }

}
+166 −0
Original line number Original line Diff line number Diff line
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))){
      System.out.println("didn't clear, tried to clear : x:" + position.x() + " y : " + position.y());
      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;
  }

}
+93 −0
Original line number Original line Diff line number Diff line
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;
        }
    }
    
    
    
}
Original line number Original line Diff line number Diff line
package model.doctorviruspatient;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Position;
import util.PositionUtil;

public class Doctor implements Entity {
    private final int priority = 1;
    private Position position;
    private int age;
    private final Color viewColor = Color.RED;
    
    // Statique car les médecins partagent les connaissances et les recherches
    private static List<Integer> knownVirusVariant = new ArrayList<Integer>();
    private static Map<Integer, Double> currentResearch = new HashMap<>(); // Map<variantId, % de recherche>

    public Doctor(Position p) {
        this.position = p;
    }

    public Doctor(Position p, int age) {
        this.position = p;
        this.age = age;
    }

    @Override
    public List<Position> nextTurn(Board<Square> board) {
        // Mettre à jour les recherches en cours
        updateCurrentResearch();

        // Interagir avec les positions adjacentes
        interactWithAdjacentPositions(PositionUtil.generateAllAdjacentPositions(position, board), board);
        
        return List.of();
    }

    /**
     * Met à jour toutes les recherches en cours en augmentant leur avancement de x%.
     * Si une recherche atteint ou dépasse 100%, elle est terminée.
     */
    private void updateCurrentResearch() {
        List<Integer> completedResearch = new ArrayList<>();
        
        for (Map.Entry<Integer, Double> entry : currentResearch.entrySet()) {
            int variantId = entry.getKey();
            double progress = entry.getValue() + 2.0;
            if (progress >= 100.0) {
                progress = 100.0;
                completedResearch.add(variantId);
            }
            currentResearch.put(variantId, progress);
        }

        // Traiter les recherches complétées
        for (int variantId : completedResearch) {
            currentResearch.remove(variantId);
            knownVirusVariant.add(variantId);
            System.out.println("Recherche terminée pour le variant ID: " + variantId);
        }
    }

    private List<Position> interactWithAdjacentPositions(List<Position> adjacentPositions, Board<Square> board) {
        List<Position> result = new ArrayList<>();
        for (Position p : adjacentPositions) {
            if (board.doesSquareContainEntity(p, Patient.class)) {
                handleEncounterWithPatient(p, board);
            }
        }
        return result;
    }

    private void handleEncounterWithPatient(Position p, Board<Square> board) {
        if (board.doesSquareContainEntity(p, Patient.class)) {
            Patient patient = (Patient) board.getStates(p).getEntities().stream()
                    .filter(e -> e instanceof Patient)
                    .findFirst()
                    .orElse(null);
            if (patient != null && patient.isInfected()) {
                Virus virus = patient.getVirus();
                if (virus != null) {
                    int variantId = virus.getVariantId();
                    if (knownVirusVariant.contains(variantId)) {
                        patient.cure();
                    } else {
                        // Si la variante n'est pas connue et pas en cours de recherche, l'ajouter à la recherche
                        if (!currentResearch.containsKey(variantId)) {
                            currentResearch.put(variantId, 0.0);
                            System.out.println("Nouvelle variante détectée! Variant ID: " + variantId + ". Recherche commencée.");
                        }
                    }
                }
            }
        }
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public void setPosition(Position p) {
        this.position = p;
    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void incrementAge() {
        this.age += 1;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Doctor) {
            Doctor other = (Doctor) obj;
            return this.position.equals(other.getPosition());
        } else {
            return false;
        }
    }

    @Override
    public int hashCode() {
        return Objects.hash(position);
    }

}
Original line number Original line Diff line number Diff line
package model.doctorviruspatient;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import model.Board;
import model.Entity;
import model.EntityFactory;
import model.Model;
import model.Scenario;
import model.Square;
import util.Position;

public class DoctorVirusPatientScenario extends Scenario implements Model{
    public DoctorVirusPatientScenario(int columns, int rows, Map<EntityFactory, Integer> initialMap) {
        super(columns, rows, initialMap);
    }

    public List<Position> updateToNextGeneration() {
        ArrayList<Position> changedPositions = new ArrayList<>();
        Iterator<Square> iterator = getMatrix().iterator();
        while (iterator.hasNext()) {
          Square s = iterator.next();
          if (s.isEmpty())
            continue;
          if (s.getMaxAge() == 0) {
            s.incrementAllAges();
            continue;
          }
          if(s.getMaxAge()>stepNumber()+1){
            continue;
          }
          List<Entity> entities = new ArrayList<>(s.getEntities());
          for (Entity e : entities) {
            e.incrementAge();
            changedPositions.addAll(e.nextTurn(this));
          }
        }
        
        // Increment the step counter
        this.step = this.step + 1;
        return changedPositions;
      }


      
    @Override
    public Board<Square> getBoard() {
        return this;
    }


}
Original line number Original line Diff line number Diff line
package model.doctorviruspatient;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Random;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.PathGenerator;
import util.Position;
import util.PositionUtil;

public class Patient implements Entity {
    private final int priority = 1;
    private Position position;
    private int age;
    private final Color viewColor = Color.BLUE;
    private Virus carriedVirus;
    private List<Entity> visitedPatients;
    private static int patientsCount = 0;
    private int patientID;
    private Position ennemy;
    private boolean isAsymptomatic;
    private boolean wandering;

    // Attributs pour le chemin
    private List<Position> path;
    private int pathIndex;
    private static final int MAX_STEPS_IN_DIRECTION = 5;
    private static final int MINIMUM_ROAD_LENGTH = 10;

    public Patient(Position p) {
        this.wandering = true;
        patientID = patientsCount;
        patientsCount += 1;
        this.visitedPatients = new ArrayList<>();
        this.position = p;
        Random r = new Random();
        int randomNumber = r.nextInt(10);
        this.isAsymptomatic = randomNumber == 1;
        // Initialiser le chemin
        this.path = new ArrayList<>();
        this.pathIndex = 0;
    }

    public Patient(Position p, int age) {
        this.wandering = true;
        patientID = patientsCount;
        patientsCount += 1;
        this.visitedPatients = new ArrayList<>();
        this.position = p;
        this.age = age;
        Random r = new Random();
        int randomNumber = r.nextInt(10);
        this.isAsymptomatic = randomNumber == 1;
        // Initialiser le chemin
        this.path = new ArrayList<>();
        this.pathIndex = 0;
    }

    @Override
    public List<Position> nextTurn(Board<Square> board) {
        if (isInfected() && !isAsymptomatic) {
            return moveToDoctor(board);
        }
        updateWanderingStatus(board);

        if (wandering) {
            return performWandering(board);
        }

        resetVisitedPatientsIfNecessary();

        Position target = determineTarget(board);

        if (target == null) {
            visitedPatients.clear();
            return List.of();
        }

        Position nextPos = determineNextPosition(target, board);

        if (nextPos != null && board.doesPositionExist(nextPos)) {

            List<Position> adjacentPositions = PositionUtil.generateAllAdjacentPositions(nextPos, board);
            List<Position> result = interactWithAdjacentPositions(adjacentPositions, board);
            result.addAll(moveToPosition(nextPos, board));
            result.addAll(adjacentPositions);
            result.add(position);
            return result;
        }

        return List.of();
    }

    private void updateWanderingStatus(Board<Square> board) {
        if (board.getStepNumber() % 8 == 0) {
            wandering = false;
            // Réinitialiser le chemin lorsque le patient arrête d'errer
            this.path.clear();
            this.pathIndex = 0;
        }
    }

    private List<Position> moveToDoctor(Board<Square> board) {
        for (Position p : PositionUtil.generateAllAdjacentPositions(position, board)) {
            if (!board.doesPositionExist(p)) continue;
            if (board.doesSquareContainEntity(p, Doctor.class)) return List.of();
        }
        Position nearestDoctor = board.getNearestEntity(position, Doctor.class, null);
        Position nextPos = PositionUtil.getNextPositionTowards(position, nearestDoctor, board);
        if (nextPos == null) return List.of();

        List<Position> changedPositions = new ArrayList<>();
        changedPositions.addAll(moveToPosition(nextPos, board));
        return changedPositions;
    }

    private List<Position> performWandering(Board<Square> board) {
        if (path == null || path.isEmpty() || pathIndex >= path.size()) {
            generateNewPath(board);
            if (path.isEmpty()) {
                return List.of();
            }
        }

        Position nextPosition = path.get(pathIndex);

        if (board.doesPositionExist(nextPosition) && board.isPositionFree(nextPosition, priority)) {
            List<Position> changedPositions = moveSelfOnBoard(nextPosition, board);
            pathIndex++;
            return changedPositions;
        } else {
            // Si la position n'est pas libre ou n'existe pas, générer un nouveau chemin
            generateNewPath(board);
            return List.of();
        }
    }

    private void generateNewPath(Board<Square> board) {
        PathGenerator pathGenerator = new PathGenerator(board, position, MAX_STEPS_IN_DIRECTION, MINIMUM_ROAD_LENGTH);
        this.path = pathGenerator.generate();
        // Supprimer la position actuelle du chemin si elle est présente
        if (!path.isEmpty() && path.get(0).equals(position)) {
            path.remove(0);
        }
        this.pathIndex = 0;
    }

    private void resetVisitedPatientsIfNecessary() {
        if (visitedPatients.size() - 1 >= patientsCount) {
            visitedPatients.clear();
        }
    }

    private Position determineTarget(Board<Square> board) {
        return board.getNearestEntity(position, Patient.class, getVisitedPatients());
    }

    private Position determineNextPosition(Position target, Board<Square> board) {
        int enemyDistance = PositionUtil.getManhattanDistance(target, position);

        if (ennemy != null && enemyDistance < 2) {
            return PositionUtil.getNextPositionAwayFrom(position, target, board);
        } else if (target != null) {
            return PositionUtil.getNextPositionTowards(position, target, board);
        }
        return null;
    }

    private List<Position> moveToPosition(Position nextPos, Board<Square> board) {
        List<Position> changedPosition = new ArrayList<>();
        if (!board.isPositionFree(nextPos, priority)) {
            nextPos = findAlternativePosition(nextPos, board);
        }

        if (nextPos != null) {
            changedPosition.addAll(moveSelfOnBoard(nextPos, board));
        }

        return changedPosition;
    }

    private Position findAlternativePosition(Position currentPos, Board<Square> board) {
        List<Position> adjacentPositions = PositionUtil.generateAdjacentPositions(position, board);
        for (Position p : adjacentPositions) {
            if (ennemy != null) {
                Position awayPos = PositionUtil.getNextPositionAwayFrom(p, ennemy, board);
                if (p.equals(awayPos)) {
                    continue;
                }
            }
            if (board.isPositionFree(p, priority)) {
                return p;
            }
        }
        return null;
    }

    private List<Position> moveSelfOnBoard(Position newPosition, Board<Square> board) {
        if(!board.isPositionFree(newPosition, priority))return List.of();
        Position oldPosition = new Position(this.position.x(), this.position.y());
        board.clearCaseFrom(this, oldPosition);
        board.addEntityAtSquare(this, newPosition);
        this.position = newPosition;
        return List.of(oldPosition, this.position);
    }

    private List<Position> interactWithAdjacentPositions(List<Position> adjacentPositions, Board<Square> board) {
        List<Position> result = new ArrayList<>();
        for (Position p : adjacentPositions) {
            if (board.doesSquareContainEntity(p, Patient.class)) {
                handleEncounterWithPatient(p, board);
                if (isInfected()) {
                    result.addAll(adjacentPositions);
                }
            }
        }
        return result;
    }

    private void handleEncounterWithPatient(Position p, Board<Square> board) {
        this.wandering = true;
        // Réinitialiser le chemin lorsqu'un patient est rencontré
        this.path.clear();
        this.pathIndex = 0;
        Patient patient = (Patient) board.getStates(p).getEntities().stream()
                .filter(entity -> entity instanceof Patient)
                .findFirst()
                .orElse(null);
        if (isInfected()) {
            patient.infect(carriedVirus);
        }
        if (patient != null) {
            this.ennemy = patient.getPosition();
            visitedPatients.add(patient);
        }
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public void setPosition(Position p) {
        this.position = p;
    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void incrementAge() {
        this.age += 1;
    }

    @Override
    public Color getViewColor() {
        return isInfected() ? Color.CYAN : this.viewColor;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }

    public boolean isInfected() {
        return this.carriedVirus != null;
    }

    public void infect(Virus virus) {
        if (this.carriedVirus != null) return;
        this.carriedVirus = virus;
    }

    public Virus getVirus() {
        return this.carriedVirus;
    }

    public List<Entity> getVisitedPatients() {
        return this.visitedPatients;
    }

    public boolean hasVisited(Patient patient) {
        return this.getVisitedPatients().contains(patient);
    }

    public int getPatientId() {
        return this.patientID;
    }

    public void cure() {
        if (this.carriedVirus.tryToKill()) {
            this.carriedVirus = null;
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (obj instanceof Patient) {
            Patient other = (Patient) obj;
            return this.patientID == other.getPatientId();
        } else {
            return false;
        }

    }

    @Override
    public int hashCode() {
        return Objects.hash(patientID);
    }
}
Original line number Original line Diff line number Diff line
package model.doctorviruspatient;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.PathGenerator;
import util.Position;
import util.PositionUtil;

public class Virus implements Entity {
    private final int priority = 2;
    private Position position;
    private int age;
    private final Color viewColor = Color.LIMEGREEN;
    private List<Position> path;
    private int pathIndex;
    private static final int MAX_STEPS_IN_DIRECTION = 5;
    private static final int MINIMUM_ROAD_LENGTH = 10;
    private int health;
    private int variantId;

    public Virus(Position p) {
        this.variantId = 0;
        this.health = 50;
        this.position = p;
        this.path = new ArrayList<>();
        this.age = 0;
        this.pathIndex = 0;
    }

    public Virus(Position p, int age) {
        this.health = 8;
        this.position = p;
        this.age = age;
        this.path = new ArrayList<>();
        this.pathIndex = 0;
    }

    @Override
    public List<Position> nextTurn(Board<Square> board) {
        if(board.getStepNumber() % 100 == 0){
            evolve();
        }
        // Génère un nouveau chemin si nécessaire
        if (path == null || path.isEmpty() || pathIndex >= path.size()) {
            generateNewPath(board);
            if (path.isEmpty()) {
                return List.of();           
            }
        }
        List<Position> adjacentPosition = PositionUtil.generateAllAdjacentPositions(position, board);
        for(Position p : adjacentPosition){
            if(board.doesSquareContainEntity(p, Patient.class)){
                Patient patient = (Patient) board.getStates(p).getEntities().stream().filter(e -> e instanceof Patient).findFirst().get();
                patient.infect(this);
            }
        }
        // Se déplace vers la prochaine position du chemin
        Position nextPosition = path.get(pathIndex);
        if (board.doesPositionExist(nextPosition)) {
            // Vérifier si la position est libre
            if (board.isPositionFree(nextPosition, priority)) {
                Position oldPosition = position;
                position = nextPosition;
                board.clearCaseFrom(this, oldPosition);
                board.addEntityAtSquare(this, position);
                pathIndex = pathIndex + 1;
                return List.of(oldPosition, position);
            } else {
                // Si la position n'est pas libre, génère un nouveau chemin
                generateNewPath(board);
                return List.of(); 
            }
        } else {
            // Si la prochaine position n'existe pas, génère un nouveau chemin
            generateNewPath(board);
            return List.of();
        }
    }

    private void generateNewPath(Board<Square> board) {
        PathGenerator pathGenerator = new PathGenerator(board, position, MAX_STEPS_IN_DIRECTION, MINIMUM_ROAD_LENGTH);
        this.path = pathGenerator.generate();
        // Supprime la position actuelle du chemin si elle est présente au début
        if (!path.isEmpty() && path.get(0).equals(position)) {
            path.remove(0);
        }
        this.pathIndex = 0; // Réinitialise l'index du chemin
    }

    // Les autres méthodes restent inchangées
    private void evolve(){
        this.variantId++;
        Random r = new Random();
        this.health = r.nextInt(100);
    }

    public int getVariantId(){
        return this.variantId;
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public void setPosition(Position p) {
        this.position = p;
    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void incrementAge() {
        this.age += 1;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }

    public boolean tryToKill(){
        if(health <= 0){
            return true;
        }
        health--;
        return false;
    }
}
Original line number Original line Diff line number Diff line
package model.firefighterscenario;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Direction;
import util.Position;
import util.PositionUtil;

public class AirTanker implements Entity{
    private final Color viewColor = Color.GOLD;
    private Direction direction; // Direction in which the AirTanker moves
    private int age;
    private Position position;
    private int priority = 3;
    private static javafx.scene.image.Image cloudImage;

    static {
        try {
            cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/avion.png").toExternalForm());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public AirTanker(Position position, Board<Square> b, int age) {
        this.age = age;
        this.position = position;
        determineDirection(b);
        extinguish(position,b);
    }

    @Override
    public int getPriority() {
        return this.priority;
    }

    private void determineDirection(Board<Square> b) {
        int rowCount = b.rowCount();
        int columnCount = b.columnCount();

        if (position.x() == 0) {
            direction = Direction.EAST; // Move east if on left edge
        } else if (position.x() == columnCount - 1) {
            direction = Direction.WEST; // Move west if on right edge
        } else if (position.y() == 0) {
            direction = Direction.SOUTH; // Move south if on top edge
        } else if (position.y() == rowCount - 1) {
            direction = Direction.NORTH; // Move north if on bottom edge
        } else {
            // Not on an edge; default to moving east or choose a random direction
            direction = Direction.EAST;
        }
    }

    @Override
    public List<Position> nextTurn(Board<Square> b) {
        List<Position> positions = new ArrayList<>();
        // Move in the determined direction
        Position nextPos = getNextPosition(position, direction, b);
        
        if (nextPos == null || !b.doesPositionExist(nextPos)) {
            // Reached the edge; remove AirTanker from the board
            b.clearCaseFrom(this, position);
            positions.add(position);
            return positions;
        } else {
            // Extinguish fires within a 3-square radius

            positions.addAll(extinguish(position, b));
            // Move to the next position
            b.clearCaseFrom(this, position);
            positions.add(new Position(position.x(), position.y())); // Old position
            this.position = nextPos;
            b.addEntityAtSquare(this, nextPos);
            positions.add(nextPos); // New position

            return positions;
        }
    }
    protected List<Position> extinguish(Position position, Board<Square> b){
        List<Position> positions = new ArrayList<Position>();
        List<Position> positionsInRange = PositionUtil.getPositionsInRadius(position, 3, b);
        for (Position p : positionsInRange) {
            if (b.doesSquareContainEntity(p, Fire.class)) {
                b.getStates(p).getEntities().removeIf(e -> e instanceof Fire);
                positions.add(p); // Add position where fire was extinguished
            }
        }
        return positions;
    }
    private Position getNextPosition(Position currentPosition, Direction direction, Board<Square> b) {
        int x = currentPosition.x();
        int y = currentPosition.y();

        switch (direction) {
            case NORTH:
                y -= 1;
                break;
            case SOUTH:
                y += 1;
                break;
            case EAST:
                x += 1;
                break;
            case WEST:
                x -= 1;
                break;
            default:
                break; // Only handling cardinal directions
        }
        Position newPos = new Position(x, y);
        if (b.doesPositionExist(newPos)) {
            return newPos;
        } else {
            return null; // Reached the edge
        }
    }

    public Direction getDirection(){
        return this.direction;
    }

        @Override
    public void setPosition(Position p) {
        this.position = p;
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void incrementAge() {
        this.age++;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }



    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true; // Check if same object
        if (obj == null || getClass() != obj.getClass()) return false; // Check for null and class match
        AirTanker airTanker = (AirTanker) obj; // Cast to Fire
        return age == airTanker.age && position.equals(airTanker.position); // Compare age and position
    }
    
    
    @Override
    public int hashCode() {
        return Objects.hash(position, age);
    }

    @Override
    public javafx.scene.image.Image getImage() {
        return cloudImage;
    }
}
Original line number Original line Diff line number Diff line
package model.firefighterscenario;

import java.util.List;
import java.util.Random;

import javafx.scene.paint.Color;
import model.Board;
import model.EmptySquare;
import model.Entity;
import model.Square;
import util.Position;
import util.PositionUtil;

public class Cloud implements Entity{
    private int age;
    private Position position;
    private final Color viewColor = Color.GRAY;
    private final int priority = 2;

    private static javafx.scene.image.Image cloudImage;
    
    static {
        try {
            cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/nuage.png").toExternalForm());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public Cloud(Position position, Board<Square> b){
        this.age = 0;
        this.position = position;

    }

    public Cloud(Position position, Board<Square> b, int age){
        this.age = age;
        this.position = position;

    }

    @Override
    public List<Position> nextTurn(Board<Square> b) {
        List<Position> adjacentPositions = PositionUtil.generateAdjacentPositions(position, b);
        adjacentPositions.removeIf( p -> b.doesSquareContainEntity(p, Cloud.class));
        adjacentPositions.removeIf( p -> b.doesSquareContainEntity(p, Mountain.class));

        // Filtrer pour obtenir uniquement les positions qui ne contiennent pas de pompier
        adjacentPositions.removeIf(p -> b.doesSquareContainEntity(p, FireFighter.class));

        // Choisir une position aléatoire parmi les mouvements possibles
        if(adjacentPositions.size() < 0){
            return List.of();
        }
        Position next_position = adjacentPositions.get(new Random().nextInt(adjacentPositions.size()));

        // Si la nouvelle position contient un feu, éteindre le feu
        if (b.getStates(next_position).getEntities().stream().anyMatch(element -> element instanceof Fire) ){
            extinguish(next_position, b);
        }
        
        Position old_position = this.position;
        this.position = next_position;
        b.addEntityAtSquare(this, next_position);
        b.clearCaseFrom(this, old_position);

        return List.of(old_position, this.position);
    }


    private Position extinguish(Position p, Board<Square> b) {
        b.getStates(p).getEntities().removeIf(element -> element instanceof Fire);
            List<Entity> entities = b.getStates(p).getEntities();
            for (Entity e : entities) {
                if (e instanceof EmptySquare) {
                    e.setAge(b.stepNumber() + 1);
                }
            }

        return p;
    }

    @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 += 1;

    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getPriority(){ 
        return this.priority;
    }

    @Override
    public javafx.scene.image.Image getImage() {
        return cloudImage;
    }



}
Original line number Original line Diff line number Diff line
package model.firefighterscenario;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Position;
import util.PositionUtil;


public class Fire implements Entity {
    private Position position;
    private final Color viewColor = Color.RED;
    private int age;
    private final int priority = 1;
    private static javafx.scene.image.Image cloudImage;

    static {
        try {
            cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/flamme.png").toExternalForm());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Fire(Position position) {
        this.position = position;
        this.age = 0;
    }

    public Fire(Position position, int age) {
        this.position = position;
        this.age = age;
    }

    @Override
    public List<Position> nextTurn(Board<Square> board) {
        List<Position> newFirePositions = new ArrayList<>();
        if(board.getStepNumber() <= 1){
            return newFirePositions;
        }
        // Fire propagates every 2 turns
        if (age % 2 != 0) {
            return newFirePositions; // No propagation this turn
        }

        // Generate adjacent positions
        List<Position> adjacentPositions = PositionUtil.generateAdjacentPositions(position, board);
        
        for (Position p : adjacentPositions) {
            // Skip if position does not exist
            if (!board.doesPositionExist(p)) {
                continue;
            }

            // Skip if the position contains a Mountain
            if (board.doesSquareContainEntity(p, Mountain.class)) {
                continue;
            }

            // Skip if the position already contains a Fire
            if (board.doesSquareContainEntity(p, Fire.class)) {
                continue;
            }

            // Skip if the position contains a Cloud (if clouds prevent fire spread)
            if (board.doesSquareContainEntity(p, Road.class)) {
                continue;
            }
            if (board.doesSquareContainEntity(p, Rockery.class)) {
                Optional<Entity> e = board.getStates(p).getEntities().stream().findFirst();
                if(e != null && e.isPresent() && e.get() instanceof Rockery){
                    Rockery rockery = (Rockery) e.get();
                    if(rockery.getBurn() < 4){
                        rockery.incrementBurn();
                        continue;
                    }
                    
                }
            }

            // Add new Fire to the board
            board.addEntityAtSquare(new Fire(p), p);
            newFirePositions.add(p); // Keep track of new fires for updates
        }

        return newFirePositions;
    }

    @Override
    public void setPosition(Position p) {
        this.position = p;
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void incrementAge() {
        this.age++;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true; // Check if same object
        if (obj == null || getClass() != obj.getClass()) return false; // Check for null and class match
        Fire fire = (Fire) obj; // Cast to Fire
        return age == fire.age && position.equals(fire.position); // Compare age and position
    }
    
    
    @Override
    public int hashCode() {
        return Objects.hash(position, age);
    }

    @Override
    public javafx.scene.image.Image getImage() {
        return cloudImage;
    }

}
Original line number Original line Diff line number Diff line
package model.firefighterscenario;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javafx.scene.paint.Color;
import model.Board;
import model.EmptySquare;
import model.Entity;
import model.Square;
import util.Position;
import util.PositionUtil;



public class FireFighter implements Entity {
    private int age;
    private Position position;
    private final Color viewColor = Color.BLUE;
    private final int priority = 1;
    protected List<Position> lastThreePosition;
    private static javafx.scene.image.Image cloudImage;

    static {
        try {
            cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/sapeur-pompier.png").toExternalForm());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public FireFighter(Position position, Board<Square> b) {
        this.position = position;
        this.age = 0;
        this.lastThreePosition = new ArrayList<Position>();
    }

    public FireFighter(Position position, Board<Square> b, int age) {
        this.position = position;
        this.age = age;
        this.lastThreePosition = new ArrayList<Position>();
    }

    public List<Position> nextTurn(Board<Square> b) {
        List<Position> positions = new ArrayList<>();
    
        // Generate adjacent positions, excluding mountains
        List<Position> adjacentPositions = PositionUtil.generateAdjacentPositions(position, b);
        adjacentPositions.removeIf(p -> b.doesSquareContainEntity(p, Mountain.class));
    
        // Check if there is fire in any adjacent positions
        boolean hasFire = adjacentPositions.stream()
                .anyMatch(p -> b.doesSquareContainEntity(p, Fire.class));
    
        boolean isStuck = isStuck();
    
        if (hasFire) {
            // Extinguish fires in adjacent positions
            positions.addAll(extinguish(adjacentPositions, b));
            if (isStuck) {
                // If stuck, attempt to move and then extinguish fires
                positions.addAll(moveAndExtinguish(b));
            }
        } else {
            // No fire adjacent; move and attempt to extinguish fires
            positions.addAll(moveAndExtinguish(b));
        }
    
        // Update last three positions
        updateLastPositions();
    
        return positions;
    }
    
    protected boolean isStuck() {
        if (lastThreePosition.size() < 3) {
            return false;
        }
        Position first = lastThreePosition.get(lastThreePosition.size() - 1);
        return lastThreePosition.stream().allMatch(pos -> pos.equals(first));
    }
    
    private void updateLastPositions() {
        if (lastThreePosition.size() >= 3) {
            lastThreePosition.remove(0);
        }
        lastThreePosition.add(this.position);
    }
    

    protected List<Position> extinguish(List<Position> adjacentPositions, Board<Square> b) {
        List<Position> extinguishedPositions = new ArrayList<>();
        for (Position p : adjacentPositions) {
            if (b.doesSquareContainEntity(p, Fire.class)) {
                if(b.doesSquareContainEntity(p, Rockery.class)){
                    Rockery rockery = (Rockery) b.getStates(p).getEntities().stream()
                    .filter(Rockery.class::isInstance)
                    .findFirst().get();
                    rockery.resetBurn();
                }
                b.getStates(p).getEntities().removeIf(element -> element instanceof Fire);
                // Update age for EmptyEntity if needed
                b.getStates(p).getEntities().forEach(e -> {
                    if (e instanceof EmptySquare) {
                        e.setAge(b.stepNumber() + 1);
                    }
                });
                extinguishedPositions.add(p); // Add position where fire was extinguished
            }
        }
        return extinguishedPositions;
    }
    

    protected List<Position> moveAndExtinguish(Board<Square> b) {
        List<Position> positions = new ArrayList<>();
    
        // Find the nearest fire
        Position nearestFirePos;
        try {
            nearestFirePos = b.getNearestEntity(position, Fire.class, null);
        } catch (Exception e) {
            return List.of();
        }
        if (nearestFirePos != null) {
            // Get the next position towards the fire
            Position nextPos = getNextPositionTowards(position, nearestFirePos, b);
            if (nextPos != null && !nextPos.equals(position)) {
                // Move the firefighter
                b.clearCaseFrom(this, position); // Clear old position
                positions.add(new Position(position.x(), position.y())); // Add old position
                this.position = nextPos;
                b.addEntityAtSquare(this, nextPos); // Add to new position
                positions.add(nextPos); // Add new position
    
                // After moving, attempt to extinguish fires adjacent to new position
                List<Position> newAdjacentPositions = PositionUtil.generateAdjacentPositions(nextPos, b);
                newAdjacentPositions.removeIf(p -> b.doesSquareContainEntity(p, Mountain.class));
                positions.addAll(extinguish(newAdjacentPositions, b));
            }
        }
        return positions;
    }
    
    protected Position getNextPositionTowards(Position currentPos, Position targetPos, Board<Square> b) {
        // Generate adjacent positions
        List<Position> possibleMoves = PositionUtil.generateAllAdjacentPositions(currentPos, b);
    
        // Filter out positions that are not empty or contain obstacles
        possibleMoves.removeIf(p -> b.doesSquareContainEntity(p, Mountain.class));
    
        // If no possible moves, return null
        if (possibleMoves.isEmpty()) {
            return null;
        }
    
        // Calculate the current distance to the target
        int currentDistance = PositionUtil.getManhattanDistance(currentPos, targetPos);
    
        // Initialize variables to find the best moves
        int minDistance = Integer.MAX_VALUE;
        List<Position> bestMoves = new ArrayList<>();
    
        for (Position move : possibleMoves) {
            int distance = PositionUtil.getManhattanDistance(move, targetPos);
    
            // Skip positions occupied by other firefighters
            if (b.doesSquareContainEntity(move, FireFighter.class)) {
                continue;
            }
    
            // Find positions that minimize the distance
            if (distance < minDistance) {
                minDistance = distance;
                bestMoves.clear();
                bestMoves.add(move);
            } else if (distance == minDistance) {
                bestMoves.add(move);
            }
        }
    
        // If no better move is found, consider moves that maintain the same distance
        if (bestMoves.isEmpty()) {
            minDistance = currentDistance;
            for (Position move : possibleMoves) {
                int distance = PositionUtil.getManhattanDistance(move, targetPos);
                if (distance == minDistance) {
                    bestMoves.add(move);
                }
            }
        }
    
        // If still no move is found, stay in the current position
        if (bestMoves.isEmpty()) {
            return currentPos;
        }
    
        // Select a move from the best moves (e.g., randomly or based on additional criteria)
        Random r = new Random();
        
        Position nextMove = bestMoves.get(r.nextInt(bestMoves.size()));
    
        return nextMove;
    }
    

    @Override
    public void setPosition(Position p) {
        this.position = p;
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void incrementAge() {
        this.age = age + 1;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }
    public int getPriority(){ return this.priority;}

    @Override
    public javafx.scene.image.Image getImage() {
        return cloudImage;
    }
}
Original line number Original line Diff line number Diff line
package model.firefighterscenario;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Random;

import model.Board;
import model.Entity;
import model.EntityFactory;
import model.Model;
import model.Scenario;
import model.Square;
import util.PathGenerator;
import util.Position;

public class FireFighterScenario extends Scenario implements Model{
  public FireFighterScenario(int columns, int rows, Map<EntityFactory, Integer> initialMap) {
    super(columns, rows, initialMap);
    generateRoads();
  }

  public List<Position> updateToNextGeneration() {
    ArrayList<Position> changedPositions = new ArrayList<>();
    Iterator<Square> iterator = getMatrix().iterator();
    List<Entity> updatedEntities = new ArrayList<Entity>();
    while (iterator.hasNext()) {
      Square s = iterator.next();
      if (s.isEmpty())
        continue;
      if (s.getMaxAge() == 0) {
        s.incrementAllAges();
        continue;
      }
      if (s.getMaxAge() == step + 1) {
        continue;
      }
      List<Entity> entities = new ArrayList<>(s.getEntities());
      for (Entity e : entities) {
        if(updatedEntities.contains(e))continue;
        if (e.getAge() >= stepNumber() - 1) {
          continue;
        }
        e.incrementAge();
        updatedEntities.add(e);
        changedPositions.addAll(e.nextTurn(this));
      }
    }

    // Increment the step counter
    this.step = this.step + 1;

    // Check if it's time to spawn an AirTanker
    if (this.step % this.turnsToSpawnAirTanker == 0) {
      // Spawn an AirTanker at a random edge position
      spawnAirTanker(changedPositions);
    }

    return changedPositions;
  }

  // Helper method to spawn an AirTanker
  private void spawnAirTanker(List<Position> changedPositions) {
    Random rand = new Random();
    int edge = rand.nextInt(4); // 0: top, 1: bottom, 2: left, 3: right
    Position position = null;

    if (edge == 0) { // Top edge (x == 0)
      int y = rand.nextInt(columnCount() - 1);
      position = new Position(0, y);
    } else if (edge == 1) { // Bottom edge (x == rowCount() - 1)
      int y = rand.nextInt(columnCount());
      position = new Position(rowCount() - 1, y);
    } else if (edge == 2) { // Left edge (y == 0)
      int x = rand.nextInt(rowCount() - 1);
      position = new Position(x, 0);
    } else if (edge == 3) { // Right edge (y == columnCount() - 1)
      int x = rand.nextInt(rowCount() - 1);
      position = new Position(x, columnCount() - 1);
    } else {
      // This else block is technically not necessary because edge will always be
      // between 0 and 3
      throw new IllegalStateException("Unexpected edge value: " + edge);
    }

    // Create a new AirTanker
    AirTanker airTanker = new AirTanker(position, this, getStepNumber());
    // Add the AirTanker to the board
    addEntityAtSquare(airTanker, position);

    // Record the changed position
    changedPositions.add(position);
  }

  private void generateRoads() {
    if(columnCount() < 10 || rowCount() < 10){
      return;
    }
    Random random = new Random();

    // Get board dimensions
    int rowCount = rowCount();      // Number of rows (vertical axis)
    int columnCount = columnCount(); // Number of columns (horizontal axis)

    // Decide randomly whether to set x or y to 0
    boolean setXToZero = random.nextBoolean();

    int x = 0;
    int y = 0;

    if (setXToZero) {
        // x is set to 0, y is random within column bounds
        x = 0;
        y = random.nextInt(columnCount);
    } else {
        // y is set to 0, x is random within row bounds
        x = random.nextInt(rowCount);
        y = 0;
    }

    Position startPosition = new Position(x, y);
    PathGenerator pathGenerator = new PathGenerator(this, startPosition, 4, columnCount());
    // Call generateEntitiesInLine to place the roads
    List<Position> snake = pathGenerator.generate();
    for(Position p : snake){
      List<Entity> entitiesAtSquare = List.copyOf(getStates(p).getEntities());
      for(Entity e: entitiesAtSquare){
        if(e instanceof Mountain || e instanceof Rockery || e instanceof Fire){
          clearCaseFrom(e, e.getPosition());
        }
      }
      addEntityAtSquare(new Road(p), p);
    }
}

  @Override
  public Board<Square> getBoard() {
    return this;
  }

  public void reset() {
    step = 0;
    super.getMatrix().clear();
    initScenario(super.getMatrix());
    placeInitialEntities(initialMap);
    generateRoads();
  }

}
Original line number Original line Diff line number Diff line
package model.firefighterscenario;

import java.util.ArrayList;
import java.util.List;

import javafx.scene.paint.Color;
import model.Board;
import model.Square;
import util.Position;
import util.PositionUtil;

public class MotorizedFireFighter extends FireFighter {
    private final Color viewColor = Color.CYAN;
    private static javafx.scene.image.Image cloudImage;


    static {
        try {
            cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/camion.png").toExternalForm());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public MotorizedFireFighter(Position position, Board<Square> b) {
        super(position, b);
    }

    @Override
    public List<Position> nextTurn(Board<Square> b) {
        List<Position> positions = new ArrayList<>();

        // Generate adjacent positions excluding mountains
        List<Position> adjacentPositions = PositionUtil.generateAdjacentPositions(getPosition(), b);
        adjacentPositions.removeIf(p -> b.doesSquareContainEntity(p, Mountain.class));

        // Check if there is fire in any adjacent positions
        boolean hasFire = adjacentPositions.stream()
                .anyMatch(p -> b.doesSquareContainEntity(p, Fire.class));

        boolean isStuck = isStuck();

        if (hasFire) {
            // Extinguish fires in adjacent positions
            positions.addAll(extinguish(adjacentPositions, b));

            if (isStuck) {
                // If stuck, attempt to move and extinguish after moving
                positions.addAll(moveAndExtinguish(b));
            }
        } else {
            // No fire adjacent; move and attempt to extinguish fires
            positions.addAll(moveAndExtinguish(b));
        }

        // Update last positions for stuck detection
        updateLastPositions();

        return positions;
    }

    @Override
    protected List<Position> moveAndExtinguish(Board<Square> b) {
        List<Position> positions = new ArrayList<>();

        // Find the nearest fire
        Position nearestFirePos;
        try {
            nearestFirePos = b.getNearestEntity(getPosition(), Fire.class, null);
        } catch (Exception e) {
            return List.of();
        }
        if (nearestFirePos != null) {
            // Get the next position after moving up to two steps towards the fire
            Position nextPos = getNextPositionTowards(getPosition(), nearestFirePos, b, 2);

            if (nextPos != null && !nextPos.equals(getPosition())) {
                // Move the firefighter
                b.clearCaseFrom(this, getPosition()); // Clear old position
                positions.add(new Position(getPosition().x(), getPosition().y())); // Add old position
                setPosition(nextPos);
                b.addEntityAtSquare(this, nextPos); // Add to new position
                positions.add(nextPos); // Add new position

                // After moving, attempt to extinguish fires adjacent to new position
                List<Position> newAdjacentPositions = PositionUtil.generateAdjacentPositions(nextPos, b);
                newAdjacentPositions.removeIf(p -> b.doesSquareContainEntity(p, Mountain.class));
                positions.addAll(extinguish(newAdjacentPositions, b));
            }
        }

        return positions;
    }

    // Overloaded method to handle multiple steps
    protected Position getNextPositionTowards(Position currentPos, Position targetPos, Board<Square> b, int steps) {
        Position nextPos = currentPos;

        for (int i = 0; i < steps; i++) {
            Position stepPos = super.getNextPositionTowards(nextPos, targetPos, b);

            if (stepPos == null || stepPos.equals(nextPos)) {
                // Can't move further
                break;
            }

            nextPos = stepPos;
        }

        return nextPos;
    }

    // Override to ensure correct color
    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    // Ensure the last positions are updated correctly
    protected void updateLastPositions() {
        if (lastThreePosition.size() >= 3) {
            lastThreePosition.remove(0);
        }
        lastThreePosition.add(this.getPosition());
    }

    @Override
    public javafx.scene.image.Image getImage() {
        return cloudImage;
    }
}
 No newline at end of file
Original line number Original line Diff line number Diff line
package model.firefighterscenario;

import java.util.List;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Position;

public class Mountain implements Entity{
    private final int priority = 0;
    Position position;
    private int age;
    private final Color viewColor = Color.CHOCOLATE;
    private static javafx.scene.image.Image cloudImage;

    static {
        try {
            cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/montagne.png").toExternalForm());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Mountain(Position p ){
        this.position = p;
    }


    public Mountain(Position p ,  int age){
        this.position = p;
        this.age = age;
    }

    @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 this.age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void incrementAge() {
        this.age += 1;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }

    @Override
    public javafx.scene.image.Image getImage() {
        return cloudImage;
    }
}
Original line number Original line Diff line number Diff line
package model.firefighterscenario;

import java.util.List;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Position;

public class Rockery implements Entity{
    private final int priority = 0;
    Position position;
    private int age;
    private int burn;
    private final Color viewColor = Color.LIMEGREEN;
    private static javafx.scene.image.Image cloudImage;


    static {
        try {
            cloudImage = new javafx.scene.image.Image(Cloud.class.getResource("/view/icons/rochers.png").toExternalForm());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Rockery(Position p ){
        this.position = p;
        this.burn = 0;
    }


    public Rockery(Position p , int age){
        System.out.println("age : " + age);
        this.position = p;
        this.age = age;
    }

    @Override
    public List<Position> nextTurn(Board<Square> board) {
        return List.of();
    }

    @Override
    public Position getPosition() {
        return this.position;
    }
    public int getBurn(){
        return this.burn;
    }
    public void incrementBurn(){
        this.burn = burn + 1;
    }

    @Override
    public void setPosition(Position p) {
        this.position = p;

    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void incrementAge() {
        this.age += 1;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }


    public void resetBurn() {
        this.burn = 0;
    }

    @Override
    public javafx.scene.image.Image getImage() {
        return cloudImage;
    }
}
Original line number Original line Diff line number Diff line
package model.rockpapercisor;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Position;
import util.PositionUtil;

public class Cisor implements Entity {
    private final int priority = 0;
    Position position;
    private int age;
    private final Color viewColor = Color.RED;

    public Cisor(Position p) {
        this.position = p;
    }

    public Cisor(Position p, int age) {
        this.position = p;
        this.age = age;
    }

    @Override
    public List<Position> nextTurn(Board<Square> board) {
        Position target = null;
        target = board.getNearestEntity(position, Paper.class, null);

        Position ennemy = null;

        ennemy = board.getNearestEntity(position, Rock.class, null);
        if(ennemy == null && target == null){
            return List.of();
        }
        Position nextPos = null;
        // Vérifier la proximité d'un ennemi avant de choisir la direction.
        if (ennemy != null && PositionUtil.getManhattanDistance(position, ennemy) < 5) {
            nextPos = PositionUtil.getNextPositionAwayFrom(position, ennemy, board);
        } else if(target != null){
            nextPos = PositionUtil.getNextPositionTowards(position, target, board);
        }

        if (nextPos != null && board.doesPositionExist(nextPos)) {
            board.addEntityAtSquare(this, nextPos);
            board.clearCaseFrom(this, position);
            Position oldPosition = new Position(position.x(), position.y());
            this.position = nextPos;
            if (board.doesSquareContainEntity(nextPos, Paper.class)) {
                List<Entity> entities = board.getStates(nextPos).getEntities();
                entities.removeIf(p -> p instanceof Paper);
            }

            List<Position> result = new ArrayList<>();
            if (target != null)
                result.add(target);
            if (oldPosition != null)
                result.add(oldPosition);
            if (position != null)
                result.add(position);
            if (ennemy != null)
                result.add(ennemy);
            return result;

        }
        return List.of();
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public void setPosition(Position p) {
        this.position = p;

    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void incrementAge() {
        this.age += 1;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null || getClass() != obj.getClass())
            return false;
        Cisor cisor = (Cisor) obj;
        return age == cisor.age &&
                Objects.equals(position, cisor.position);
    }

    @Override
    public int hashCode() {
        return Objects.hash(position, age);
    }
}
Original line number Original line Diff line number Diff line
package model.rockpapercisor;

import java.util.ArrayList;
import java.util.List;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Position;
import util.PositionUtil;

public class Paper implements Entity {
    private final int priority = 0;
    Position position;
    private int age;
    private final Color viewColor = Color.GRAY;

    public Paper(Position p) {
        this.position = p;
    }

    public Paper(Position p, int age) {
        this.position = p;
        this.age = age;
    }

    @Override
    public List<Position> nextTurn(Board<Square> board) {
        Position target = null;
        target = board.getNearestEntity(position, Rock.class, null);

        Position ennemy = null;
        ennemy = board.getNearestEntity(position, Cisor.class, null);
        if(ennemy == null && target == null){
            return List.of();
        }
        Position nextPos = null;
        // Vérifier la proximité d'un ennemi avant de choisir la direction.
        if (ennemy != null && PositionUtil.getManhattanDistance(position, ennemy) < 5) {
            nextPos = PositionUtil.getNextPositionAwayFrom(position, ennemy, board);
        } else if(target != null){
            nextPos = PositionUtil.getNextPositionTowards(position, target, board);
        }

        if (nextPos != null && board.doesPositionExist(nextPos)) {
            board.addEntityAtSquare(this, nextPos);
            board.clearCaseFrom(this, position);
            Position oldPosition = new Position(position.x(), position.y());
            this.position = nextPos;
            if (board.doesSquareContainEntity(nextPos, Rock.class)) {
                List<Entity> entities = board.getStates(nextPos).getEntities();
                entities.removeIf(p -> p instanceof Rock);
            }

            List<Position> result = new ArrayList<>();
            if (target != null)
                result.add(target);
            if (oldPosition != null)
                result.add(oldPosition);
            if (position != null)
                result.add(position);
            if (ennemy != null)
                result.add(ennemy);
            return result;

        }
        return List.of();
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public void setPosition(Position p) {
        this.position = p;

    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void incrementAge() {
        this.age += 1;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }
}
Original line number Original line Diff line number Diff line
package model.rockpapercisor;

import java.util.ArrayList;
import java.util.List;

import javafx.scene.paint.Color;
import model.Board;
import model.Entity;
import model.Square;
import util.Position;
import util.PositionUtil;

public class Rock implements Entity {
    private final int priority = 0;
    Position position;
    private int age;
    private final Color viewColor = Color.CHOCOLATE;

    public Rock(Position p) {
        this.position = p;
    }

    public Rock(Position p, int age) {
        this.position = p;
        this.age = age;
    }

    @Override
    public List<Position> nextTurn(Board<Square> board) {
        Position target = null;
        target = board.getNearestEntity(position, Cisor.class, null);

        Position ennemy = null;
        ennemy = board.getNearestEntity(position, Paper.class, null);

        if(ennemy == null && target == null){
            return List.of();
        }
        Position nextPos = null;
        // Vérifier la proximité d'un ennemi avant de choisir la direction.
        if (ennemy != null && PositionUtil.getManhattanDistance(position, ennemy) < 5) {
            nextPos = PositionUtil.getNextPositionAwayFrom(position, ennemy, board);
        } else if(target != null){
            nextPos = PositionUtil.getNextPositionTowards(position, target, board);
        }

        if (nextPos != null && board.doesPositionExist(nextPos)) {
            board.addEntityAtSquare(this, nextPos);
            board.clearCaseFrom(this, position);
            Position oldPosition = new Position(position.x(), position.y());
            this.position = nextPos;
            if (board.doesSquareContainEntity(nextPos, Cisor.class)) {
                List<Entity> entities = board.getStates(nextPos).getEntities();
                entities.removeIf(p -> p instanceof Cisor);
            }

            List<Position> result = new ArrayList<>();
            if (target != null)
                result.add(target);
            if (oldPosition != null)
                result.add(oldPosition);
            if (position != null)
                result.add(position);
            if (ennemy != null)
                result.add(ennemy);
            return result;

        }
        return List.of();
    }

    @Override
    public Position getPosition() {
        return this.position;
    }

    @Override
    public void setPosition(Position p) {
        this.position = p;

    }

    @Override
    public int getAge() {
        return this.age;
    }

    @Override
    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public void incrementAge() {
        this.age += 1;
    }

    @Override
    public Color getViewColor() {
        return this.viewColor;
    }

    @Override
    public int getPriority() {
        return this.priority;
    }
}
Original line number Original line Diff line number Diff line
package model.rockpapercisor;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import model.Board;
import model.Entity;
import model.EntityFactory;
import model.Model;
import model.Scenario;
import model.Square;
import util.Position;

public class RockPaperCisorScenario extends Scenario implements Model{
    public RockPaperCisorScenario(int columns, int rows, Map<EntityFactory, Integer> initialMap) {
        super(columns, rows, initialMap);
    }

    public List<Position> updateToNextGeneration() {
        ArrayList<Position> changedPositions = new ArrayList<>();
        Iterator<Square> iterator = getMatrix().iterator();
        while (iterator.hasNext()) {
          Square s = iterator.next();
          if (s.isEmpty())
            continue;
          if (s.getMaxAge() == 0) {
            s.incrementAllAges();
            continue;
          }
          if(s.getMaxAge()>stepNumber()+1){
            continue;
          }
          List<Entity> entities = new ArrayList<>(s.getEntities());
          for (Entity e : entities) {
            e.incrementAge();
            changedPositions.addAll(e.nextTurn(this));
          }
        }
        
        // Increment the step counter
        this.step = this.step + 1;
        return changedPositions;
      }

    @Override
    public Board<Square> getBoard() {
        return this;
    }
}
Original line number Original line Diff line number Diff line
@@ -2,6 +2,7 @@ module firefighter {
  requires javafx.controls;
  requires javafx.controls;
  requires javafx.fxml;
  requires javafx.fxml;
  requires javafx.graphics;
  requires javafx.graphics;
    requires java.sql;
    opens controller to javafx.fxml;
    opens controller to javafx.fxml;
  exports app;
  exports app;
  opens app to javafx.fxml;
  opens app to javafx.fxml;
+5 −0
Original line number Original line Diff line number Diff line
package util;

public enum Direction {
    NORTH, EAST, WEST, SOUTH
}
+109 −0
Original line number Original line Diff line number Diff line
package util;

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.Set;
import java.util.stream.Collectors;

import model.Board;
import model.Square;

public class DirectionUtils {
    
    /**
     * Obtient l'incrément sur l'axe X pour une direction donnée.
     *
     * @param direction La direction.
     * @return L'incrément sur l'axe X.
     */
    public static int getXIncrement(Direction direction) {
        switch (direction) {
            case NORTH:
                return -1;
            case SOUTH:
                return 1;
            default:
                return 0;
        }
    }

    /**
     * Obtient l'incrément sur l'axe Y pour une direction donnée.
     *
     * @param direction La direction.
     * @return L'incrément sur l'axe Y.
     */
    public static int getYIncrement(Direction direction) {
        switch (direction) {
            case EAST:
                return 1;
            case WEST:
                return -1;
            default:
                return 0;
        }
    }

    /**
     * Obtient la direction opposée.
     *
     * @param direction La direction actuelle.
     * @return La direction opposée.
     */
    public static Direction getOpposite(Direction direction) {
        switch (direction) {
            case NORTH:
                return Direction.SOUTH;
            case SOUTH:
                return Direction.NORTH;
            case EAST:
                return Direction.WEST;
            case WEST:
                return Direction.EAST;
            default:
                throw new IllegalArgumentException("Direction non supportée : " + direction);
        }
    }

    /**
     * Choisit une direction aléatoire parmi toutes les directions disponibles.
     *
     * @param random Instance de Random.
     * @return Une direction aléatoire.
     */
    public static Direction getRandomDirection() {
        Random r = new Random();
        Direction[] directions = Direction.values();
        return directions[r.nextInt(directions.length)];
    }

    /**
     * Obtient les directions valides en fonction des exclusions et de la position actuelle.
     *
     * @param currentDirection              La direction actuelle.
     * @param permanentlyExcludedDirections Les directions définitivement exclues.
     * @param temporarilyExcludedDirections Les directions temporairement exclues.
     * @param board                         Le plateau de jeu.
     * @param currentPosition               La position actuelle.
     * @return Une liste de directions valides.
     */
    public static List<Direction> getValidDirections(
            Direction currentDirection,
            Set<Direction> permanentlyExcludedDirections,
            Set<Direction> temporarilyExcludedDirections,
            Board<Square> board,
            Position currentPosition
    ) {
        return Arrays.stream(Direction.values())
                .filter(dir -> dir != currentDirection)
                .filter(dir -> dir != getOpposite(currentDirection))
                .filter(dir -> !permanentlyExcludedDirections.contains(dir))
                .filter(dir -> !temporarilyExcludedDirections.contains(dir))
                .filter(dir -> {
                    Position nextPos = currentPosition.move(dir);
                    return board.doesPositionExist(nextPos);
                })
                .collect(Collectors.toList());
    }
}
+151 −0
Original line number Original line Diff line number Diff line
package util;


import java.util.ArrayList;
import java.util.Iterator;
import java.util.NoSuchElementException;

import model.Square;
import model.doctorviruspatient.Patient;
import model.firefighterscenario.Fire;
import model.firefighterscenario.FireFighter;

public class Matrix<E> implements Iterable<E> {
    private ArrayList<ArrayList<E>> matrix;
    private final int rows;
    private final int columns;

    public Matrix(int rows, int columns) {
        this.rows = rows;
        this.columns = columns;
        this.matrix = new ArrayList<>(rows);
        
        // Initialiser chaque ligne de la matrice
        for (int i = 0; i < rows; i++) {
            ArrayList<E> row = new ArrayList<>(columns);
            // Initialiser chaque colonne avec des valeurs nulles
            for (int j = 0; j < columns; j++) {
                row.add(null);
            }
            this.matrix.add(row);
        }
    }

    public E get(int x, int y) {
        validateIndex(x, y);
        return matrix.get(x).get(y);
    }

    public E set(int x, int y, E object) {
        validateIndex(x, y);
        return matrix.get(x).set(y, object);
    }

    public void clear() {
        this.matrix = new ArrayList<>(rows);
        // Initialiser chaque ligne de la matrice
        for (int i = 0; i < rows; i++) {
            ArrayList<E> row = new ArrayList<>(columns);
            // Initialiser chaque colonne avec des valeurs nulles
            for (int j = 0; j < columns; j++) {
                row.add(null);
            }
            this.matrix.add(row);
        }
    }

    public int size() {
        return rows * columns;
    }

    public int getColumns(){
        return this.columns;
    }
    public int getRows(){
        return this.rows;
    }

    
    public void displayMatrix() {
        System.out.print("  ");
        for (int j = 0; j < columns; j++) {
            System.out.print("___ ");
        }
        System.out.println();

        for (int i = 0; i < rows; i++) {
            System.out.print("| ");
            for (int j = 0; j < columns; j++) {
                if (matrix.get(i).get(j) != null) {
                    Square s = (Square) matrix.get(i).get(j);
                    if(s.getEntities().stream().anyMatch(p -> p instanceof Fire)){
                        System.out.print(" F | ");
                    }   
                    else if(s.getEntities().stream().anyMatch(p -> p instanceof FireFighter)){
                        System.out.print(" ff | ");
                    }
                    else if(s.getEntities().stream().anyMatch(p -> p instanceof Patient)){
                        System.out.print(" P | ");
                    }
                    else if(s.getEntities().stream().anyMatch(p -> p instanceof model.doctorviruspatient.Virus)){
                        System.out.print(" V | ");
                    }else{
                        System.out.print("  | ");
                    }
                    
                } else {
                    System.out.print("   | ");
                }
            }
            System.out.println();
            System.out.print("  ");
            for (int j = 0; j < columns; j++) {
                System.out.print("___ ");
            }
            System.out.println();
        }
    }
    

    private void validateIndex(int x, int y) {
        if (x < 0 || x >= rows || y < 0 || y >= columns) {
            throw new IndexOutOfBoundsException("Indices x: "+ x + " y: " + y + " hors limites pour la matrice.");
        }
    }

    public boolean validateIndex(Position position){
        return position.x() >= 0 && position.x() < rows && position.y() >= 0 && position.y() < columns;
    }
    

    

    @Override
    public Iterator<E> iterator() {
        return new MatrixIterator();
    }

    private class MatrixIterator implements Iterator<E> {
        private int row = 0;
        private int col = 0;

        @Override
        public boolean hasNext() {
            return row < rows && col < columns;
        }

        @Override
        public E next() {
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            E element = matrix.get(row).get(col);
            col++;
            if (col >= columns) {
                col = 0;
                row++;
            }
            return element;
        }
    }
}
+191 −0
Original line number Original line Diff line number Diff line
package util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;

import model.Board;

public class PathGenerator {
    private Board<?> board;
    private final Random random = new Random();
    private final int maxStepsInDirection;
    private final int minimumRoadLength;
    private final Set<Direction> initialExcludedDirections = new HashSet<>();
    private final Position anchor;

    private Position currentPosition;
    private Direction currentDirection;
    private Direction excludedDirection = null;
    private Direction lastDirection = null;
    private int stepsInCurrentDirection = 0;
    private int roadLength = 1;
    private final List<Position> path = new ArrayList<>();

    /**
     * Constructeur de PathGenerator.
     *
     * @param board               Le plateau de jeu.
     * @param anchor              La position de départ.
     * @param maxStepsInDirection Nombre maximal de pas dans une direction avant de changer.
     * @param minimumRoadLength   Longueur minimale du chemin.
     */
    public PathGenerator(Board<?> board, Position anchor, int maxStepsInDirection, int minimumRoadLength) {
        this.board = board;
        this.anchor = anchor;
        this.maxStepsInDirection = maxStepsInDirection;
        this.minimumRoadLength = minimumRoadLength;
        initializePath();
    }

    /**
     * Génère le chemin en respectant les contraintes.
     *
     * @return Liste des positions formant le chemin.
     */
    public List<Position> generate() {
        while (true) {
            if (canMoveInCurrentDirection()) {
                moveInCurrentDirection();
                if (shouldChangeDirection()) {
                    if (!changeDirection(false)) break;
                }
            } else {
                if (!handleObstacle()) break;
            }
        }
        return roadLength >= minimumRoadLength ? path : regeneratePath();
    }
    public void setBoard(Board<?> board){
        this.board = board;
    }
    /** Initialise le chemin avec l'ancre et choisit la première direction. */
    private void initializePath() {
        path.clear();
        roadLength = 1;
        stepsInCurrentDirection = 0;
        currentPosition = anchor;
        path.add(anchor);
        currentDirection = DirectionUtils.getRandomDirection();
        initialExcludedDirections.clear();
        initialExcludedDirections.add(currentDirection);
        lastDirection = currentDirection;
        excludedDirection = null;
    }

    /** Vérifie si le mouvement dans la direction actuelle est possible. */
    private boolean canMoveInCurrentDirection() {
        Position nextPosition = currentPosition.move(currentDirection);
        return board.doesPositionExist(nextPosition);
    }

    /** Effectue le mouvement dans la direction actuelle. */
    private void moveInCurrentDirection() {
        currentPosition = currentPosition.move(currentDirection);
        path.add(currentPosition);
        roadLength++;
        stepsInCurrentDirection++;
        excludedDirection = DirectionUtils.getOpposite(currentDirection);
    }

    /** Détermine si un changement de direction est nécessaire. */
    private boolean shouldChangeDirection() {
        return stepsInCurrentDirection >= maxStepsInDirection;
    }

    /**
     * Change la direction actuelle.
     *
     * @param mustContinue Si true, la nouvelle direction doit permettre de continuer le chemin.
     * @return true si la direction a été changée avec succès, sinon false.
     */
    private boolean changeDirection(boolean mustContinue) {
        Direction newDirection = chooseNewDirection(mustContinue);
        if (newDirection == null) return false;
        updateDirection(newDirection);
        return true;
    }

    /** Gère le cas où le mouvement n'est pas possible (obstacle ou bord du plateau). */
    private boolean handleObstacle() {
        if (roadLength < minimumRoadLength) {
            return changeDirection(true);
        }
        return false;
    }

    /** Réinitialise et génère à nouveau le chemin. */
    private List<Position> regeneratePath() {
        initializePath();
        return generate();
    }

    /** Met à jour la direction actuelle et les compteurs associés. */
    private void updateDirection(Direction newDirection) {
        currentDirection = newDirection;
        stepsInCurrentDirection = 0;
        lastDirection = newDirection;
        excludedDirection = DirectionUtils.getOpposite(newDirection);
    }

    /**
     * Choisit une nouvelle direction valide en tenant compte des exclusions.
     *
     * @param mustContinue Si true, la direction doit permettre de continuer le chemin.
     * @return La nouvelle direction choisie ou null si aucune n'est valide.
     */
    private Direction chooseNewDirection(boolean mustContinue) {
        Set<Direction> exclusions = new HashSet<>(initialExcludedDirections);
        Collections.addAll(exclusions, excludedDirection, lastDirection);
        List<Direction> validDirections = getValidDirections(exclusions, currentPosition);

        if (mustContinue) {
            validDirections = filterDirectionsToContinue(validDirections, currentPosition);
        }

        return validDirections.isEmpty() ? null : validDirections.get(random.nextInt(validDirections.size()));
    }

    /**
     * Obtient les directions valides à partir de la position actuelle.
     *
     * @param exclusions       Les directions à exclure.
     * @param currentPosition  La position actuelle.
     * @return Liste des directions valides.
     */
    private List<Direction> getValidDirections(Set<Direction> exclusions, Position currentPosition) {
        List<Direction> validDirections = new ArrayList<>();
        for (Direction dir : Direction.values()) {
            if (!exclusions.contains(dir)) {
                Position nextPos = currentPosition.move(dir);
                if (board.doesPositionExist(nextPos)) {
                    validDirections.add(dir);
                }
            }
        }
        return validDirections;
    }

    /**
     * Filtre les directions pour s'assurer qu'elles permettent de continuer le chemin.
     *
     * @param directions      Liste des directions valides.
     * @param currentPosition La position actuelle.
     * @return Liste des directions permettant de continuer.
     */
    private List<Direction> filterDirectionsToContinue(List<Direction> directions, Position currentPosition) {
        List<Direction> filtered = new ArrayList<>();
        for (Direction dir : directions) {
            Position nextPos = currentPosition.move(dir);
            Set<Direction> futureExclusions = new HashSet<>(initialExcludedDirections);
            futureExclusions.add(DirectionUtils.getOpposite(dir));
            if (!getValidDirections(futureExclusions, nextPos).isEmpty()) {
                filtered.add(dir);
            }
        }
        return filtered;
    }
}
Original line number Original line Diff line number Diff line
package util;
package util;


public record Position(int row, int column) {
public record Position(int x, int y) {

        // Méthode pour déplacer la position dans une direction donnée
        public Position move(Direction direction) {
            int newX = this.x + DirectionUtils.getXIncrement(direction);
            int newY = this.y + DirectionUtils.getYIncrement(direction);
            return new Position(newX, newY);
        }
}
}
+324 −0
Original line number Original line Diff line number Diff line
package util;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import model.Board;
import model.Square;
import model.firefighterscenario.FireFighter;
import model.firefighterscenario.Mountain;

public class PositionUtil {
    /**
     * Calcule et retourne une liste de positions situées à une distance de
     * Manhattan spécifique à partir d'une position donnée.
     * La distance de Manhattan entre deux points (x1, y1) et (x2, y2) est donnée
     * par |x1 - x2| + |y1 - y2|.
     * Cette méthode génère toutes les positions possibles à cette distance dans une
     * grille de taille spécifiée.
     *
     * @param fromPos  la position de départ à partir de laquelle la distance est
     *                 calculée.
     * @param distance la distance de Manhattan à utiliser pour trouver les
     *                 positions.
     * @param rows     le nombre de lignes dans la grille.
     * @param cols     le nombre de colonnes dans la grille.
     * @return une liste de positions à la distance de Manhattan spécifiée de la
     *         position initiale, qui se trouvent également dans les limites de la
     *         grille.
     */
    public static List<Position> getPositionsAtManhattanDistance(Position fromPos, int distance, int rows, int cols) {
        List<Position> positions = new ArrayList<>();
        int x0 = fromPos.x();
        int y0 = fromPos.y();

        // Générer toutes les positions à une distance de Manhattan donnée
        for (int dx = -distance; dx <= distance; dx++) {
            int dy = distance - Math.abs(dx);

            int[] dyOptions = { dy, -dy };
            for (int deltaY : dyOptions) {
                int x = x0 + dx;
                int y = y0 + deltaY;

                // Vérifier si la position est dans les limites de la matrice
                if (x >= 0 && x < rows && y >= 0 && y < cols) {
                    positions.add(new Position(x, y));
                }
            }
        }

        return positions;
    }

    /**
     * Génère une liste de positions adjacentes à une position donnée, en vérifiant
     * si chaque position est valide dans le contexte du jeu.
     *
     * @param position la position de départ pour laquelle générer les positions
     *                 adjacentes.
     * @param board    l'objet représentant le plateau de jeu qui permet de vérifier
     *                 si une position existe.
     * @return une liste des positions adjacentes valides.
     */
    public static List<Position> generateAdjacentPositions(Position position, Board<Square> board) {
        int x = position.x();
        int y = position.y();

        return Stream.of(
                new Position(x, y + 1),
                new Position(x + 1, y),
                new Position(x, y - 1),
                new Position(x - 1, y))
                .filter(p -> board.doesPositionExist(p))
                .collect(Collectors.toList());
    }

    public static List<Position> generateAdjacentPositions(Position position, Board<Square> board, int distance) {

        List<Position> positions = new ArrayList<Position>();
        for(int i = 0; i < 4; i++){

        }
        return positions;
    }


    // Méthode pour générer toutes les positions adjacentes (y compris les
    // diagonales)
    public static List<Position> generateAllAdjacentPositions(Position position, Board<Square> board) {
        int x = position.x();
        int y = position.y();

        List<Position> positions = new ArrayList<>();

        // Liste des 8 déplacements possibles
        int[][] deltas = {
                { 1, 0 }, // x+1, y
                { -1, 0 }, // x-1, y
                { 0, 1 }, // x, y+1
                { 0, -1 }, // x, y-1
                { 1, 1 }, // x+1, y+1
                { -1, -1 }, // x-1, y-1
                { 1, -1 }, // x+1, y-1
                { -1, 1 } // x-1, y+1
        };

        for (int[] delta : deltas) {
            int newX = x + delta[0];
            int newY = y + delta[1];
            Position p = new Position(newX, newY);
            if (board.doesPositionExist(p)) {
                positions.add(p);
            }
        }

        return positions;
    }

    public static int getManhattanDistance(Position p1, Position p2) {
        return Math.abs(p1.x() - p2.x()) + Math.abs(p1.y() - p2.y());
    }

        /**
     * Generates all positions within a specified radius from a center position.
     * It uses Manhattan distance by default.
     *
     * @param center The center position.
     * @param radius The radius (distance) from the center position.
     * @param board  The board to consider boundaries.
     * @return A list of positions within the radius.
     */
    public static List<Position> getPositionsInRadius(Position center, int radius, Board<Square> board) {
        List<Position> positions = new ArrayList<>();

        int startX = center.x() - radius;
        int endX = center.x() + radius;
        int startY = center.y() - radius;
        int endY = center.y() + radius;

        for (int x = startX; x <= endX; x++) {
            for (int y = startY; y <= endY; y++) {
                Position pos = new Position(x, y);
                if (board.doesPositionExist(pos)) {
                    // Calculate Manhattan distance from the center
                    int distance = Math.abs(center.x() - x) + Math.abs(center.y() - y);
                    if (distance <= radius) {
                        positions.add(pos);
                    }
                }
            }
        }

        return positions;
    }


    public static Position getNextPositionTowards(Position currentPos, Position targetPos, Board<Square> b) {
        // Generate adjacent positions
        List<Position> possibleMoves = PositionUtil.generateAllAdjacentPositions(currentPos, b);
    
        // Filter out positions that are not empty or contain obstacles
        possibleMoves.removeIf(p -> b.doesSquareContainEntity(p, Mountain.class));
    
        // If no possible moves, return null
        if (possibleMoves.isEmpty()) {
            return null;
        }
    
        // Calculate the current distance to the target
        int currentDistance = PositionUtil.getManhattanDistance(currentPos, targetPos);
    
        // Initialize variables to find the best moves
        int minDistance = Integer.MAX_VALUE;
        List<Position> bestMoves = new ArrayList<>();
    
        for (Position move : possibleMoves) {
            int distance = PositionUtil.getManhattanDistance(move, targetPos);
    
            // Skip positions occupied by other firefighters
            if (b.doesSquareContainEntity(move, FireFighter.class)) {
                continue;
            }
    
            // Find positions that minimize the distance
            if (distance < minDistance) {
                minDistance = distance;
                bestMoves.clear();
                bestMoves.add(move);
            } else if (distance == minDistance) {
                bestMoves.add(move);
            }
        }
    
        // If no better move is found, consider moves that maintain the same distance
        if (bestMoves.isEmpty()) {
            minDistance = currentDistance;
            for (Position move : possibleMoves) {
                int distance = PositionUtil.getManhattanDistance(move, targetPos);
                if (distance == minDistance) {
                    bestMoves.add(move);
                }
            }
        }
    
        // If still no move is found, stay in the current position
        if (bestMoves.isEmpty()) {
            return currentPos;
        }
    
        // Select a move from the best moves (e.g., randomly or based on additional criteria)
        Random r = new Random();
        
        Position nextMove = bestMoves.get(r.nextInt(bestMoves.size()));
    
        return nextMove;
    }

    public static Position getNextPositionAwayFrom(Position currentPos, Position targetPos, Board<Square> b) {
        // Générer les positions adjacentes
        List<Position> possibleMoves = PositionUtil.generateAllAdjacentPositions(currentPos, b);
    
        // Filtrer les positions qui ne sont pas vides ou contiennent des obstacles
        possibleMoves.removeIf(p -> b.doesSquareContainEntity(p, Mountain.class));
    
        // Si aucune possibilité de déplacement, retourner null
        if (possibleMoves.isEmpty()) {
            return null;
        }
    
        // Calculer la distance actuelle par rapport à la cible
        int currentDistance = PositionUtil.getManhattanDistance(currentPos, targetPos);
    
        // Initialiser les variables pour trouver les meilleurs déplacements
        int maxDistance = Integer.MIN_VALUE;
        List<Position> bestMoves = new ArrayList<>();
    
        for (Position move : possibleMoves) {
            int distance = PositionUtil.getManhattanDistance(move, targetPos);
    
            // Ignorer les positions occupées par d'autres entités, comme les pompiers
            if (b.doesSquareContainEntity(move, FireFighter.class)) {
                continue;
            }
    
            // Trouver les positions qui maximisent la distance
            if (distance > maxDistance) {
                maxDistance = distance;
                bestMoves.clear();
                bestMoves.add(move);
            } else if (distance == maxDistance) {
                bestMoves.add(move);
            }
        }
    
        // Si aucun meilleur déplacement n'est trouvé, considérer les mouvements qui maintiennent la même distance
        if (bestMoves.isEmpty()) {
            maxDistance = currentDistance;
            for (Position move : possibleMoves) {
                int distance = PositionUtil.getManhattanDistance(move, targetPos);
                if (distance == maxDistance) {
                    bestMoves.add(move);
                }
            }
        }
    
        // Si toujours aucun mouvement n'est trouvé, rester à la position actuelle
        if (bestMoves.isEmpty()) {
            return currentPos;
        }
    
        // Sélectionner un mouvement parmi les meilleurs mouvements (par exemple aléatoirement ou selon des critères supplémentaires)
        Random r = new Random();
    
        Position nextMove = bestMoves.get(r.nextInt(bestMoves.size()));
    
        return nextMove;
    }
    public static Direction getOppositeDirection(Direction direction) {
        switch (direction) {
            case NORTH: return Direction.SOUTH;
            case SOUTH: return Direction.NORTH;
            case EAST:  return Direction.WEST;
            case WEST:  return Direction.EAST;
            default:    throw new IllegalArgumentException("Direction non supportée : " + direction);
        }
    }

    /**
 * Détermine la direction principale (NORTH, SOUTH, EAST, WEST) de toPos par rapport à fromPos.
 *
 * @param fromPos la position de départ.
 * @param toPos   la position de destination.
 * @return la direction principale de toPos par rapport à fromPos.
 */
public static Direction getDirectionFromTwoPoints(Position fromPos, Position toPos) {
    int deltaX = toPos.x() - fromPos.x();
    int deltaY = toPos.y() - fromPos.y();

    if (deltaX == 0 && deltaY == 0) {
        return null; // Les positions sont identiques
    }

    if (Math.abs(deltaX) > Math.abs(deltaY)) {
        // Mouvement principalement vers l'Est ou l'Ouest
        if (deltaX > 0) {
            return Direction.EAST;
        } else {
            return Direction.WEST;
        }
    } else {
        // Mouvement principalement vers le Nord ou le Sud
        if (deltaY > 0) {
            return Direction.SOUTH;
        } else {
            return Direction.NORTH;
        }
    }
}


}
 No newline at end of file
Original line number Original line Diff line number Diff line
package model;
package util;


import util.Position;
import java.util.Collection;

import java.util.HashMap;
import java.util.*;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;


public class TargetStrategy {
public class TargetStrategy {


@@ -12,7 +17,7 @@ public class TargetStrategy {
     * @param targets positions that are targeted.
     * @param targets positions that are targeted.
     * @return the position next to the current position that is on the path to the closest target.
     * @return the position next to the current position that is on the path to the closest target.
     */
     */
    Position neighborClosestToFire(Position position, Collection<Position> targets,
    public Position neighborClosestToFire(Position position, Collection<Position> targets,
                                   Map<Position,List<Position>>neighbors) {
                                   Map<Position,List<Position>>neighbors) {
        Set<Position> seen = new HashSet<Position>();
        Set<Position> seen = new HashSet<Position>();
        HashMap<Position, Position> firstMove = new HashMap<Position, Position>();
        HashMap<Position, Position> firstMove = new HashMap<Position, Position>();
Original line number Original line Diff line number Diff line
package view;
package view;
import javafx.scene.image.Image;

import java.util.List;


import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.Canvas;
import javafx.scene.paint.Color;
import javafx.scene.paint.Color;
import javafx.util.Pair;
import javafx.util.Pair;
import util.Position;
import util.Position;


import java.util.List;

public class FirefighterGrid extends Canvas implements Grid<ViewElement>{
public class FirefighterGrid extends Canvas implements Grid<ViewElement>{


    //private void paintElementAtPosition(ViewElement element, Position position) {
      //  paintBox(position.x(), position.y(), element.getColor());
    //}


    private void paintElementAtPosition(ViewElement element, Position position) {
    private void paintElementAtPosition(ViewElement element, Position position) {
        paintBox(position.row(), position.column(), element.color);
        // Efface la case pour éviter les superpositions
        clearBox(position.x(), position.y());

        // Vérifie si une image est définie dans l'élément
        if (element.getImage() != null) {
            Image image = element.getImage();
            getGraphicsContext2D().drawImage(image, position.y() * boxWidth, position.x() * boxHeight, boxWidth, boxHeight);
        } else {
            // Sinon, utilise une couleur
            paintBox(position.x(), position.y(), element.getColor());
        }
        }
    }

    private int boxWidth;
    private int boxWidth;
    private int boxHeight;
    private int boxHeight;
    private int columnCount;
    private int columnCount;
@@ -27,7 +44,7 @@ public class FirefighterGrid extends Canvas implements Grid<ViewElement>{
    private void clear(List<Pair<Position, ViewElement>> positionedElements) {
    private void clear(List<Pair<Position, ViewElement>> positionedElements) {
        for (Pair<Position, ViewElement> positionElement : positionedElements) {
        for (Pair<Position, ViewElement> positionElement : positionedElements) {
            Position position = positionElement.getKey();
            Position position = positionElement.getKey();
            clearBox(position.row(), position.column());
            clearBox(position.x(), position.y());
        }
        }
    }
    }


Original line number Original line Diff line number Diff line
package view;
package view;


import javafx.scene.image.Image;
import javafx.scene.paint.Color;
import javafx.scene.paint.Color;


public enum ViewElement {
public class ViewElement {
  FIREFIGHTER(Color.BLUE), FIRE(Color.RED), EMPTY(Color.WHITE);
    private final Color color;
  final Color color;
    private final Image image;
  ViewElement(Color color) {

    // Constructeur avec couleur uniquement
    public ViewElement(Color color) {
        this.color = color;
        this.color = color;
        this.image = null;
    }

    // Constructeur avec image
    public ViewElement(Image image) {
        this.color = null;
        this.image = image;
    }

    public Color getColor() {
        return color;
    }

    public Image getImage() {
        return image;
    }
    }
}
}
Original line number Original line Diff line number Diff line
<?xml version="1.0" encoding="UTF-8"?>
<?xml version="1.0" encoding="UTF-8"?>


<?import javafx.scene.control.Button?>
<?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.HBox?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.layout.VBox?>
<?import view.FirefighterGrid?>
<?import view.FirefighterGrid?>


<?import javafx.scene.control.ToggleButton?>
<?import javafx.scene.control.Separator?>
<?import javafx.scene.control.Label?>
<HBox styleClass="background" stylesheets="@DarkTheme.css"
<HBox styleClass="background" stylesheets="@DarkTheme.css"
      xmlns="http://javafx.com/javafx" xmlns:fx="http://javafx.com/fxml"
      xmlns="http://javafx.com/javafx"
      xmlns:fx="http://javafx.com/fxml"
      fx:controller="controller.Controller">
      fx:controller="controller.Controller">
  <VBox>
  <VBox>
    <Separator maxHeight="-Infinity" maxWidth="-Infinity"
    <Separator maxHeight="-Infinity" maxWidth="-Infinity"
               prefHeight="24.0" prefWidth="200.0" />
               prefHeight="24.0" prefWidth="200.0" />
    <Label maxHeight="-Infinity" maxWidth="-Infinity" alignment="CENTER" prefHeight="24.0" prefWidth="200.0"
    <Label maxHeight="-Infinity" maxWidth="-Infinity"
           alignment="CENTER" prefHeight="24.0" prefWidth="200.0"
           text="Generation number" />
           text="Generation number" />
    <Label fx:id="generationNumberLabel" alignment="CENTER" contentDisplay="TEXT_ONLY"
    <Label fx:id="generationNumberLabel"
           maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="24.0" prefWidth="200.0"/>
           alignment="CENTER" contentDisplay="TEXT_ONLY"
           maxHeight="-Infinity" maxWidth="-Infinity"
           prefHeight="24.0" prefWidth="200.0" />
    <Separator maxHeight="-Infinity" maxWidth="-Infinity"
    <Separator maxHeight="-Infinity" maxWidth="-Infinity"
               prefHeight="24.0" prefWidth="200.0" />
               prefHeight="24.0" prefWidth="200.0" />
    <Button fx:id="restartButton" maxHeight="-Infinity" maxWidth="-Infinity"
    <Button fx:id="restartButton" maxHeight="-Infinity" maxWidth="-Infinity"
            mnemonicParsing="false" onAction="#restartButtonAction" prefHeight="24.0" prefWidth="200.0"
            mnemonicParsing="false" onAction="#restartButtonAction"
            text="Restart"/>
            prefHeight="24.0" prefWidth="200.0" text="Restart" />
    <Button fx:id="oneStepButton" maxHeight="-Infinity" maxWidth="-Infinity"
    <Button fx:id="oneStepButton" maxHeight="-Infinity" maxWidth="-Infinity"
            mnemonicParsing="false" onAction="#oneStepButtonAction" prefHeight="24.0" prefWidth="200.0"
            mnemonicParsing="false" onAction="#oneStepButtonAction"
            text="One step"/>
            prefHeight="24.0" prefWidth="200.0" text="One step" />
    <ToggleButton fx:id="playToggleButton" maxHeight="-Infinity" maxWidth="-Infinity"
    <ToggleButton fx:id="playToggleButton" maxHeight="-Infinity" maxWidth="-Infinity"
                  mnemonicParsing="false" onAction="#playToggleButtonAction" prefHeight="24.0"
                  mnemonicParsing="false" onAction="#playToggleButtonAction"
                  prefWidth="200.0" styleClass="button" text="Play"/>
                  prefHeight="24.0" prefWidth="200.0"
                  styleClass="button" text="Play" />
    <ToggleButton fx:id="pauseToggleButton" maxHeight="-Infinity" maxWidth="-Infinity"
    <ToggleButton fx:id="pauseToggleButton" maxHeight="-Infinity" maxWidth="-Infinity"
                  mnemonicParsing="false" onAction="#pauseToggleButtonAction" prefHeight="24.0"
                  mnemonicParsing="false" onAction="#pauseToggleButtonAction"
                  prefWidth="200.0" styleClass="button" text="Pause"/>
                  prefHeight="24.0" prefWidth="200.0"
                  styleClass="button" text="Pause" />
  </VBox>
  </VBox>
  <FirefighterGrid fx:id="grid"
  <FirefighterGrid fx:id="grid"
                   xmlns="http://javafx.com/javafx"
                   xmlns="http://javafx.com/javafx"
+0 −39
Original line number Original line Diff line number Diff line
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);
  }

}
Original line number Original line Diff line number Diff line
package view;
package view;


import org.junit.jupiter.api.Test;
import static org.assertj.core.api.Assertions.*;


import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;


public class FirefighterGridTest {
public class FirefighterGridTest {
  @Test
  @Test
  void testColumnCount(){
  void testColumnCount(){
    Grid<ViewElement> grid = new FirefighterGrid();
    Grid<ViewElement> grid = new FirefighterGrid();
    grid.setDimensions(20,10,10,10);
    grid.setDimensions(20,10,5,5);
    assertThat(grid.columnCount()).isEqualTo(20);
    assertThat(grid.columnCount()).isEqualTo(20);
  }
  }
  @Test
  @Test
  void testRowCount(){
  void testRowCount(){
    Grid<ViewElement> grid = new FirefighterGrid();
    Grid<ViewElement> grid = new FirefighterGrid();
    grid.setDimensions(20,10,10,10);
    grid.setDimensions(20,10,5,5);
    assertThat(grid.rowCount()).isEqualTo(10);
    assertThat(grid.rowCount()).isEqualTo(10);
  }
  }
}
}