package model; import java.awt.Point; import java.util.ArrayList; import java.util.function.Function; /** *

Map représente le circuit de course.

*

Cette classe contient:

* */ public class Map { /** * Tableau 2D représentant le circuit. * Chaque élément est une instance de {@link Circuit}. */ private Circuit[][] map; /** * Liste des coordonnées représentant le chemin du START au FINISH. */ private ArrayList pathMap; /** * Crée une nouvelle instance de Map à partir d'un tableau générique * et d'une fonction de transformation. * * @param Type des éléments du tableau source * @param fn Fonction qui transforme un élément T en Circuit * @param array Tableau 2D d'éléments T * @return Une nouvelle instance de Map */ public static Map create(Function fn, T[][] map) { int lenX = map[0].length; int lenY = map.length; Circuit[][] newmap = new Circuit[lenY][lenX]; for (int y = 0; y < lenY; y++) { for (int x = 0; x < lenX; x++) { newmap[y][x] = fn.apply(map[y][x]); } } return new Map(newmap); } /** * Crée une map à partir d'un tableau d'entiers. *
    *
  • 0 -> EMPTY
  • *
  • -1 -> ROAD
  • *
  • -2 -> START
  • *
  • -3 -> FINISH
  • *
  • autres -> YROAD avec la valeur associée
  • *
* * @param array Tableau 2D d'entiers * @return Une nouvelle instance de Map */ public static Map fromInts(Integer[][] map) { return create((i) -> switch (i) { case 0 -> new Circuit(Circuit.Cell.EMPTY); case -1 -> new Circuit(Circuit.Cell.ROAD); case -2 -> new Circuit(Circuit.Cell.START); case -3 -> new Circuit(Circuit.Cell.FINISH); default -> new Circuit(Circuit.Cell.YROAD, i); }, map); } /** * Crée une map à partir d'un tableau de caractères. *
    *
  • '#' -> ROAD
  • *
  • 'S' -> START
  • *
  • 'F' -> FINISH
  • *
  • '0'-'9' -> YROAD avec valeur correspondante
  • *
  • autres -> EMPTY
  • *
* * @param array Tableau 2D de caractères * @return Une nouvelle instance de Map */ public static Map fromChars(Character[][] map) { return create((i) -> switch (i) { case '#' -> new Circuit(Circuit.Cell.ROAD); case 'S' -> new Circuit(Circuit.Cell.START); case 'F' -> new Circuit(Circuit.Cell.FINISH); case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' -> new Circuit(Circuit.Cell.YROAD, i - '0'); default -> new Circuit(Circuit.Cell.EMPTY); }, map); } /** * Constructeur privé utilisé par les méthodes statiques de création. * Initialise la map et construit le chemin. * Si la map est invalide ou impossible à résoudre, termine le programme. * * @param map Tableau 2D de Circuit */ private Map(Circuit[][] map) { this.map = map; boolean isPossible = this.buildPath(); if (!isPossible) { System.err.println("La map contient des doublons ou est impossible à finir!"); System.exit(1); } } /** * Construit le chemin entre START et FINISH. * * @return true si un chemin valide existe, false sinon */ private boolean buildPath() { Point[] p = bindPath(); if (p == null) return false; Point start = p[0]; Point end = p[1]; this.pathMap = new ArrayList<>(); return followPath(start, end); } /** * Cherche les points START et FINISH sur la map. * * @return Un tableau de 2 éléments : {START, FINISH} ou null si la map est invalide */ private Point[] bindPath() { Point start = null; Point end = null; for (int i = 0; i < map.length; i++) { for (int j = 0; j < map[0].length; j++) { switch (map[i][j].getType()) { case Circuit.Cell.START: if (start == null) start = new Point(j, i); else return null; break; case Circuit.Cell.FINISH: if (end == null) end = new Point(j, i); else return null; break; default: break; } } } if (start == null || end == null) return null; return new Point[] {start, end}; } /** * Suivi du chemin depuis START jusqu'à FINISH. * Parcours les cases ROAD et YROAD jusqu'à atteindre FINISH. * * @param start Point de départ * @param end Point d'arrivée * @return true si un chemin complet a été trouvé, false sinon */ private boolean followPath(Point start, Point end) { // remettre à 0 la liste pathMap.clear(); // positions Point current = start; Point previous = null; int[][] coord = { {1, 0}, // haut {-1, 0}, // bas {0, -1}, // gauche {0, 1} // droite }; // sécurité pour éviter les boucles infinie int step = 0; int max = map.length * map[0].length; for (; step < max; step++) { pathMap.add(current); if (current.equals(end)) return true; boolean moved = false; for (int[] pos : coord) { int x = current.x + pos[1]; int y = current.y + pos[0]; if ((x >= 0 && map[0].length > x) && (y >= 0 && map.length > y)) { Point next = new Point(x, y); if (next.equals(previous)) continue; Circuit element = getElement(x, y); Circuit cElement = getElement(current.x, current.y); if (element.isRoad() || (element.isFinish() && cElement.isRoad())) { previous = current; current = next; moved = true; break; } } } // Si il est bloqué if (!moved) break; } return false; } /** * Retourne l'objet Circuit à la position (x, y). * * @param x Coordonnée X * @param y Coordonnée Y * @return L'objet Circuit */ public Circuit getElement(int x, int y) { return map[y][x]; } /** * Retourne le type de cellule (Circuit.Cell) à la position (x, y). * * @param x Coordonnée X * @param y Coordonnée Y * @return Le type de cellule Circuit.Cell */ public Circuit.Cell getCell(int x, int y) { return getElement(x, y).getType(); } /** * Retourne le point du chemin à l'indice donné. * * @param index Indice dans le chemin * @return Point correspondant */ public Point getPath(int i) { return this.pathMap.get(i); } /** * Retourne la taille du chemin trouvé. * * @return Nombre de points dans le chemin */ public int getPathSize() { return this.pathMap.size(); } }