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 static class CarInfo { /** Nom de la voiture */ public final String name; /** Couleur de la voiture */ public final Color color; public CarInfo(String name, Color color, boolean visual) { this.name = name; this.color = color; } } /** * 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<>(); /** É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; } /** * 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); } return new Game(map, cars, state, time); } } 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(); } } } public ArrayList getCars() { return cars; } public Map getMap() { return map; } public State getState() { return state; } public boolean getPause() { return isPaused; } /** Carte sur laquelle le jeu se déroule */ public Game addVisual(Class c, String title, int width, int height, int x, int y) { if (c == Rankboard.class) obs.add(new Rankboard(this, title, width, height, x, y)); else if (c == Track.class) obs.add(new Track(this, title, width, height, x, y)); else if (c == Dashboard.class) { for (Car car : cars) if (car.getName().equals(title)) { obs.add(new Dashboard(this, car, title, width, height, x, y)); break; } } return this; } }