feat(Game.java): ajout commentaire et amelioration

- enlever record (meilleur comprehension)
- mis un static pour savoir si il y a un server d'affichage
This commit is contained in:
2025-11-09 18:37:55 +01:00
parent 528ab1c91b
commit f2f7258962
2 changed files with 283 additions and 230 deletions

View File

@@ -1,232 +1,289 @@
import java.awt.Color; import java.awt.Color;
import java.awt.GraphicsEnvironment;
import java.util.ArrayList; import java.util.ArrayList;
/**
* La classe {@link Game} représente le moteur principal du jeu.
* <p>
* 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).
* </p>
*/
public class Game public class Game
{ {
public static record CarInfo(String name, Color color) {} /**
* {@link CarInfo} est une structure simple qui contient les informations
public static class Builder * nécessaires pour créer une voiture dans le jeu : son nom et sa couleur.
*/
public static class CarInfo
{ {
/** /** Nom de la voiture */
* Liste des voitures ajoutées au jeu. public final String name;
* On utilise ArrayList pour pouvoir ajouter/supprimer des voitures dynamiquement. /** Couleur de la voiture */
*/ public final Color color;
private ArrayList<CarInfo> cars = new ArrayList<>();
private State state = new State();
private int time = 1000;
private Map map = null; public CarInfo(String name, Color color)
{
public Builder addCar(CarInfo car) this.name = name;
{ this.color = color;
cars.add(car); }
return this;
}
public Builder remCar(CarInfo car)
{
cars.remove(car);
return this;
}
public Builder setMap(Map map)
{
this.map = map;
return this;
}
public Builder setState(State s)
{
this.state = s;
return this;
}
public Builder setTime(int time)
{
this.time = time;
return this;
}
public Game build()
{
if (map == null)
{
System.err.println("Vous devez declarer la map avant de build!");
System.exit(1);
}
return new Game(map, cars, state, time);
}
} }
private final Map map; /**
private final State state; * Builder pour créer une instance de {@link Game} de façon fluide.
private final int time; * <p>
* 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.
* </p>
*/
public static class Builder
{
/** Liste des voitures à créer pour le jeu */
private ArrayList<CarInfo> cars = new ArrayList<>();
/** État initial du jeu */
private State state = new State();
/** Temps entre chaque "step" du jeu (en millisecondes) */
private int time = 1000;
/** Carte sur laquelle se déroule le jeu */
private Map map = null;
private final ArrayList<Car> cars = new ArrayList<>(); /** Ajoute une voiture à la liste */
private final ArrayList<GObserver> obs = new ArrayList<>(); public Builder addCar(CarInfo car)
{
cars.add(car);
return this;
}
private boolean isPaused = false; /** Supprime une voiture de la liste */
public Builder remCar(CarInfo car)
{
cars.remove(car);
return this;
}
public Game(Map map, ArrayList<CarInfo> cars, State state, int time) /** Définit la carte du jeu */
{ public Builder setMap(Map map)
this.map = map; {
this.state = state; this.map = map;
this.time = time; return this;
}
init(cars); /** 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 */
private Game init(ArrayList<CarInfo> carInfos) public Builder setTime(int time)
{ {
new Track(map, cars, "Piste Formule 1", 1000, 500, 1, 1); this.time = time;
new Rankboard("Score", cars, 200, 200, 0, 510); return this;
}
final int loop = map.getPathSize();
int i = 0;
for (CarInfo ci : carInfos)
{
Car car = new Car(ci.name, ci.color, loop, state);
new Dashboard(car, car.toString(), this::togglePause, 300, 200, 1000, 200*i);
cars.add(car);
i++;
}
GameView.update(); /**
return this; * Construit l'instance de {@link Game} avec les paramètres définis.
} * <p>
* Si la carte n'a pas été définie, le programme s'arrête.
* </p>
*/
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 boolean isFinish() /** Carte sur laquelle le jeu se déroule */
{ private final Map map;
for (Car car : cars) /** État du jeu (par exemple, positions, carburant) */
{ private final State state;
if (car.getFuel() == 0) /** Temps entre chaque étape du jeu en millisecondes */
return true; private final int time;
}
return false;
}
public Game addObserver(GObserver o)
{
obs.add(o);
return this;
}
public Game remObserver(GObserver o)
{
obs.remove(o);
return this;
}
public synchronized boolean togglePause()
{
if (isPaused) notifyAll();
isPaused = !isPaused;
GameView.update();
return isPaused;
}
private synchronized void step() throws InterruptedException /** Liste des voitures du jeu */
{ private final ArrayList<Car> cars = new ArrayList<>();
for (int i = 0; i < cars.size(); i++) /** Liste des observateurs pour la mise à jour des vues */
{ private final ArrayList<GObserver> obs = new ArrayList<>();
// pause du jeu, while si on notifyall sans faire exprès un autre bout de code
while (isPaused)
wait();
cars.get(i).run();
for (GObserver o : obs) /** Indique si le jeu est en pause */
{ private boolean isPaused = false;
if (!o.apply())
{
System.err.println("Une erreur s'est produite pendant le jeu.");
System.exit(1);
}
}
}
}
public void run() /**
{ * Bloc statique exécuté au chargement de la classe pour vérifier
while (!isFinish()) * si le programme dispose d'un environnement graphique.
{ */
try static {
{ if (GraphicsEnvironment.isHeadless())
step(); {
GameView.update(); System.err.println("Aucun serveur d'affichage trouvé");
Thread.sleep(time); System.exit(1);
} }
catch (InterruptedException e) }
{ e.printStackTrace(); }
}
}
}
/**
* 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<CarInfo> carInfos, State state, int time)
{
this.map = map;
this.state = state;
this.time = time;
// private Game init(int pnumber, State state) init(carInfos);
// { }
// // ajout de voiture
// int loop = map.getPathSize();
// for (int i = 0; i < pnumber; i++) /**
// { * Initialise le jeu en créant les vues et les objets {@link Car}.
// cars[i] = new Car(loop, state); * <p>
* Chaque voiture obtient un Dashboard, la piste est affichée avec Track,
* et le Rankboard est créé pour afficher le classement.
* </p>
*
* @param carInfos liste des informations des voitures
* @return l'instance de Game
*/
private Game init(ArrayList<CarInfo> carInfos)
{
// Création des vues principales
new Track(map, cars, "Piste Formule 1", 1000, 500, 1, 1);
new Rankboard("Score", cars, 200, 200, 0, 510);
// Color color = (i < colors.size()) ? colors.get(i) : Color.BLUE; final int loop = map.getPathSize();
// // new Dashboard(cars[i], "Voiture " + (i+1), this::togglePause, color, 300, 300, 300, 300*i);
// }
// // new Track();
// // new Rankboard();
// return this;
// }
// private boolean isFinish() // Création de chaque voiture avec son Dashboard
// { int i = 0;
// for (Car car : cars) for (CarInfo ci : carInfos)
// { {
// if (car.getFuel() == 0) Car car = new Car(ci.name, ci.color, loop, state);
// return true; new Dashboard(car, car.toString(), this::togglePause, 300, 200, 1000, 200*i);
// } cars.add(car);
// return false; i++;
// } }
GameView.update();
return this;
}
// private void step() throws InterruptedException /**
// { * Vérifie si le jeu est terminé.
// for (Car c : cars) * <p>
// { * Le jeu est fini si au moins une voiture a épuisé son carburant.
// synchronized (this) * </p>
// { *
// // pause du jeu, while si on notifyall sans faire exprès un autre bout de code * @return true si le jeu est terminé
// while (paused) */
// { private boolean isFinish()
// wait(); {
// } for (Car car : cars)
// } {
// c.run(); if (car.getFuel() == 0)
return true;
}
return false;
}
// if (!isSuccess) /**
// { * Ajoute un observateur pour recevoir les mises à jour du jeu.
// System.err.println("Une erreur s'est produite pendant le jeu."); *
// System.exit(1); * @param o observateur
// } * @return instance de Game pour chaîner
// } */
// } public Game addObserver(GObserver o)
{
obs.add(o);
return this;
}
// public void run() /**
// { * Supprime un observateur.
// while (!isFinish()) *
// { * @param o observateur
// try * @return instance de Game pour chaîner
// { */
// step(); public Game remObserver(GObserver o)
// Thread.sleep(time); {
// } obs.remove(o);
// catch (InterruptedException e) return this;
// { e.printStackTrace(); } }
// }
// } /**
// } * Bascule l'état de pause du jeu.
* <p>
* Si le jeu était en pause, il est relancé et les threads en attente sont notifiés.
* </p>
*
* @return true si le jeu est maintenant en pause
*/
public synchronized boolean togglePause()
{
if (isPaused) notifyAll();
isPaused = !isPaused;
GameView.update();
return isPaused;
}
/**
* Exécute un cycle du jeu
* <p>
* Chaque voiture effectue son action, puis les observateurs sont notifiés.
* La boucle attend si le jeu est en pause.
* </p>
*
* @throws InterruptedException si le thread est interrompu pendant wait()
*/
private synchronized void step() throws InterruptedException
{
for (int i = 0; i < cars.size(); i++)
{
while (isPaused)
wait();
cars.get(i).run();
for (GObserver o : obs)
{
if (!o.apply())
{
System.err.println("Une erreur s'est produite pendant le jeu.");
System.exit(1);
}
}
}
}
/**
* Boucle principale du jeu.
* <p>
* Tant que le jeu n'est pas terminé, on exécute les étapes et on met à jour
* les vues toutes les 'time' millisecondes.
* </p>
*/
public void run()
{
while (!isFinish())
{
try
{
step();
GameView.update();
Thread.sleep(time);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
}

View File

@@ -1,34 +1,30 @@
import java.awt.Color; import java.awt.Color;
import java.awt.GraphicsEnvironment;
public class Main { public class Main {
public static void main(String[] args) throws InterruptedException public static void main(String[] args) throws InterruptedException
{ {
if (!GraphicsEnvironment.isHeadless()) Integer[][] map = new Integer[][]
{ {
Integer[][] map = new Integer[][] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -1, -1, 5, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, { 0, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 0, 0, -1, 0, 0, -1, 0 },
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -1, -1, 5, 0 }, { 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0 },
{ 0, 3, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 2, 0, 0, -1, 0, 0, -1, 0 }, { 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0 },
{ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0 }, { 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -1, -1, 2, 0, 0, -1, 0 },
{ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, -1, 0 }, { 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0 },
{ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, -1, -1, 2, 0, 0, -1, 0 }, { 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0 },
{ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0 }, { 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -3, 0 },
{ 0, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1, 0 }, { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
{ 0, 5, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -2, -3, 0 }, };
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
}; Map m = Map.fromInts(map);
Map m = Map.fromInts(map); Game game = new Game.Builder()
.addCar(new Game.CarInfo("Voiture 1", Color.BLUE))
Game game = new Game.Builder() .addCar(new Game.CarInfo("Voiture 2", Color.PINK))
.addCar(new Game.CarInfo("Voiture 1", Color.BLUE)) .addCar(new Game.CarInfo("Voiture 3", Color.RED))
.addCar(new Game.CarInfo("Voiture 2", Color.PINK)) .setMap(m)
.addCar(new Game.CarInfo("Voiture 3", Color.RED)) .build();
.setMap(m) game.run();
.build();
game.run();
}
} }
} }