Compare commits
4 Commits
Readme
...
MONTE_CARL
| Author | SHA1 | Date | |
|---|---|---|---|
| f59f9975fa | |||
| aa2dbc0c87 | |||
| 0df6b74d54 | |||
|
|
4b2b37a04a |
131
README.md
131
README.md
@@ -1,56 +1,115 @@
|
|||||||
# BUT3 – Projet Jeu : Hex
|
# 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
|
||||||
|
|
||||||
|
|
||||||
Il s’agit d’une implémentation du jeu **Hex** en Java, développée à partir de l’API fournie par le Monsieur Madelaine.
|
## 2. Création de la Branche
|
||||||
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 :
|
||||||
|
|
||||||
## Compilation
|
nom-de-la-feature-#numeroduticket
|
||||||
|
|
||||||
Depuis la racine du projet, compiler l’ensemble des fichiers Java avec la commande suivante :
|
### Pour créer une branche :
|
||||||
|
|
||||||
```bash
|
git checkout -b feature-recherche-#123
|
||||||
javac -d build $(find javaAPI -name "*.java")
|
|
||||||
|
## 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
|
||||||
```
|
```
|
||||||
|
|
||||||
Les fichiers compilés (`.class`) sont générés dans le dossier `bin`.
|
|
||||||
|
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 l’approbation de l’équipe avant de pouvoir fusionner la branche dans main ou develop selon le flux de travail de votre projet.
|
||||||
|
|
||||||
|
|
||||||
## Lancer une démonstration
|
# Résumé des Commandes Git :
|
||||||
|
|
||||||
### Partie automatique (bot)
|
Voici un récapitulatif des commandes Git que vous utiliserez fréquemment :
|
||||||
|
|
||||||
```bash
|
## 1. Créer une branche
|
||||||
java -cp build fr.iut_fbleau.HexGame.HexMain 3 autoplay
|
|
||||||
```
|
|
||||||
|
|
||||||
Ce mode permet de lancer une partie entièrement automatique en utilisant le bot implémenté dans la classe `Simulation`.
|
git checkout -b feature-recherche-#123
|
||||||
|
|
||||||
### Partie interactive (joueur humain)
|
|
||||||
|
|
||||||
```bash
|
|
||||||
java -cp build fr.iut_fbleau.HexGame.HexMain
|
|
||||||
```
|
|
||||||
|
|
||||||
Le plateau s’affiche dans le terminal et les coups sont entrés sous forme de coordonnées.
|
|
||||||
|
|
||||||
|
|
||||||
## Tests et validation
|
## 2. Ajouter les fichiers modifiés :
|
||||||
|
|
||||||
Les tests sont réalisés sous forme de **tests fonctionnels** via des méthodes `main` et des modes de démonstration :
|
git add .
|
||||||
- vérification de la validité des coups,
|
git add *
|
||||||
- alternance correcte des joueurs,
|
git add <nom_du_fichier>
|
||||||
- détection des conditions de fin de partie,
|
|
||||||
- exécution de parties complètes en mode automatique.
|
|
||||||
|
|
||||||
L’affichage console du plateau, fourni par la méthode `HexBoard.toString()`, est utilisé comme outil de debug pour visualiser l’état du jeu à chaque tour.
|
|
||||||
|
|
||||||
|
|
||||||
## Organisation du projet
|
## 3. Commit des changements :
|
||||||
|
|
||||||
- `HexBoard` : représentation du plateau et gestion des règles du jeu
|
git commit -m "Ajout de [...] #numeroticket"
|
||||||
- `HexPly` : représentation d’un coup
|
|
||||||
- `Simulation` : bot basé sur une recherche Minimax à profondeur limitée
|
|
||||||
- `HexMain` : point d’entrée du programme
|
|
||||||
|
|
||||||
Les classes principales sont documentées à l’aide de **Javadoc**.
|
|
||||||
|
## 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>
|
||||||
9784
Rapport Hex.pdf
9784
Rapport Hex.pdf
File diff suppressed because it is too large
Load Diff
BIN
build/fr/iut_fbleau/GameAPI/AbstractBoard.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/AbstractBoard.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/AbstractGame.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/AbstractGame.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/AbstractGamePlayer.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/AbstractGamePlayer.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/AbstractPly.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/AbstractPly.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/IBoard.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/IBoard.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/Player.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/Player.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/GameAPI/Result.class
Normal file
BIN
build/fr/iut_fbleau/GameAPI/Result.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexBoard.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexBoard.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexMain$1.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexMain$1.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexMain.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexMain.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexPly.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexPly.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HumanConsolePlayer.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HumanConsolePlayer.class
Normal file
Binary file not shown.
40
javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
Normal file
40
javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
|
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.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
public class RandomBot extends AbstractGamePlayer {
|
||||||
|
|
||||||
|
private final Random rng;
|
||||||
|
|
||||||
|
public RandomBot(Player me, Random rng) {
|
||||||
|
super(me);
|
||||||
|
this.rng = rng;
|
||||||
|
}
|
||||||
|
|
||||||
|
public RandomBot(Player me, long seed) {
|
||||||
|
this(me, new Random(seed));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractPly giveYourMove(IBoard board) {
|
||||||
|
List<AbstractPly> legal = new ArrayList<>();
|
||||||
|
Iterator<AbstractPly> it = board.iterator();
|
||||||
|
while (it.hasNext()) {
|
||||||
|
legal.add(it.next());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (legal.isEmpty()) {
|
||||||
|
throw new IllegalStateException("No legal move available (board is full?)");
|
||||||
|
}
|
||||||
|
|
||||||
|
return legal.get(rng.nextInt(legal.size()));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ package fr.iut_fbleau.HexGame;
|
|||||||
import fr.iut_fbleau.GameAPI.*;
|
import fr.iut_fbleau.GameAPI.*;
|
||||||
import java.util.EnumMap;
|
import java.util.EnumMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
|
||||||
public class Simulation extends AbstractGame {
|
public class Simulation extends AbstractGame {
|
||||||
@@ -10,7 +11,8 @@ public class Simulation extends AbstractGame {
|
|||||||
//ATTRIBUTS
|
//ATTRIBUTS
|
||||||
private HexPly bestmove;
|
private HexPly bestmove;
|
||||||
private float bestoutcome;
|
private float bestoutcome;
|
||||||
private int MAXDEPTH = 6;
|
private int MAXDEPTH = 9;
|
||||||
|
private int EVALDEPTH = 10;
|
||||||
private LinkedList<Integer[]> taken = new LinkedList<Integer[]>();
|
private LinkedList<Integer[]> taken = new LinkedList<Integer[]>();
|
||||||
|
|
||||||
//ATTRIBUTS QUE JE NE VOUDRAIS PAS CRÉER IDÉALEMENT
|
//ATTRIBUTS QUE JE NE VOUDRAIS PAS CRÉER IDÉALEMENT
|
||||||
@@ -25,13 +27,52 @@ public class Simulation extends AbstractGame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//METHODES
|
//METHODES
|
||||||
|
/*Le jeu de Hex ne peut jamais finir avec le résultat null. En utilisant cette propriété, on peut avoir cet algorithme simplifié du monte-carlo*/
|
||||||
|
private float MonteCarlo(HexBoard position, Player current){
|
||||||
|
RandomBot simplay = new RandomBot(current, new Random().nextLong());
|
||||||
|
HexBoard simpos = position;
|
||||||
|
LinkedList<Integer[]> ctaken = taken;
|
||||||
|
HexPly testmove;
|
||||||
|
float wins = 0;
|
||||||
|
float losses = 0;
|
||||||
|
int count = 0;
|
||||||
|
|
||||||
|
for(int i=0; i<EVALDEPTH; i++){
|
||||||
|
while(!simpos.isGameOver()){
|
||||||
|
count++;
|
||||||
|
testmove = (HexPly) simplay.giveYourMove(simpos);
|
||||||
|
if(!ctaken.contains(new Integer[]{testmove.getRow(), testmove.getCol()}) && simpos.isLegal(testmove)){
|
||||||
|
ctaken.add(new Integer[]{testmove.getRow(), testmove.getCol()});
|
||||||
|
simpos.doPly(testmove);
|
||||||
|
if(simpos.getResult()==Result.LOSS){
|
||||||
|
losses++;
|
||||||
|
} else if(simpos.getResult()==Result.WIN){
|
||||||
|
wins++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//System.out.println("count:"+count);
|
||||||
|
for (int j=0; j<count; j++) {
|
||||||
|
simpos.undoPly();
|
||||||
|
}
|
||||||
|
ctaken = taken;
|
||||||
|
count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println(" wins : "+wins+"/losses : "+losses);
|
||||||
|
System.out.println(" eval : "+(wins-losses)/EVALDEPTH);
|
||||||
|
|
||||||
|
return (wins-losses)/EVALDEPTH;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
private float explMAX(HexBoard position, int depth){
|
private float explMAX(HexBoard position, int depth){
|
||||||
if (position.getResult()==Result.LOSS) {
|
if (position.getResult()==Result.LOSS) {
|
||||||
return -1.0f;
|
return -1.0f;
|
||||||
} else if (position.getResult()==Result.WIN){
|
} else if (position.getResult()==Result.WIN){
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
} else if (depth==MAXDEPTH) {
|
} else if (depth==MAXDEPTH) {
|
||||||
return 0f;
|
return MonteCarlo(position, Player.PLAYER1);
|
||||||
} else {
|
} else {
|
||||||
float bestcase = -1.0f;
|
float bestcase = -1.0f;
|
||||||
HexPly bestcasemove;
|
HexPly bestcasemove;
|
||||||
@@ -73,7 +114,7 @@ public class Simulation extends AbstractGame {
|
|||||||
} else if (position.getResult()==Result.WIN){
|
} else if (position.getResult()==Result.WIN){
|
||||||
return 1.0f;
|
return 1.0f;
|
||||||
} else if (depth==MAXDEPTH) {
|
} else if (depth==MAXDEPTH) {
|
||||||
return 0f;
|
return MonteCarlo(position, Player.PLAYER2);
|
||||||
} else {
|
} else {
|
||||||
float bestcase = 1.0f;
|
float bestcase = 1.0f;
|
||||||
HexPly bestcasemove;
|
HexPly bestcasemove;
|
||||||
@@ -109,6 +150,97 @@ public class Simulation extends AbstractGame {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private float explMAXAB(HexBoard position, int depth, float A, float B){
|
||||||
|
if (position.getResult()==Result.LOSS) {
|
||||||
|
return -1.0f;
|
||||||
|
} else if (position.getResult()==Result.WIN){
|
||||||
|
return 1.0f;
|
||||||
|
} else if (depth==MAXDEPTH) {
|
||||||
|
return MonteCarlo(position, Player.PLAYER1);
|
||||||
|
} else {
|
||||||
|
float bestcase = A;
|
||||||
|
HexPly bestcasemove;
|
||||||
|
HexPly testmove;
|
||||||
|
for (int i=0; i<position.getSize(); i++) {
|
||||||
|
for (int j=0; j<position.getSize(); j++) {
|
||||||
|
if(depth==0){
|
||||||
|
//System.out.println("MAX New Line :");
|
||||||
|
}
|
||||||
|
Integer[] t = new Integer[]{i, j};
|
||||||
|
testmove = new HexPly(Player.PLAYER1, i, j);
|
||||||
|
if(!taken.contains(t) && position.isLegal(testmove)){
|
||||||
|
//System.out.println(" MAX test move : "+Integer.toString(i)+","+Integer.toString(j));
|
||||||
|
taken.add(t);
|
||||||
|
position.doPly(testmove);
|
||||||
|
float val = explMINAB(position, depth+1, bestcase, B);
|
||||||
|
if (val >= bestcase) {
|
||||||
|
//System.out.println(" MAX new best case");
|
||||||
|
bestcase = val;
|
||||||
|
bestcasemove = testmove;
|
||||||
|
if (depth==0) {
|
||||||
|
this.bestoutcome = bestcase;
|
||||||
|
this.bestmove = bestcasemove;
|
||||||
|
}
|
||||||
|
if(bestcase>=B){
|
||||||
|
return bestcase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
position.undoPly();
|
||||||
|
taken.remove(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestcase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float explMINAB(HexBoard position, int depth, float A, float B){
|
||||||
|
if (position.getResult()==Result.LOSS) {
|
||||||
|
return -1.0f;
|
||||||
|
} else if (position.getResult()==Result.WIN){
|
||||||
|
return 1.0f;
|
||||||
|
} else if (depth==MAXDEPTH) {
|
||||||
|
return MonteCarlo(position, Player.PLAYER2);
|
||||||
|
} else {
|
||||||
|
float bestcase = B;
|
||||||
|
HexPly bestcasemove;
|
||||||
|
HexPly testmove;
|
||||||
|
for (int i=0; i<position.getSize(); i++) {
|
||||||
|
for (int j=0; j<position.getSize(); j++) {
|
||||||
|
if(depth==0){
|
||||||
|
//System.out.println("MIN New Line :");
|
||||||
|
}
|
||||||
|
Integer[] t = new Integer[]{i, j};
|
||||||
|
testmove = new HexPly(Player.PLAYER2, i, j);
|
||||||
|
if(!taken.contains(t) && position.isLegal(testmove)){
|
||||||
|
//System.out.println(" MIN test move : "+Integer.toString(i)+","+Integer.toString(j));
|
||||||
|
taken.add(t);
|
||||||
|
position.doPly(testmove);
|
||||||
|
float val = explMAXAB(position, depth+1, A, bestcase);
|
||||||
|
if (val <= bestcase) {
|
||||||
|
//System.out.println(" MIN new best case");
|
||||||
|
bestcase = val;
|
||||||
|
bestcasemove = testmove;
|
||||||
|
if (depth==0) {
|
||||||
|
this.bestoutcome = bestcase;
|
||||||
|
this.bestmove = bestcasemove;
|
||||||
|
}
|
||||||
|
if(bestcase<=A){
|
||||||
|
return bestcase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
position.undoPly();
|
||||||
|
taken.remove(t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestcase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private AbstractPly GiveBestMove(IBoard board) {
|
private AbstractPly GiveBestMove(IBoard board) {
|
||||||
if (!(board instanceof HexBoard)) {
|
if (!(board instanceof HexBoard)) {
|
||||||
throw new IllegalArgumentException("Ce joueur attend un HexBoard.");
|
throw new IllegalArgumentException("Ce joueur attend un HexBoard.");
|
||||||
@@ -116,9 +248,9 @@ public class Simulation extends AbstractGame {
|
|||||||
HexBoard hb = (HexBoard) board;
|
HexBoard hb = (HexBoard) board;
|
||||||
float bestcase;
|
float bestcase;
|
||||||
if(hb.getCurrentPlayer()==Player.PLAYER1){
|
if(hb.getCurrentPlayer()==Player.PLAYER1){
|
||||||
bestcase = explMAX(hb, 0);
|
bestcase = explMAXAB(hb, 0, -1.0f, 1.0f);
|
||||||
} else {
|
} else {
|
||||||
bestcase = explMIN(hb, 0);
|
bestcase = explMINAB(hb, 0, -1.0f, 1.0f);
|
||||||
}
|
}
|
||||||
return this.bestmove;
|
return this.bestmove;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user