Skip to content
Snippets Groups Projects
Commit ea4b8208 authored by Yanis O's avatar Yanis O
Browse files

[Ajout] Generation des routes

parent ce56e87c
No related branches found
No related tags found
No related merge requests found
Pipeline #40633 failed
......@@ -13,16 +13,16 @@ import javafx.stage.Stage;
public class SimulatorApplication extends javafx.application.Application {
private static final String VIEW_RESOURCE_PATH = "/view/view.fxml";
private static final String APP_NAME = "Firefighter simulator";
private static final int ROW_COUNT = 40;
private static final int COLUMN_COUNT = 40;
private static final int BOX_WIDTH = 15;
private static final int BOX_HEIGHT = 15;
public static final int INITIAL_FIRE_COUNT = 4;
public static final int INITIAL_FIREFIGHTER_COUNT = 18;
public static final int INITIAL_MOTORIZED_FIREFIGHTER_COUNT = 5;
public static final int INITIAL_CLOUD_COUNT = 9;
public static final int INITIAL_MOUNTAIN_COUNT= 18;
public static final int TURNS_FOR_SPAWNING_AIRTANKER = 8;
private static final int ROW_COUNT = 80;
private static final int COLUMN_COUNT = 80;
private static final int BOX_WIDTH = 8;
private static final int BOX_HEIGHT = 8;
public static final int INITIAL_FIRE_COUNT = 6;
public static final int INITIAL_FIREFIGHTER_COUNT = 32;
public static final int INITIAL_MOTORIZED_FIREFIGHTER_COUNT = 10;
public static final int INITIAL_CLOUD_COUNT = 15;
public static final int INITIAL_MOUNTAIN_COUNT= 28;
public static final int TURNS_FOR_SPAWNING_AIRTANKER = 4;
private Stage primaryStage;
private Parent view;
......
......@@ -128,7 +128,7 @@ public class Controller {
int rowCount, int initialFireCount, int initialFirefighterCount, int initialMotorizedFirefightersCount, int initialcloudCount, int initialmountaincount, int turnsForSpawningAirTanker) {
grid.setDimensions(columnCount, rowCount, squareWidth, squareHeight);
Board<Square> model = new FireFighterScenario(columnCount, rowCount);
Map<EntityFactory, Integer> entityCounts = new HashMap<>();
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);
......
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;
public class EntitySpawner {
......@@ -68,4 +72,198 @@ public class EntitySpawner {
}
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 = 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 = 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 = 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;
}
}
private 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);
}
}
}
......@@ -56,7 +56,7 @@ public class Fire implements Entity {
}
// Skip if the position contains a Cloud (if clouds prevent fire spread)
if (board.doesSquareContainEntity(p, Cloud.class)) {
if (board.doesSquareContainEntity(p, Road.class)) {
continue;
}
......
......@@ -7,6 +7,7 @@ import java.util.Map;
import java.util.Random;
import util.Matrix;
import util.PathGenerator;
import util.Position;
import util.PositionUtil;
......@@ -26,10 +27,10 @@ public class FireFighterScenario extends EntityScenario implements Board<Square>
public void placeInitialEntities(Map<EntityFactory, Integer> entityCounts) {
EntitySpawner spawner = new EntitySpawner(this);
spawner.spawnEntities(entityCounts);
generateRoads();
this.initialMap = entityCounts;
}
public Square getStates(Position position) {
if (position.x() > matrix.size() || position.y() > matrix.size()) {
throw new IllegalArgumentException(
......@@ -62,7 +63,6 @@ public void placeInitialEntities(Map<EntityFactory, Integer> entityCounts) {
}
}
public int rowCount() {
return matrix.getRows();
}
......@@ -115,7 +115,6 @@ public void placeInitialEntities(Map<EntityFactory, Integer> entityCounts) {
// Helper method to spawn an AirTanker
private void spawnAirTanker(List<Position> changedPositions) {
System.out.println("Spawning AirTanker");
Random rand = new Random();
int edge = rand.nextInt(4); // 0: top, 1: bottom, 2: left, 3: right
Position position = null;
......@@ -133,14 +132,13 @@ private void spawnAirTanker(List<Position> changedPositions) {
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
// This else block is technically not necessary because edge will always be
// between 0 and 3
throw new IllegalStateException("Unexpected edge value: " + edge);
}
System.out.println("Position: " + position.toString());
// Create a new AirTanker
AirTanker airTanker = new AirTanker(position, this, getStepNumber());
System.out.println(" direction : " + airTanker.getDirection());
// Add the AirTanker to the board
addEntityAtSquare(airTanker, position);
......@@ -148,8 +146,6 @@ private void spawnAirTanker(List<Position> changedPositions) {
changedPositions.add(position);
}
public Position getNearestEntity(Position fromPos, Class<?> entityType) {
int rows = matrix.getRows();
int cols = matrix.getColumns();
......@@ -217,4 +213,46 @@ private void spawnAirTanker(List<Position> changedPositions) {
}
return true;
}
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){
clearCaseFrom(e, e.getPosition());
}
}
addEntityAtSquare(new Road(p), p);
}
}
}
package model;
import java.util.List;
import javafx.scene.paint.Color;
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;
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;
}
}
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());
}
}
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 final 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();
}
/** 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;
}
}
package util;
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);
}
}
......@@ -75,8 +75,6 @@ public class PositionUtil {
}
public static List<Position> generateAdjacentPositions(Position position, Board<Square> board, int distance) {
int x = position.x();
int y = position.y();
List<Position> positions = new ArrayList<Position>();
for(int i = 0; i < 4; i++){
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment