mirror of
https://github.com/guezoloic/L3-racing-game.git
synced 2026-03-30 11:56:14 +00:00
feat!: refonte du projet, bug niveau initialisation
This commit is contained in:
@@ -1,285 +1,160 @@
|
||||
package model;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import model.car.Car;
|
||||
import model.map.Map;
|
||||
import visual.*;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
@FunctionalInterface
|
||||
/**
|
||||
* L'interface utilisée pour Game.
|
||||
*/
|
||||
public static interface Observer {
|
||||
/**
|
||||
*
|
||||
* @return true si la fonction s'est bien passé sinon false (le programme va se
|
||||
* stopper)
|
||||
* @return si False le programme s'arrete, sinon il continue
|
||||
*/
|
||||
public boolean apply();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@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).
|
||||
* <p>
|
||||
* Chaque instance contient toutes les informations nécessaires pour créer et
|
||||
* positionner
|
||||
* cette vue à l’écran.
|
||||
* </p>
|
||||
*
|
||||
* @param type le type concret de la vue (classe héritant de {@link Game})
|
||||
* @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
|
||||
*/
|
||||
private record VisualInfo(Class<? extends Game> type,
|
||||
String title, int width, int height, int x, int y) {
|
||||
}
|
||||
|
||||
public static class GameFactory {
|
||||
public static Map defaultMap() {
|
||||
return Map.fromInts(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, 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, 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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
|
||||
});
|
||||
}
|
||||
|
||||
public static Game defaultGame(Map map) {
|
||||
return new Game.Builder()
|
||||
.addCar("Voiture à LUWIK", Color.BLUE)
|
||||
.addCar("Voiture à CHARAZADE", Color.PINK)
|
||||
.addCar("Voiture de UPEC", Color.RED)
|
||||
.addVisual(Dashboard.class, "Voiture à LUWIK", 300, 200, 1000, 0)
|
||||
.addVisual(Dashboard.class, "Voiture à CHARAZADE", 300, 200, 1000, 200)
|
||||
.addVisual(Dashboard.class, "Voiture de UPEC", 300, 200, 1000, 400)
|
||||
.addVisual(Track.class, "Piste Formule 1", 1000, 500, 1, 1)
|
||||
.addVisual(Rankboard.class, "Score", 200, 200, 0, 510)
|
||||
.setTime(1000)
|
||||
.setMap(map)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Builder pour créer une instance de {@link Game} de façon fluide.
|
||||
* <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<>();
|
||||
/** Liste des voitures à créer pour le jeu */
|
||||
private ArrayList<VisualInfo> visuals = new ArrayList<>();
|
||||
/** État initial du jeu */
|
||||
private Car.State state = Car.State.NORMAL;
|
||||
/** Temps entre chaque step du jeu */
|
||||
private int time = 1000;
|
||||
/** Carte sur laquelle se déroule le jeu */
|
||||
private final List<Game.Observer> OBSERVERS = new ArrayList<>();
|
||||
private int time = 500;
|
||||
private Map map = null;
|
||||
|
||||
/** Ajoute une voiture à la liste */
|
||||
public Builder addCar(String name, Color color) {
|
||||
cars.add(new CarInfo(name, color));
|
||||
public Builder car(Car car) {
|
||||
this.OBSERVERS.add(car);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Supprime une voiture de la liste */
|
||||
public Builder removeCar(String name, Color color) {
|
||||
cars.forEach((car) -> {
|
||||
if (car.name == name && car.color == color)
|
||||
cars.remove(car);
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Définit la carte du jeu */
|
||||
public Builder setMap(Map map) {
|
||||
public Builder map(Map map) {
|
||||
this.map = map;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Définit l'état initial du jeu */
|
||||
public Builder setState(Car.State s) {
|
||||
this.state = s;
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Définit le temps entre chaque étape du jeu */
|
||||
public Builder setTime(int time) {
|
||||
public Builder time(int time) {
|
||||
this.time = time;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addVisual(Class<? extends Game> type, String title,
|
||||
int width, int height, int x, int y) {
|
||||
visuals.add(new VisualInfo(type, title, width, height, x, y));
|
||||
public Builder rankboard(String title, int width,
|
||||
int height, int x, int y) {
|
||||
this.OBSERVERS.add(new Rankboard(null, title, width, height, x, y));
|
||||
return this;
|
||||
}
|
||||
|
||||
private void buildVisual(Game game) {
|
||||
for (VisualInfo visual : visuals) {
|
||||
Game view = null;
|
||||
|
||||
if (visual.type == Rankboard.class) {
|
||||
view = new Rankboard(game, visual.title, visual.width,
|
||||
visual.height, visual.x, visual.y);
|
||||
} else if (visual.type == Track.class) {
|
||||
view = new Track(game, visual.title, visual.width,
|
||||
visual.height, visual.x, visual.y);
|
||||
} else if (visual.type == Dashboard.class) {
|
||||
Car car = game.getCars().stream()
|
||||
.filter((e) -> visual.title.equals(e.getName()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
|
||||
view = new Dashboard(game,
|
||||
car,
|
||||
visual.title, visual.width,
|
||||
visual.height, visual.x, visual.y);
|
||||
}
|
||||
if (view != null)
|
||||
game.addObserver(view);
|
||||
}
|
||||
public Builder track(String title, int width,
|
||||
int height, int x, int y) {
|
||||
this.OBSERVERS.add(new Track(null, title, width, height, x, y));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder dashboard(Car car, String title, int width,
|
||||
int height, int x, int y) {
|
||||
this.OBSERVERS.add(new Dashboard(null, car, title, width, height, x, y));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder dashboards(int width, int height, int x, int y) {
|
||||
List<Car> cars = OBSERVERS.stream()
|
||||
.filter(o -> o instanceof Car)
|
||||
.map(o -> (Car) o)
|
||||
.toList();
|
||||
|
||||
int index = 0;
|
||||
for (Car car : cars) {
|
||||
dashboard(car, car.getName(), width, height, x, y + (height * index++));
|
||||
}
|
||||
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);
|
||||
Game game = new Game();
|
||||
|
||||
for (Game.Observer observer : this.OBSERVERS) {
|
||||
switch (observer) {
|
||||
case GameView gameView -> {
|
||||
gameView.setGame(game);
|
||||
}
|
||||
case Car car -> {
|
||||
car.setMap(map);
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Game game = new Game(map, cars, state, time);
|
||||
buildVisual(game);
|
||||
game.map = this.map;
|
||||
game.time = this.time;
|
||||
game.observers = this.OBSERVERS;
|
||||
return game;
|
||||
}
|
||||
}
|
||||
|
||||
private final Map map;
|
||||
/** État du jeu (par exemple, positions, carburant) */
|
||||
private final Car.State state;
|
||||
/** Temps entre chaque étape du jeu en millisecondes */
|
||||
private final int time;
|
||||
private Map map;
|
||||
private int time;
|
||||
private List<Game.Observer> observers;
|
||||
|
||||
/** Liste des voitures du jeu */
|
||||
private final ArrayList<Car> cars = new ArrayList<>();
|
||||
/** Liste des observateurs pour la mise à jour des vues */
|
||||
private final ArrayList<Observer> obs = new ArrayList<>();
|
||||
private boolean isPaused;
|
||||
|
||||
/** Indique si le jeu est en pause */
|
||||
private boolean isPaused = false;
|
||||
private Game() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, Car.State state, int time) {
|
||||
private Game(Map map, int time, List<Game.Observer> observer) {
|
||||
this.map = map;
|
||||
this.state = state;
|
||||
this.time = time;
|
||||
|
||||
init(carInfos);
|
||||
this.observers = observer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise le jeu en créant les vues et les objets {@link Car}.
|
||||
* <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 de chaque voiture avec son Dashboard
|
||||
for (CarInfo ci : carInfos)
|
||||
cars.add(new Car(ci.name, ci.color, map)
|
||||
.setState(state));
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le jeu est terminé.
|
||||
* <p>
|
||||
* Le jeu est fini si au moins une voiture a épuisé son carburant.
|
||||
* </p>
|
||||
*
|
||||
* @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(Observer o) {
|
||||
obs.add(o);
|
||||
observers.add(o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un observateur.
|
||||
*
|
||||
* @param o observateur
|
||||
* @return instance de Game pour chaîner
|
||||
*/
|
||||
public Game remObserver(Observer o) {
|
||||
obs.remove(o);
|
||||
public Game removeObserver(Observer o) {
|
||||
observers.remove(o);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
private boolean isFinish() {
|
||||
for (Game.Observer observer : observers) {
|
||||
switch (observer) {
|
||||
case Car car -> {
|
||||
if (car.getFuel() > 0)
|
||||
return false;
|
||||
}
|
||||
default -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void notifyObservers() {
|
||||
for (Observer o : observers) {
|
||||
if (!o.apply()) {
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void run() {
|
||||
try {
|
||||
while (!isFinish()) {
|
||||
synchronized (this) {
|
||||
while (isPaused)
|
||||
wait();
|
||||
}
|
||||
notifyObservers();
|
||||
Thread.sleep(time);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized boolean togglePause() {
|
||||
if (isPaused)
|
||||
notifyAll();
|
||||
@@ -287,73 +162,17 @@ public class Game {
|
||||
return isPaused;
|
||||
}
|
||||
|
||||
public void notifyObservers() {
|
||||
for (Observer 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
|
||||
* <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 void step() throws InterruptedException {
|
||||
for (Car car : cars) {
|
||||
synchronized (this) {
|
||||
while (isPaused)
|
||||
wait();
|
||||
}
|
||||
|
||||
car.run();
|
||||
notifyObservers();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
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<Car> getCars() {
|
||||
return cars;
|
||||
public List<Car> getCars() {
|
||||
return observers.stream()
|
||||
.filter(o -> o instanceof Car)
|
||||
.map(o -> (Car) o)
|
||||
.toList();
|
||||
}
|
||||
|
||||
public Map getMap() {
|
||||
return map;
|
||||
}
|
||||
|
||||
public Car.State getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public boolean getPause() {
|
||||
return isPaused;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user