ajout TP3

This commit is contained in:
James Boutaric
2025-10-09 10:20:53 +02:00
parent 84f5b7d973
commit b9c926f493
60 changed files with 1425 additions and 304 deletions

Binary file not shown.

View File

@@ -0,0 +1,99 @@
package fr.iut_fbleau.GameAPI;
import java.util.Iterator;
import java.util.Deque;
/**
* An abstract class implementing the interface IBoard.
*
* used to implement some things to do with the update of the next player
* which is the same for all games and provides also a minimal
* implantation of the history mechanism.
*/
public abstract class AbstractBoard implements IBoard{
private Player currentPlayer ;
private Deque<AbstractPly> history;
private void setNextPlayer(){
if (this.currentPlayer==Player.PLAYER1){
this.currentPlayer= Player.PLAYER2;
}
else
this.currentPlayer= Player.PLAYER1;
}
// Beware not checking if move is legal.
// To be used in do(AbstractPly c)
private void addPlyToHistory(AbstractPly c){
this.history.addLast(c);
}
// Beware not checking if history is not empty
// To be used in undo()
private void removePlyFromHistory(){
this.history.removeLast();
}
/**
* @return the current player
*/
public Player getcurrentPlayer(){
return this.currentPlayer;
}
/**
* Returns the game status
*
* @return true iff the game is over
*/
public abstract boolean isGameOver();
/**
*
* @return the result (null if not over)
*/
public abstract Result getResult();
/**
* checker of legal moves from this position
*
* @throws NullPointerException if the game is over
* @return the iterator
*/
public abstract boolean isLegal(AbstractPly c);
/**
* constructor of Iterator over legal moves from this position
*
* @throws NullPointerException if the game is over
* @return the iterator
*/
public abstract Iterator<AbstractPly> iterator();
/**
* Plays a given move on the plateau.
* Should update history using
* @throws IllegalArgumentException if the move is always illegal (say from the wrong game)
* @throws IllegalStateException if the move is not legal in this position
*
* @param AbstractPly to be played
*
*/
public abstract void doPly(AbstractPly c);
/**
* Resets the plateau to the position before the last move.
*
* @throws IllegalStateException if nothing to undo in history
*
*/
public abstract void undoPly();
}

Binary file not shown.

View File

@@ -0,0 +1,26 @@
package fr.iut_fbleau.GameAPI;
import java.util.Iterator;
/**
* The abstract class for a game Player.
*/
public abstract class AbstractGamePlayer {
// not a band, but which type of player I am in the game (PLAYER1 or PLAYER2).
private Player iAm;
// Le joueur réel pourrait avoir besoin de connaître un constructeur de coup?
// pas pour l'instant.
/**
*
*
* @throws UnsupportedOperationException if the method is not yet implemented
*
* @throws IllegalStateException if the Situation is already in the bookmarks
*/
public abstract AbstractPly giveYourMove(IBoard p);
}

Binary file not shown.

View File

@@ -0,0 +1,5 @@
package fr.iut_fbleau.GameAPI;
public abstract class AbstractPly {
private Player joueur;
}

Binary file not shown.

View File

@@ -0,0 +1,69 @@
package fr.iut_fbleau.GameAPI;
import java.util.Iterator;
/**
* The interface Board.
*/
public interface IBoard {
/**
* @return the current player
*/
public Player getcurrentPlayer();
/**
* Returns the game status
*
* @return true iff the game is over
*/
public boolean isGameOver();
/**
*
* @return the result (null if not over)
*/
public Result getResult();
/**
* checker of legal moves from this position
*
* @throws NullPointerException if the game is over
* @return the iterator
*/
public boolean isLegal(AbstractPly c);
/**
* constructor of Iterator over legal moves from this position
*
* @throws NullPointerException if the game is over
* @return the iterator
*/
public Iterator<AbstractPly> iterator();
/**
* Plays a given move on the plateau.
* Should update history using
* @throws IllegalArgumentException if the move is always illegal (say from the wrong game)
* @throws IllegalStateException if the move is not legal in this position
*
* @param AbstractPly to be played
*
*/
public void doPly(AbstractPly c);
/**
* Resets the plateau to the position before the last move.
*
* @throws IllegalStateException if nothing to undo in history
*
*/
public void undoPly();
}

Binary file not shown.

View File

@@ -0,0 +1,6 @@
package fr.iut_fbleau.GameAPI;
public enum Player {
PLAYER1,
PLAYER2
}

Binary file not shown.

View File

@@ -0,0 +1,11 @@
package fr.iut_fbleau.GameAPI;
/**
* To output the result of a 2 player game that is symmetric.
* from the perspective of the player PLAYER1.
*/
public enum Result {
WIN,
DRAW,
LOSS;
}

Binary file not shown.

View File

@@ -0,0 +1,199 @@
package fr.iut_fbleau.Nim;
import fr.iut_fbleau.GameAPI.AbstractBoard;
import fr.iut_fbleau.GameAPI.AbstractPly;
import fr.iut_fbleau.GameAPI.Player;
import fr.iut_fbleau.GameAPI.Result;
import java.util.Iterator;
import java.util.ArrayList;
import java.util.Deque;
import java.util.ArrayDeque;
/**
* Représente le plateau du jeu de Nim.
* Le jeu de Nim se joue avec un certain nombre d'allumettes.
* Les joueurs retirent à tour de rôle entre 1 et maxParCoup allumettes.
* Le joueur qui retire la dernière allumette perd (variante misère).
*/
public class NimBoard extends AbstractBoard {
private int allumettesRestantes;
private int maxParCoup;
private int allumettesInitiales;
private Player currentPlayer;
private Deque<AbstractPly> history;
private boolean gameOver;
private Result result;
/**
* Constructeur du plateau de Nim
*
* @param allumettesInitiales le nombre d'allumettes au début du jeu
* @param maxParCoup le nombre maximum d'allumettes qu'on peut retirer par coup
*/
public NimBoard(int allumettesInitiales, int maxParCoup) {
if (allumettesInitiales <= 0) {
throw new IllegalArgumentException("Le nombre d'allumettes doit être positif");
}
if (maxParCoup <= 0) {
throw new IllegalArgumentException("Le nombre maximum par coup doit être positif");
}
this.allumettesInitiales = allumettesInitiales;
this.allumettesRestantes = allumettesInitiales;
this.maxParCoup = maxParCoup;
this.currentPlayer = Player.PLAYER1;
this.history = new ArrayDeque<>();
this.gameOver = false;
this.result = null;
}
/**
* @return le nombre d'allumettes restantes
*/
public int getAllumettesRestantes() {
return this.allumettesRestantes;
}
/**
* @return le nombre maximum d'allumettes par coup
*/
public int getMaxParCoup() {
return this.maxParCoup;
}
@Override
public Player getcurrentPlayer() {
return this.currentPlayer;
}
@Override
public boolean isGameOver() {
return this.gameOver;
}
@Override
public Result getResult() {
return this.result;
}
@Override
public boolean isLegal(AbstractPly c) {
if (this.gameOver) {
throw new NullPointerException("Le jeu est terminé");
}
if (!(c instanceof NimPly)) {
return false;
}
NimPly coup = (NimPly) c;
if (coup.getJoueur() != this.currentPlayer) {
return false;
}
// Vérifier que le nombre d'allumettes est valide
int nbAllumettes = coup.getNombreAllumettesPrises();
return nbAllumettes >= 1 &&
nbAllumettes <= this.maxParCoup &&
nbAllumettes <= this.allumettesRestantes;
}
@Override
public Iterator<AbstractPly> iterator() {
if (this.gameOver) {
throw new NullPointerException("Le jeu est terminé");
}
ArrayList<AbstractPly> coups = new ArrayList<>();
int maxRetirage = Math.min(this.maxParCoup, this.allumettesRestantes);
for (int i = 1; i <= maxRetirage; i++) {
coups.add(new NimPly(this.currentPlayer, i));
}
return coups.iterator();
}
@Override
public void doPly(AbstractPly c) {
if (!(c instanceof NimPly)) {
throw new IllegalArgumentException("Le coup doit être un NimPly");
}
if (!isLegal(c)) {
throw new IllegalStateException("Ce coup n'est pas légal");
}
NimPly coup = (NimPly) c;
// Retirer les allumettes
this.allumettesRestantes -= coup.getNombreAllumettesPrises();
// Ajouter le coup à l'historique
this.history.addLast(c);
// Vérifier si le jeu est terminé
if (this.allumettesRestantes == 0) {
this.gameOver = true;
// Dans la variante misère : celui qui prend la dernière allumette perd
if (this.currentPlayer == Player.PLAYER1) {
this.result = Result.LOSS;
} else {
this.result = Result.WIN;
}
}
// Changer de joueur
setNextPlayer();
}
@Override
public void undoPly() {
if (this.history.isEmpty()) {
throw new IllegalStateException("Rien à annuler dans l'historique");
}
AbstractPly dernierCoup = this.history.removeLast();
NimPly coup = (NimPly) dernierCoup;
// Remettre les allumettes
this.allumettesRestantes += coup.getNombreAllumettesPrises();
// Revenir au joueur précédent
setNextPlayer();
// Réinitialiser l'état du jeu
this.gameOver = false;
this.result = null;
}
/**
* Change le joueur courant
*/
private void setNextPlayer() {
if (this.currentPlayer == Player.PLAYER1) {
this.currentPlayer = Player.PLAYER2;
} else {
this.currentPlayer = Player.PLAYER1;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Jeu de Nim\n");
sb.append("Allumettes restantes : ").append(allumettesRestantes).append("\n");
sb.append("Maximum par coup : ").append(maxParCoup).append("\n");
sb.append("Joueur courant : ").append(currentPlayer).append("\n");
if (gameOver) {
sb.append("Jeu terminé - Résultat (du point de vue de PLAYER1) : ").append(result).append("\n");
}
return sb.toString();
}
}

Binary file not shown.

View File

@@ -0,0 +1,166 @@
package fr.iut_fbleau.Nim;
import fr.iut_fbleau.GameAPI.AbstractGamePlayer;
import fr.iut_fbleau.GameAPI.AbstractPly;
import fr.iut_fbleau.GameAPI.IBoard;
import fr.iut_fbleau.GameAPI.Player;
import fr.iut_fbleau.GameAPI.Result;
import java.util.Iterator;
/**
* Représente un joueur bot pour le jeu de Nim.
* Le bot utilise l'algorithme MiniMax avec élagage Alpha-Beta pour choisir le meilleur coup.
*/
public class NimBotPlayer extends AbstractGamePlayer {
private Player joueur;
/**
* Constructeur d'un bot joueur de Nim
*
* @param joueur le type de joueur (PLAYER1 ou PLAYER2)
*/
public NimBotPlayer(Player joueur) {
this.joueur = joueur;
}
@Override
public AbstractPly giveYourMove(IBoard p) {
if (!(p instanceof NimBoard)) {
throw new IllegalArgumentException("Le plateau doit être un NimBoard");
}
NimBoard plateau = (NimBoard) p;
System.out.println("\n" + plateau);
System.out.println("Le bot réfléchit...");
AbstractPly meilleurCoup = findBestMove(plateau);
System.out.println("Le bot joue : " + meilleurCoup);
return meilleurCoup;
}
/**
* Trouve le meilleur coup à jouer en utilisant l'algorithme MiniMax avec Alpha-Beta
*/
private AbstractPly findBestMove(NimBoard plateau) {
AbstractPly meilleurCoup = null;
int meilleureValeur;
if (this.joueur == Player.PLAYER1) {
meilleureValeur = Integer.MIN_VALUE;
Iterator<AbstractPly> coups = plateau.iterator();
while (coups.hasNext()) {
AbstractPly coup = coups.next();
plateau.doPly(coup);
int score = minimax(plateau, Integer.MIN_VALUE, Integer.MAX_VALUE, false);
plateau.undoPly();
if (score > meilleureValeur) {
meilleureValeur = score;
meilleurCoup = coup;
}
}
} else {
meilleureValeur = Integer.MAX_VALUE;
Iterator<AbstractPly> coups = plateau.iterator();
while (coups.hasNext()) {
AbstractPly coup = coups.next();
plateau.doPly(coup);
int score = minimax(plateau, Integer.MIN_VALUE, Integer.MAX_VALUE, true);
plateau.undoPly();
if (score < meilleureValeur) {
meilleureValeur = score;
meilleurCoup = coup;
}
}
}
return meilleurCoup;
}
/**
* Algorithme MiniMax avec élagage Alpha-Beta
*
* @param plateau le plateau de jeu
* @param alpha la valeur alpha pour l'élagage
* @param beta la valeur beta pour l'élagage
* @param isMaximizing true si on maximise, false si on minimise
* @return l'évaluation de la position
*/
private int minimax(NimBoard plateau, int alpha, int beta, boolean isMaximizing) {
// Condition terminale : jeu fini
if (plateau.isGameOver()) {
Result result = plateau.getResult();
if (result == Result.WIN) {
return 1; // PLAYER1 gagne
} else if (result == Result.LOSS) {
return -1; // PLAYER1 perd (PLAYER2 gagne)
} else {
return 0; // Match nul
}
}
if (isMaximizing) {
// Maximiser pour PLAYER1
int max = Integer.MIN_VALUE;
Iterator<AbstractPly> coups = plateau.iterator();
while (coups.hasNext()) {
AbstractPly coup = coups.next();
plateau.doPly(coup);
int eval = minimax(plateau, alpha, beta, false);
plateau.undoPly();
max = Math.max(max, eval);
alpha = Math.max(alpha, eval);
// Élagage Beta
if (beta <= alpha) {
break;
}
}
return max;
} else {
// Minimiser pour PLAYER2
int min = Integer.MAX_VALUE;
Iterator<AbstractPly> coups = plateau.iterator();
while (coups.hasNext()) {
AbstractPly coup = coups.next();
plateau.doPly(coup);
int eval = minimax(plateau, alpha, beta, true);
plateau.undoPly();
min = Math.min(min, eval);
beta = Math.min(beta, eval);
// Élagage Alpha
if (beta <= alpha) {
break;
}
}
return min;
}
}
/**
* @return le type de joueur
*/
public Player getPlayer() {
return this.joueur;
}
}

Binary file not shown.

View File

@@ -0,0 +1,79 @@
package fr.iut_fbleau.Nim;
import fr.iut_fbleau.GameAPI.Player;
import fr.iut_fbleau.GameAPI.AbstractPly;
import java.util.Scanner;
/**
* Classe principale pour jouer au jeu de Nim.
* Cette classe permet de lancer une partie entre deux joueurs.
*/
public class NimGame {
public static void main(String[] args) {
System.out.println("=== Jeu de Nim ===\n");
Scanner scanner = new Scanner(System.in);
// Demander le nombre d'allumettes
int allumettesInitiales = 0;
while (allumettesInitiales <= 0) {
System.out.print("Avec combien d'allumettes voulez-vous commencer ? (minimum 1) : ");
try {
allumettesInitiales = scanner.nextInt();
if (allumettesInitiales <= 0) {
System.out.println("Le nombre doit être positif !");
}
} catch (Exception e) {
System.out.println("Entrée invalide. Veuillez entrer un nombre.");
scanner.nextLine(); // vider le buffer
}
}
// Maximum par coup fixé à 3
int maxParCoup = 3;
NimBoard plateau = new NimBoard(allumettesInitiales, maxParCoup);
// Créer les joueurs
NimPlayer joueur1 = new NimPlayer(Player.PLAYER1);
NimBotPlayer joueur2 = new NimBotPlayer(Player.PLAYER2);
System.out.println("\nConfiguration du jeu :");
System.out.println("- Nombre d'allumettes : " + allumettesInitiales);
System.out.println("- Maximum par coup : " + maxParCoup);
System.out.println("- Règle : Le joueur qui prend la dernière allumette PERD");
System.out.println("- PLAYER1 : Humain");
System.out.println("- PLAYER2 : Bot (MiniMax avec Alpha-Beta)\n");
// Boucle de jeu
while (!plateau.isGameOver()) {
// Obtenir le coup du joueur actif
AbstractPly coup;
if (plateau.getcurrentPlayer() == Player.PLAYER1) {
coup = joueur1.giveYourMove(plateau);
} else {
coup = joueur2.giveYourMove(plateau);
}
// Jouer le coup
plateau.doPly(coup);
System.out.println("\n>>> " + coup);
}
// Afficher le résultat
System.out.println("\n=== Fin de la partie ===");
System.out.println(plateau);
// Déterminer le gagnant en utilisant l'enum Result
if (plateau.getResult() == fr.iut_fbleau.GameAPI.Result.WIN) {
System.out.println("Le PLAYER1 a gagné !");
} else if (plateau.getResult() == fr.iut_fbleau.GameAPI.Result.LOSS) {
System.out.println("Le PLAYER2 (Bot) a gagné !");
} else {
System.out.println("Match nul !");
}
}
}

Binary file not shown.

View File

@@ -0,0 +1,66 @@
package fr.iut_fbleau.Nim;
import fr.iut_fbleau.GameAPI.AbstractGamePlayer;
import fr.iut_fbleau.GameAPI.AbstractPly;
import fr.iut_fbleau.GameAPI.IBoard;
import fr.iut_fbleau.GameAPI.Player;
import java.util.Scanner;
import java.util.Iterator;
/**
* Représente un joueur humain pour le jeu de Nim.
*/
public class NimPlayer extends AbstractGamePlayer {
private Player joueur;
private Scanner scanner;
public NimPlayer(Player joueur) {
this.joueur = joueur;
this.scanner = new Scanner(System.in);
}
@Override
public AbstractPly giveYourMove(IBoard p) {
if (!(p instanceof NimBoard)) {
throw new IllegalArgumentException("Le plateau doit être un NimBoard");
}
NimBoard plateau = (NimBoard) p;
System.out.println("\n" + plateau);
System.out.println("C'est ton tour joueur : " + this.joueur);
// Afficher les coups possibles
System.out.println("Coups possibles :");
Iterator<AbstractPly> coups = plateau.iterator();
int compteur = 1;
while (coups.hasNext()) {
NimPly coup = (NimPly) coups.next();
System.out.println(" " + compteur + ". Retirer " + coup.getNombreAllumettesPrises() + " allumette(s)");
compteur++;
}
// Demander au joueur de choisir
int maxAllumettes = Math.min(plateau.getMaxParCoup(), plateau.getAllumettesRestantes());
int choix = -1;
while (choix < 1 || choix > maxAllumettes) {
System.out.print("Combien d'allumettes voulez-vous retirer ? (1-" + maxAllumettes + ") : ");
try {
choix = scanner.nextInt();
if (choix < 1 || choix > maxAllumettes) {
System.out.println("Choix invalide. Veuillez choisir entre 1 et " + maxAllumettes);
}
} catch (Exception e) {
System.out.println("Entrée invalide. Veuillez entrer un nombre.");
scanner.nextLine(); // vider le buffer
}
}
return new NimPly(this.joueur, choix);
}
}

Binary file not shown.

View File

@@ -0,0 +1,38 @@
package fr.iut_fbleau.Nim;
import fr.iut_fbleau.GameAPI.AbstractPly;
import fr.iut_fbleau.GameAPI.Player;
/**
* Représente un coup dans le jeu de Nim.
* Un coup consiste à retirer un certain nombre d'allumettes.
*/
public class NimPly extends AbstractPly {
private Player joueur;
private int nombreAllumettesPrises;
public NimPly(Player joueur, int nombreAllumettesPrises) {
this.joueur = joueur;
this.nombreAllumettesPrises = nombreAllumettesPrises;
}
/**
* @return le joueur qui effectue le coup
*/
public Player getJoueur() {
return this.joueur;
}
/**
* @return le nombre d'allumettes prises
*/
public int getNombreAllumettesPrises() {
return this.nombreAllumettesPrises;
}
public String toString() {
return "Joueur " + joueur + " retire " + nombreAllumettesPrises + " allumette(s)";
}
}