This commit is contained in:
2026-02-13 16:39:48 +01:00
parent fe13b946b1
commit fafebb2648
3 changed files with 115 additions and 103 deletions

130
README.md
View File

@@ -1,115 +1,55 @@
# Instructions de Travail sur les Tickets
Ce document présente la procédure à suivre lors de la création et de la gestion des tickets de développement. Veuillez suivre chaque étape avec attention.
## 1. Création du Ticket
### Titre du Ticket
Le titre doit décrire de manière générale la tâche à réaliser. Soyez précis, mais sans entrer dans les détails techniques. Par exemple :
Ajout d'une nouvelle fonctionnalité de recherche dans l'application
Correction du bug d'affichage sur la page d'accueil
### Description du Ticket
La description doit fournir une explication légèrement détaillée des tâches à réaliser. Elle doit inclure les éléments suivants :
Objectif global de la tâche
Étapes spécifiques ou parties du projet concernées
Comportement attendu une fois la tâche accomplie
# BUT3 Projet Jeu : Hex
## 2. Création de la Branche
Il sagit dune implémentation du jeu **Hex** en Java, développée à partir de lAPI fournie par le Monsieur Madelaine.
Le projet comprend un moteur de jeu fonctionnel, un affichage console pour le debug, ainsi que des bots permettant de jouer automatiquement.
Lorsque vous commencez à travailler sur un ticket, créez une nouvelle branche avec un nom particulier qui reflète le ticket en cours. Le format de la branche doit être :
nom-de-la-feature-#numeroduticket
## Compilation
### Pour créer une branche :
Depuis la racine du projet, compiler lensemble des fichiers Java avec la commande suivante :
git checkout -b feature-recherche-#123
## 3. Commit des Changements
Les commits doivent suivre la convention suivante :
- Le message de commit doit décrire brièvement le changement effectué.
- À la fin du message de commit, vous devez toujours ajouter le numéro du ticket pour faciliter le suivi des tâches.
Exemple de message de commit :
Ajout du champ de recherche sur la page d'accueil #123
## 4. Push de la Branche
Après avoir effectué vos changements et effectué vos commits, vous devrez pousser la branche sur le dépôt distant. Lors de votre premier git push, vous recevrez un message pour définir l'upstream de la branche.
Exemple de message affiché :
```
fatal: The upstream branch 'origin/feature-recherche-#123' does not exist
To push the branch and set the upstream, use the following command:
git push --set-upstream origin nom-de-la-feature-#numero
```bash
javac -d build $(find javaAPI -name "*.java")
```
Vous devez copier et coller la commande dans votre terminal pour effectuer le push. Une fois cette commande exécutée, votre branche sera poussée vers le dépôt distant.
## 5. Création d'une Pull Request (PR)
Une fois que vous avez poussé votre branche sur Gitea, vous devez ouvrir une pull request pour demander la révision de votre code.
Voici les étapes pour créer une pull request correctement :
- Allez sur Gitea et naviguez vers le projet concerné.
- Cliquez sur "Branches" et vous devriez voir la branche que vous venez de pousser.
- Cliquez sur le bouton "Create Pull Request" à côté de votre branche.
Remplissez les informations nécessaires :
- Titre de la PR : Utilisez le même titre que celui du ticket.
- Description de la PR : Décrivez brièvement ce que votre PR accomplit. Vous pouvez vous baser sur la description du ticket.
- Revues : Assurez-vous de demander une révision par deux membres de léquipe.
- Cliquez sur "Create Pull Request" pour soumettre.
Une fois la PR ouverte, vous devrez attendre la révision et lapprobation de léquipe avant de pouvoir fusionner la branche dans main ou develop selon le flux de travail de votre projet.
Les fichiers compilés (`.class`) sont générés dans le dossier `bin`.
# Résumé des Commandes Git :
## Lancer une démonstration
Voici un récapitulatif des commandes Git que vous utiliserez fréquemment :
### Partie automatique (bot)
## 1. Créer une branche
```bash
java -cp build fr.iut_fbleau.HexGame.HexMain 3 autoplay
```
git checkout -b feature-recherche-#123
Ce mode permet de lancer une partie entièrement automatique en utilisant le bot implémenté dans la classe `Simulation`.
### Partie interactive (joueur humain)
```bash
java -cp build fr.iut_fbleau.HexGame.HexMain
```
Le plateau saffiche dans le terminal et les coups sont entrés sous forme de coordonnées.
## 2. Ajouter les fichiers modifiés :
### Partie arène (bot)
git add .
git add *
git add <nom_du_fichier>
```bash
java -cp build fr.iut_fbleau.HexGame.ArenaMain
```
Créer un fichier arena_result.csv à la fin avec les résultats des combats
## 3. Commit des changements :
## Tests et validation
git commit -m "Ajout de [...] #numeroticket"
Les tests sont réalisés sous forme de **tests fonctionnels** via des méthodes `main` et des modes de démonstration :
- vérification de la validité des coups,
- alternance correcte des joueurs,
- détection des conditions de fin de partie,
- exécution de parties complètes en mode automatique.
## 4. Pousser la branche
git push -set-upstream origin <nom-de-la-branche-#numeroticket>
## 5. Supprimer une branche
git branch -d <nom_de_la_branche>
Laffichage console du plateau, fourni par la méthode `HexBoard.toString()`, est utilisé comme outil de debug pour visualiser létat du jeu à chaque tour.

View File

@@ -6,6 +6,13 @@ public class MiniMaxBot extends AbstractGamePlayer {
private int MAXDEPTH = 5;
/**
* En dessous (ou égal) à ce nombre de coups restants (cases vides),
* on considère que l'arbre est "petit" et on fait un alpha-bêta simple :
* pas de cut-off, on explore jusqu'aux positions terminales (isGameOver()).
*/
private int SMALL_TREE_MOVE_LIMIT = 6;
public MiniMaxBot(Player me) {
super(me); // Correct constructor usage
}
@@ -20,12 +27,19 @@ public class MiniMaxBot extends AbstractGamePlayer {
float bestScore = -Float.MAX_VALUE;
HexPly bestMove = null;
// Détermine si l'arbre est petit ou grand
int movesLeft = countLegalMoves(hb);
boolean useCutoff = (movesLeft > SMALL_TREE_MOVE_LIMIT);
int depthToUse = useCutoff ? MAXDEPTH : 0; // 0 car en "simple" on ne coupe pas sur depth
for (int i = 0; i < hb.getSize(); i++) {
for (int j = 0; j < hb.getSize(); j++) {
HexPly move = new HexPly(hb.getCurrentPlayer(), i, j);
if (hb.isLegal(move)) {
hb.doPly(move);
float score = minimax(hb, MAXDEPTH, -Float.MAX_VALUE, Float.MAX_VALUE, true);
float score = minimax(hb, depthToUse, -Float.MAX_VALUE, Float.MAX_VALUE, true, useCutoff);
if (score > bestScore) {
bestScore = score;
bestMove = move;
@@ -37,8 +51,19 @@ public class MiniMaxBot extends AbstractGamePlayer {
return bestMove;
}
private float minimax(HexBoard board, int depth, float alpha, float beta, boolean isMaximizing) {
if (depth == 0 || board.isGameOver()) {
/**
* Minimax + alpha-bêta
* - useCutoff = false : alpha-bêta "simple" -> on s'arrête uniquement sur isGameOver()
* - useCutoff = true : alpha-bêta avec cut-off -> arrêt si depth == 0 (heuristique)
*/
private float minimax(HexBoard board, int depth, float alpha, float beta, boolean isMaximizing, boolean useCutoff) {
// Toujours prioritaire : position terminale
if (board.isGameOver()) {
return terminalScore(board);
}
// Cut-off uniquement si demandé
if (useCutoff && depth == 0) {
return evaluateBoard(board);
}
@@ -49,10 +74,18 @@ public class MiniMaxBot extends AbstractGamePlayer {
HexPly move = new HexPly(board.getCurrentPlayer(), i, j);
if (board.isLegal(move)) {
board.doPly(move);
float score = minimax(board, depth - 1, alpha, beta, false);
int nextDepth = useCutoff ? (depth - 1) : depth;
float score = minimax(board, nextDepth, alpha, beta, false, useCutoff);
bestScore = Math.max(bestScore, score);
alpha = Math.max(alpha, bestScore);
if (beta <= alpha) break; // Pruning
if (beta <= alpha) {
board.undoPly();
break; // Pruning
}
board.undoPly();
}
}
@@ -65,10 +98,18 @@ public class MiniMaxBot extends AbstractGamePlayer {
HexPly move = new HexPly(board.getCurrentPlayer(), i, j);
if (board.isLegal(move)) {
board.doPly(move);
float score = minimax(board, depth - 1, alpha, beta, true);
int nextDepth = useCutoff ? (depth - 1) : depth;
float score = minimax(board, nextDepth, alpha, beta, true, useCutoff);
bestScore = Math.min(bestScore, score);
beta = Math.min(beta, bestScore);
if (beta <= alpha) break; // Pruning
if (beta <= alpha) {
board.undoPly();
break; // Pruning
}
board.undoPly();
}
}
@@ -77,6 +118,22 @@ public class MiniMaxBot extends AbstractGamePlayer {
}
}
/**
* Score terminal (feuille) du point de vue de PLAYER1 :
* - WIN : PLAYER1 gagne
* - LOSS : PLAYER1 perd
*/
private float terminalScore(HexBoard board) {
Result r = board.getResult();
if (r == null) return 0f;
if (r == Result.WIN) return 1000000f;
return -1000000f;
}
/**
* Heuristique actuelle (inchangée) : distance au centre des pions PLAYER1.
* (Je ne la modifie pas pour ne pas toucher à la logique existante.)
*/
private float evaluateBoard(HexBoard board) {
int size = board.getSize();
int center = size / 2;
@@ -90,4 +147,19 @@ public class MiniMaxBot extends AbstractGamePlayer {
}
return score;
}
/**
* Compte les coups légaux (cases vides) sur le plateau courant.
*/
private int countLegalMoves(HexBoard board) {
int count = 0;
for (int i = 0; i < board.getSize(); i++) {
for (int j = 0; j < board.getSize(); j++) {
if (board.getCellPlayer(i, j) == null) {
count++;
}
}
}
return count;
}
}