import java.awt.Color; import java.util.ArrayList; /** * La classe {@link Game} représente le moteur principal du jeu. *

* Elle contient le modèle (les voitures, l'état du jeu, la carte), * la vue (Track, Dashboard, Rankboard) et la logique de contrôle * (pause, boucle de jeu, observateurs). *

*/ public class Game { /** * {@link CarInfo} est une structure simple qui contient les informations * nécessaires pour créer une voiture dans le jeu : son nom et sa couleur. */ public record CarInfo(String name, Color color) {} /** * {@link VisualInfo} est un enregistrement (record) qui décrit une vue graphique * à afficher dans le jeu (par exemple un tableau de bord, une piste ou un classement). *

* Chaque instance contient toutes les informations nécessaires pour créer et positionner * cette vue à l’écran. *

* * @param type le type concret de la vue (classe héritant de {@link GameView}) * @param title le titre de la fenêtre ou du panneau associé * @param width la largeur en pixels de la vue * @param height la hauteur en pixels de la vue * @param x la position horizontale (en pixels) de la vue sur l’écran * @param y la position verticale (en pixels) de la vue sur l’écran */ public record VisualInfo ( Class type, String title, int width, int height, int x, int y ) {} /** * Builder pour créer une instance de {@link Game} de façon fluide. *

* Permet d'ajouter des voitures, de définir la carte, l'état initial et * le temps entre les étapes du jeu avant de construire l'objet final. *

*/ public static class Builder { /** Liste des voitures à créer pour le jeu */ private ArrayList cars = new ArrayList<>(); /** Liste des voitures à créer pour le jeu */ private ArrayList visuals = new ArrayList<>(); /** État initial du jeu */ private State state = new State(); /** Temps entre chaque step du jeu */ private int time = 1000; /** Carte sur laquelle se déroule le jeu */ private Map map = null; /** Ajoute une voiture à la liste */ public Builder addCar(CarInfo car) { cars.add(car); return this; } /** Supprime une voiture de la liste */ public Builder remCar(CarInfo car) { cars.remove(car); return this; } /** Définit la carte du jeu */ public Builder setMap(Map map) { this.map = map; return this; } /** Définit l'état initial du jeu */ public Builder setState(State s) { this.state = s; return this; } /** Définit le temps entre chaque étape du jeu */ public Builder setTime(int time) { this.time = time; return this; } public Builder addVisual(Class type, String title, int width, int height, int x, int y) { visuals.add(new VisualInfo(type, title, width, height, x, y)); return this; } private void buildVisual(Game game) { for (VisualInfo v : visuals) { if (v.type == Rankboard.class) game.addObserver(new Rankboard(game, v.title, v.width, v.height, v.x, v.y)); else if (v.type == Track.class) game.addObserver(new Track(game, v.title, v.width, v.height, v.x, v.y)); else if (v.type == Dashboard.class) { for (CarInfo info : cars) { if (info.name.equals(v.title)) { Car car = game.getCars().stream() .filter(c -> c.getName().equals(info.name)) .findFirst() .orElse(null); if (car != null) game.addObserver(new Dashboard(game, car, v.title, v.width, v.height, v.x, v.y)); break; } } } } } /** * Construit l'instance de {@link Game} avec les paramètres définis. *

* Si la carte n'a pas été définie, le programme s'arrête. *

*/ public Game build() { if (map == null) { System.err.println("Vous devez définir une carte avant de construire le jeu !"); System.exit(1); } Game game = new Game(map, cars, state, time); buildVisual(game); return game; } } private final Map map; /** État du jeu (par exemple, positions, carburant) */ private final State state; /** Temps entre chaque étape du jeu en millisecondes */ private final int time; /** Liste des voitures du jeu */ private final ArrayList cars = new ArrayList<>(); /** Liste des observateurs pour la mise à jour des vues */ private final ArrayList obs = new ArrayList<>(); /** Indique si le jeu est en pause */ private boolean isPaused = false; /** * Constructeur principal. * * @param map carte du jeu * @param carInfos liste des informations des voitures à créer * @param state état initial du jeu * @param time temps entre chaque étape du jeu */ public Game(Map map, ArrayList carInfos, State state, int time) { this.map = map; this.state = state; this.time = time; init(carInfos); } /** * Initialise le jeu en créant les vues et les objets {@link Car}. *

* Chaque voiture obtient un Dashboard, la piste est affichée avec Track, * et le Rankboard est créé pour afficher le classement. *

* * @param carInfos liste des informations des voitures * @return l'instance de Game */ private Game init(ArrayList carInfos) { final int loop = map.getPathSize(); // Création de chaque voiture avec son Dashboard for (CarInfo ci : carInfos) cars.add(new Car(ci.name, ci.color, loop, state)); return this; } /** * Vérifie si le jeu est terminé. *

* Le jeu est fini si au moins une voiture a épuisé son carburant. *

* * @return true si le jeu est terminé */ private boolean isFinish() { for (Car car : cars) { if (car.getFuel() == 0) return true; } return false; } /** * Ajoute un observateur pour recevoir les mises à jour du jeu. * * @param o observateur * @return instance de Game pour chaîner */ public Game addObserver(GObserver o) { obs.add(o); return this; } /** * Supprime un observateur. * * @param o observateur * @return instance de Game pour chaîner */ public Game remObserver(GObserver o) { obs.remove(o); return this; } /** * Bascule l'état de pause du jeu. *

* Si le jeu était en pause, il est relancé et les threads en attente sont notifiés. *

* * @return true si le jeu est maintenant en pause */ public synchronized boolean togglePause() { if (isPaused) notifyAll(); isPaused = !isPaused; return isPaused; } public void notifyObservers() { for (GObserver o : obs) { boolean isSuccess = o.apply(); if (!isSuccess) { System.err.println("Une erreur s'est produite pendant le jeu."); System.exit(1); } } } /** * Exécute un cycle du jeu *

* Chaque voiture effectue son action, puis les observateurs sont notifiés. * La boucle attend si le jeu est en pause. *

* * @throws InterruptedException si le thread est interrompu pendant wait() */ private void step() throws InterruptedException { for (Car car : cars) { synchronized(this) { while (isPaused) wait(); } car.run(); notifyObservers(); } } /** * Boucle principale du jeu. *

* Tant que le jeu n'est pas terminé, on exécute les étapes et on met à jour * les vues toutes les 'time' millisecondes. *

*/ public void run() { while (!isFinish()) { try { step(); Thread.sleep(time); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Fini!\nVoici le score :"); for (Car c : cars) { System.out.println(c.getName() + "\t" + c.getScore()); } System.exit(0); } public ArrayList getCars() { return cars; } public Map getMap() { return map; } public State getState() { return state; } public boolean getPause() { return isPaused; } }