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; } } }