Compare commits
5 Commits
instructio
...
Bots-aleat
| Author | SHA1 | Date | |
|---|---|---|---|
| fa96aae6e6 | |||
| 22891ae2b6 | |||
| d8ea5cd958 | |||
| d2a1cb0d65 | |||
| 1c127319f9 |
66
README.md
66
README.md
@@ -1,4 +1,4 @@
|
|||||||
# Instructions pour une bonne developer experience
|
# 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.
|
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
|
## 1. Création du Ticket
|
||||||
@@ -21,18 +21,6 @@ La description doit fournir une explication légèrement détaillée des tâches
|
|||||||
|
|
||||||
Comportement attendu une fois la tâche accomplie
|
Comportement attendu une fois la tâche accomplie
|
||||||
|
|
||||||
### Assigner une personne au ticket
|
|
||||||
|
|
||||||
L'assignement d'une eprsonne est essentielle pour savoir qui travaille sur quoi. Si plusieurs personnes ont participé dans le ticket, vous pouvez le modifier.
|
|
||||||
|
|
||||||
Aller dans assignements ou assignees et choisissez les developers
|
|
||||||
|
|
||||||
### Assigner une deadline (optionnel)
|
|
||||||
|
|
||||||
Le mieux serait d'ajouter une deadline pour les tâches pour savoir à peu près quand vous pensez avoir fini votre travail. ce n'est pas grave si vous n'en mettez pas mais si vous êtes sûr de quand vous allez à peu près le finir, cela peut-être intéressant de le savoir. C'est particulièrement intéressant pour les grosses tâches.
|
|
||||||
|
|
||||||
|
|
||||||
### Note : vous pouvez créer des tickets pour d'autres personnes si vous avez des recommandations
|
|
||||||
|
|
||||||
## 2. Création de la Branche
|
## 2. Création de la Branche
|
||||||
|
|
||||||
@@ -96,48 +84,6 @@ Remplissez les informations nécessaires :
|
|||||||
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.
|
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.
|
||||||
|
|
||||||
|
|
||||||
## 6. Après la fusion accordée
|
|
||||||
|
|
||||||
Une fois que la fusion de votre branche a été approuvée, vous devez supprimer cette branche localement. Pour se faire, recopiez cette commande :
|
|
||||||
|
|
||||||
git branch -d <nom_de_la_branche>
|
|
||||||
|
|
||||||
Lorsque vous avez supprimé votre branche et que vous souhaitez travailler à nouveau sur une nouvelle tâche, rebasez vous sur la branche master. Pour se faire suivez les instrucitons :
|
|
||||||
|
|
||||||
git checkout master
|
|
||||||
|
|
||||||
Vérifiez que vous êtes bien sur la branche master :
|
|
||||||
|
|
||||||
git branch
|
|
||||||
|
|
||||||
Si vous êtes dessus, vous pouvez recommencer le rpocessus de création de ticket et de branche.
|
|
||||||
|
|
||||||
|
|
||||||
## 7. Révisions
|
|
||||||
|
|
||||||
Si vous voyez qu'une pull request est en attente, n'hésitez pas à jetter un coup d'oeil au code et à le **TESTER** également ! Si vous avez des doutes sur le code contactez l'un des participants pour plus de détails.
|
|
||||||
|
|
||||||
### Si vous souhaitez réviser le code
|
|
||||||
|
|
||||||
Aller dans pull requests (ou demandes d'ajout) à côté des tickets (ou issues)
|
|
||||||
|
|
||||||
Vous y verrez les demandes d'ajouts en cours et déjà mergées. Si vous souhaitez faire une révision :
|
|
||||||
|
|
||||||
Cliquer sur celle qui vous intéresse et qui est open (ou ouverte)
|
|
||||||
|
|
||||||
Aller dans fichiers modifiés (ou files changed)
|
|
||||||
|
|
||||||
Vérifier et ajouter des commentaires si problèmes
|
|
||||||
|
|
||||||
Si problèmes refuser pull request et demander des changements
|
|
||||||
|
|
||||||
Si aucun problèmes en haut à droite cliquer sur évaluation (ou review)
|
|
||||||
|
|
||||||
Accepter les modifications et si vous êtes le deuxième à review, merger pull request
|
|
||||||
|
|
||||||
### Note :
|
|
||||||
Une fois que tout ça est fait, supprimez la branche qui a été fusionnée, gitea le notifie juste après que vous ayez fusionner la branche dans l'historique de conversation de la pull request.
|
|
||||||
|
|
||||||
# Résumé des Commandes Git :
|
# Résumé des Commandes Git :
|
||||||
|
|
||||||
Voici un récapitulatif des commandes Git que vous utiliserez fréquemment :
|
Voici un récapitulatif des commandes Git que vous utiliserez fréquemment :
|
||||||
@@ -166,12 +112,4 @@ Voici un récapitulatif des commandes Git que vous utiliserez fréquemment :
|
|||||||
|
|
||||||
## 5. Supprimer une branche
|
## 5. Supprimer une branche
|
||||||
|
|
||||||
git branch -d <nom_de_la_branche>
|
git branch -d <nom_de_la_branche>
|
||||||
|
|
||||||
## 6. Vérifier la branche où l'on se trouve
|
|
||||||
|
|
||||||
git branch
|
|
||||||
|
|
||||||
## 7. Changer de branche
|
|
||||||
|
|
||||||
git checkout <nom_de_la_branche>
|
|
||||||
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/HexFrame.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexFrame.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/HexPanel$1.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexPanel$1.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexPanel.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexPanel.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/HexSimMain$Args.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexSimMain$Args.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexSimMain$Stats.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexSimMain$Stats.class
Normal file
Binary file not shown.
BIN
build/fr/iut_fbleau/HexGame/HexSimMain.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/HexSimMain.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.
BIN
build/fr/iut_fbleau/HexGame/RandomBot.class
Normal file
BIN
build/fr/iut_fbleau/HexGame/RandomBot.class
Normal file
Binary file not shown.
BIN
javaAPI/fr/iut_fbleau.tar
Normal file
BIN
javaAPI/fr/iut_fbleau.tar
Normal file
Binary file not shown.
317
javaAPI/fr/iut_fbleau/HexGame/HexBoard.java
Normal file
317
javaAPI/fr/iut_fbleau/HexGame/HexBoard.java
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.GameAPI.*;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Représente le plateau du jeu de Hex.
|
||||||
|
*
|
||||||
|
* <h2>Rappel des conditions de victoire</h2>
|
||||||
|
* <ul>
|
||||||
|
* <li>{@link Player#PLAYER1} gagne s'il existe un chemin de pions connectés
|
||||||
|
* reliant le bord gauche au bord droit.</li>
|
||||||
|
* <li>{@link Player#PLAYER2} gagne s'il existe un chemin de pions connectés
|
||||||
|
* reliant le bord haut au bord bas.</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* <h2>Idée de l'algorithme de détection de victoire</h2>
|
||||||
|
* On modélise le plateau comme un graphe :
|
||||||
|
* <ul>
|
||||||
|
* <li>Chaque case est un sommet</li>
|
||||||
|
* <li>Deux cases sont connectées si elles sont voisines sur la grille hexagonale (6 voisins)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* Pour tester la victoire d'un joueur, on lance un parcours (DFS/BFS) :
|
||||||
|
* <ol>
|
||||||
|
* <li>On part de toutes les cases du bord de départ qui contiennent un pion du joueur.</li>
|
||||||
|
* <li>On explore tous les pions du joueur connectés à ces cases.</li>
|
||||||
|
* <li>Si on atteint le bord opposé, il existe un chemin : le joueur gagne.</li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* Complexité : O(N²) au pire (on visite chaque case au plus une fois).
|
||||||
|
*/
|
||||||
|
public class HexBoard extends AbstractBoard {
|
||||||
|
|
||||||
|
/** Taille du plateau : size x size. */
|
||||||
|
private final int size;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grille des cases.
|
||||||
|
* Une case vaut :
|
||||||
|
* <ul>
|
||||||
|
* <li>null : case vide</li>
|
||||||
|
* <li>PLAYER1 : pion du joueur 1</li>
|
||||||
|
* <li>PLAYER2 : pion du joueur 2</li>
|
||||||
|
* </ul>
|
||||||
|
*/
|
||||||
|
private final Player[][] cells;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Offsets des 6 voisins d'une case dans une grille hexagonale.
|
||||||
|
*
|
||||||
|
* Pour une case (r,c), les voisins potentiels sont :
|
||||||
|
* (r-1,c), (r+1,c), (r,c-1), (r,c+1), (r-1,c+1), (r+1,c-1).
|
||||||
|
*/
|
||||||
|
private static final int[][] NEIGHBORS = {
|
||||||
|
{-1, 0}, {+1, 0},
|
||||||
|
{ 0, -1}, { 0, +1},
|
||||||
|
{-1, +1}, {+1, -1}
|
||||||
|
};
|
||||||
|
|
||||||
|
/** Crée un plateau vide avec {@link Player#PLAYER1} qui commence. */
|
||||||
|
public HexBoard(int size) {
|
||||||
|
this(size, Player.PLAYER1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructeur interne, utile pour {@link #safeCopy()}.
|
||||||
|
* @param size taille du plateau
|
||||||
|
* @param current joueur courant
|
||||||
|
*/
|
||||||
|
private HexBoard(int size, Player current) {
|
||||||
|
super(current, new ArrayDeque<>());
|
||||||
|
if (size <= 0) throw new IllegalArgumentException("size must be > 0");
|
||||||
|
this.size = size;
|
||||||
|
this.cells = new Player[size][size];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return la taille du plateau. */
|
||||||
|
public int getSize() {
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vérifie si (r,c) est dans le plateau.
|
||||||
|
* @param r ligne (0..size-1)
|
||||||
|
* @param c colonne (0..size-1)
|
||||||
|
*/
|
||||||
|
private boolean inBounds(int r, int c) {
|
||||||
|
return r >= 0 && r < size && c >= 0 && c < size;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return le contenu d'une case (null si vide). */
|
||||||
|
private Player getCell(int r, int c) {
|
||||||
|
return cells[r][c];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Modifie une case (utilisé par doPly/undoPly). */
|
||||||
|
private void setCell(int r, int c, Player p) {
|
||||||
|
cells[r][c] = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teste la victoire de PLAYER1 (gauche -> droite).
|
||||||
|
*
|
||||||
|
* <h3>Détails de l'algorithme</h3>
|
||||||
|
* <ol>
|
||||||
|
* <li>On initialise une structure "visited" pour ne pas revisiter les cases.</li>
|
||||||
|
* <li>On met dans une pile toutes les cases du bord gauche (colonne 0)
|
||||||
|
* qui contiennent un pion PLAYER1.</li>
|
||||||
|
* <li>On effectue un DFS :
|
||||||
|
* <ul>
|
||||||
|
* <li>on dépile une case</li>
|
||||||
|
* <li>si elle est sur la colonne size-1 : on a touché le bord droit -> victoire</li>
|
||||||
|
* <li>sinon, on empile tous ses voisins qui sont des pions PLAYER1 et pas encore visités</li>
|
||||||
|
* </ul>
|
||||||
|
* </li>
|
||||||
|
* </ol>
|
||||||
|
*
|
||||||
|
* @return true si PLAYER1 a un chemin gauche->droite, false sinon
|
||||||
|
*/
|
||||||
|
private boolean hasPlayer1Won() {
|
||||||
|
boolean[][] visited = new boolean[size][size];
|
||||||
|
Deque<int[]> stack = new ArrayDeque<>();
|
||||||
|
|
||||||
|
// 1) points de départ : bord gauche
|
||||||
|
for (int r = 0; r < size; r++) {
|
||||||
|
if (getCell(r, 0) == Player.PLAYER1) {
|
||||||
|
visited[r][0] = true;
|
||||||
|
stack.push(new int[]{r, 0});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2) DFS
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
int[] cur = stack.pop();
|
||||||
|
int cr = cur[0], cc = cur[1];
|
||||||
|
|
||||||
|
// condition d'arrivée : bord droit
|
||||||
|
if (cc == size - 1) return true;
|
||||||
|
|
||||||
|
// explore les 6 voisins
|
||||||
|
for (int[] d : NEIGHBORS) {
|
||||||
|
int nr = cr + d[0], nc = cc + d[1];
|
||||||
|
if (inBounds(nr, nc)
|
||||||
|
&& !visited[nr][nc]
|
||||||
|
&& getCell(nr, nc) == Player.PLAYER1) {
|
||||||
|
visited[nr][nc] = true;
|
||||||
|
stack.push(new int[]{nr, nc});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teste la victoire de PLAYER2 (haut -> bas).
|
||||||
|
*
|
||||||
|
* Même principe que {@link #hasPlayer1Won()} mais :
|
||||||
|
* <ul>
|
||||||
|
* <li>Départ : bord haut (ligne 0)</li>
|
||||||
|
* <li>Arrivée : bord bas (ligne size-1)</li>
|
||||||
|
* </ul>
|
||||||
|
*
|
||||||
|
* @return true si PLAYER2 a un chemin haut->bas, false sinon
|
||||||
|
*/
|
||||||
|
private boolean hasPlayer2Won() {
|
||||||
|
boolean[][] visited = new boolean[size][size];
|
||||||
|
Deque<int[]> stack = new ArrayDeque<>();
|
||||||
|
|
||||||
|
// points de départ : bord haut
|
||||||
|
for (int c = 0; c < size; c++) {
|
||||||
|
if (getCell(0, c) == Player.PLAYER2) {
|
||||||
|
visited[0][c] = true;
|
||||||
|
stack.push(new int[]{0, c});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DFS
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
int[] cur = stack.pop();
|
||||||
|
int cr = cur[0], cc = cur[1];
|
||||||
|
|
||||||
|
// condition d'arrivée : bord bas
|
||||||
|
if (cr == size - 1) return true;
|
||||||
|
|
||||||
|
for (int[] d : NEIGHBORS) {
|
||||||
|
int nr = cr + d[0], nc = cc + d[1];
|
||||||
|
if (inBounds(nr, nc)
|
||||||
|
&& !visited[nr][nc]
|
||||||
|
&& getCell(nr, nc) == Player.PLAYER2) {
|
||||||
|
visited[nr][nc] = true;
|
||||||
|
stack.push(new int[]{nr, nc});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLegal(AbstractPly move) {
|
||||||
|
if (!(move instanceof HexPly)) return false;
|
||||||
|
HexPly hp = (HexPly) move;
|
||||||
|
int r = hp.getRow(), c = hp.getCol();
|
||||||
|
return inBounds(r, c)
|
||||||
|
&& getCell(r, c) == null
|
||||||
|
&& hp.getPlayer() == getCurrentPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Teste si un coup est immédiatement gagnant.
|
||||||
|
*
|
||||||
|
* On joue le coup, on teste la victoire, puis on annule le coup.
|
||||||
|
* Cela permet d'évaluer un coup sans modifier définitivement l'état du plateau.
|
||||||
|
*
|
||||||
|
* @param move coup à tester
|
||||||
|
* @return true si après ce coup le joueur a gagné, false sinon
|
||||||
|
*/
|
||||||
|
public boolean isWinningMove(AbstractPly move) {
|
||||||
|
if (!isLegal(move)) return false;
|
||||||
|
Player p = move.getPlayer();
|
||||||
|
|
||||||
|
doPly(move);
|
||||||
|
boolean winNow = (p == Player.PLAYER1) ? hasPlayer1Won() : hasPlayer2Won();
|
||||||
|
undoPly();
|
||||||
|
|
||||||
|
return winNow;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doPly(AbstractPly move) {
|
||||||
|
if (!(move instanceof HexPly)) {
|
||||||
|
throw new IllegalArgumentException("Coup invalide: " + move);
|
||||||
|
}
|
||||||
|
if (!isLegal(move)) {
|
||||||
|
throw new IllegalStateException("Coup illégal: " + move);
|
||||||
|
}
|
||||||
|
|
||||||
|
HexPly hp = (HexPly) move;
|
||||||
|
setCell(hp.getRow(), hp.getCol(), hp.getPlayer());
|
||||||
|
addPlyToHistory(move);
|
||||||
|
setNextPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undoPly() {
|
||||||
|
AbstractPly last = removePlyFromHistory();
|
||||||
|
HexPly hp = (HexPly) last;
|
||||||
|
|
||||||
|
setCell(hp.getRow(), hp.getCol(), null);
|
||||||
|
setNextPlayer();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isGameOver() {
|
||||||
|
return hasPlayer1Won() || hasPlayer2Won();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Result getResult() {
|
||||||
|
if (!isGameOver()) return null;
|
||||||
|
if (hasPlayer1Won()) return Result.WIN; // du point de vue PLAYER1
|
||||||
|
return Result.LOSS;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Iterator<AbstractPly> iterator() {
|
||||||
|
Player me = getCurrentPlayer();
|
||||||
|
List<AbstractPly> moves = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int r = 0; r < size; r++) {
|
||||||
|
for (int c = 0; c < size; c++) {
|
||||||
|
if (getCell(r, c) == null) {
|
||||||
|
moves.add(new HexPly(me, r, c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moves.iterator();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public IBoard safeCopy() {
|
||||||
|
HexBoard copy = new HexBoard(this.size, this.getCurrentPlayer());
|
||||||
|
for (int r = 0; r < size; r++) {
|
||||||
|
System.arraycopy(this.cells[r], 0, copy.cells[r], 0, size);
|
||||||
|
}
|
||||||
|
return copy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
for (int r = 0; r < size; r++) {
|
||||||
|
for (int k = 0; k < r; k++) sb.append(" ");
|
||||||
|
for (int c = 0; c < size; c++) {
|
||||||
|
Player p = getCell(r, c);
|
||||||
|
char ch = '.';
|
||||||
|
if (p == Player.PLAYER1) ch = '1';
|
||||||
|
else if (p == Player.PLAYER2) ch = '2';
|
||||||
|
sb.append(ch).append(" ");
|
||||||
|
}
|
||||||
|
sb.append("\n");
|
||||||
|
}
|
||||||
|
sb.append("Current player: ").append(getCurrentPlayer()).append("\n");
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Player getCellPlayer(int r, int c) {
|
||||||
|
return cells[r][c];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
52
javaAPI/fr/iut_fbleau/HexGame/HexFrame.java
Normal file
52
javaAPI/fr/iut_fbleau/HexGame/HexFrame.java
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.GameAPI.Player;
|
||||||
|
import fr.iut_fbleau.GameAPI.Result;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class HexFrame {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
int size = 11;
|
||||||
|
if (args.length >= 1) {
|
||||||
|
try { size = Integer.parseInt(args[0]); } catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
HexBoard board = new HexBoard(size);
|
||||||
|
|
||||||
|
JFrame frame = new JFrame("Hex - " + size + "x" + size);
|
||||||
|
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||||
|
frame.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
JLabel statusLabel = new JLabel("", SwingConstants.CENTER);
|
||||||
|
statusLabel.setFont(statusLabel.getFont().deriveFont(Font.BOLD, 18f));
|
||||||
|
statusLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
|
||||||
|
HexPanel panel = new HexPanel(board, statusLabel);
|
||||||
|
|
||||||
|
frame.add(statusLabel, BorderLayout.NORTH);
|
||||||
|
frame.add(panel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// Taille confortable
|
||||||
|
frame.pack();
|
||||||
|
frame.setLocationRelativeTo(null);
|
||||||
|
frame.setVisible(true);
|
||||||
|
|
||||||
|
// Message initial
|
||||||
|
updateStatus(board, statusLabel);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static void updateStatus(HexBoard board, JLabel statusLabel) {
|
||||||
|
if (board.isGameOver()) {
|
||||||
|
Result r = board.getResult(); // résultat du point de vue PLAYER1
|
||||||
|
Player winner = (r == Result.WIN) ? Player.PLAYER1 : Player.PLAYER2;
|
||||||
|
statusLabel.setText("" + winner + " a gagné !");
|
||||||
|
} else {
|
||||||
|
statusLabel.setText("C'est à " + board.getCurrentPlayer() + " de jouer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
javaAPI/fr/iut_fbleau/HexGame/HexMain.java
Normal file
34
javaAPI/fr/iut_fbleau/HexGame/HexMain.java
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.GameAPI.*;
|
||||||
|
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lancement d'une partie de Hex en console.
|
||||||
|
*/
|
||||||
|
public class HexMain {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
int size = 7;
|
||||||
|
if (args.length >= 1) {
|
||||||
|
try { size = Integer.parseInt(args[0]); } catch (NumberFormatException ignored) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
HexBoard board = new HexBoard(size);
|
||||||
|
|
||||||
|
Scanner sc = new Scanner(System.in);
|
||||||
|
EnumMap<Player, AbstractGamePlayer> players = new EnumMap<>(Player.class);
|
||||||
|
players.put(Player.PLAYER1, new HumanConsolePlayer(Player.PLAYER1, sc));
|
||||||
|
players.put(Player.PLAYER2, new HumanConsolePlayer(Player.PLAYER2, sc));
|
||||||
|
|
||||||
|
AbstractGame game = new AbstractGame(board, players) {};
|
||||||
|
Result res = game.run();
|
||||||
|
|
||||||
|
System.out.println(board);
|
||||||
|
System.out.println("Résultat (du point de vue de PLAYER1) : " + res);
|
||||||
|
|
||||||
|
sc.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
166
javaAPI/fr/iut_fbleau/HexGame/HexPanel.java
Normal file
166
javaAPI/fr/iut_fbleau/HexGame/HexPanel.java
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.GameAPI.Player;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.MouseAdapter;
|
||||||
|
import java.awt.event.MouseEvent;
|
||||||
|
import java.awt.geom.Path2D;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Panel Swing qui dessine un plateau Hex en hexagones et gère les clics.
|
||||||
|
*
|
||||||
|
* Grille "flat-top" (hexagones à sommet plat en haut),
|
||||||
|
* avec décalage vertical d'une demi-hauteur une colonne sur deux.
|
||||||
|
*/
|
||||||
|
public class HexPanel extends JPanel {
|
||||||
|
|
||||||
|
private final HexBoard board;
|
||||||
|
private final JLabel statusLabel;
|
||||||
|
|
||||||
|
// Rayon (distance centre -> sommet)
|
||||||
|
private final int s = 26;
|
||||||
|
private final int margin = 40;
|
||||||
|
// pointy-top : largeur = sqrt(3)*s, hauteur = 2*s
|
||||||
|
private final double hexW = Math.sqrt(3) * s;
|
||||||
|
private final double hexVStep = 1.5 * s; // distance verticale entre centres
|
||||||
|
|
||||||
|
|
||||||
|
private Shape[][] hexShapes;
|
||||||
|
|
||||||
|
public HexPanel(HexBoard board, JLabel statusLabel) {
|
||||||
|
this.board = board;
|
||||||
|
this.statusLabel = statusLabel;
|
||||||
|
this.hexShapes = new Shape[board.getSize()][board.getSize()];
|
||||||
|
|
||||||
|
setBackground(Color.WHITE);
|
||||||
|
|
||||||
|
addMouseListener(new MouseAdapter() {
|
||||||
|
@Override
|
||||||
|
public void mouseClicked(MouseEvent e) {
|
||||||
|
handleClick(e.getX(), e.getY());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dimension getPreferredSize() {
|
||||||
|
int n = board.getSize();
|
||||||
|
|
||||||
|
// largeur : n * hexW + décalage max (hexW/2) + marges
|
||||||
|
int w = margin * 2 + (int) (n * hexW + hexW / 2);
|
||||||
|
|
||||||
|
// hauteur : (n-1)*1.5*s + 2*s + marges
|
||||||
|
int h = margin * 2 + (int) ((n - 1) * hexVStep + 2 * s);
|
||||||
|
|
||||||
|
return new Dimension(w, h);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void handleClick(int x, int y) {
|
||||||
|
if (board.isGameOver()) return;
|
||||||
|
|
||||||
|
int n = board.getSize();
|
||||||
|
for (int row = 0; row < n; row++) {
|
||||||
|
for (int col = 0; col < n; col++) {
|
||||||
|
Shape sh = hexShapes[row][col];
|
||||||
|
if (sh != null && sh.contains(x, y)) {
|
||||||
|
HexPly ply = new HexPly(board.getCurrentPlayer(), row, col);
|
||||||
|
if (board.isLegal(ply)) {
|
||||||
|
board.doPly(ply);
|
||||||
|
HexFrame.updateStatus(board, statusLabel);
|
||||||
|
repaint();
|
||||||
|
} else {
|
||||||
|
Toolkit.getDefaultToolkit().beep();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void paintComponent(Graphics g) {
|
||||||
|
super.paintComponent(g);
|
||||||
|
|
||||||
|
Graphics2D g2 = (Graphics2D) g.create();
|
||||||
|
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||||
|
|
||||||
|
// Bordures objectifs (bleu gauche/droite, rouge haut/bas)
|
||||||
|
drawGoalBorders(g2);
|
||||||
|
|
||||||
|
int n = board.getSize();
|
||||||
|
|
||||||
|
// IMPORTANT : boucles cohérentes -> row puis col
|
||||||
|
for (int row = 0; row < n; row++) {
|
||||||
|
for (int col = 0; col < n; col++) {
|
||||||
|
|
||||||
|
Shape hex = createHexShape(row, col);
|
||||||
|
hexShapes[row][col] = hex;
|
||||||
|
|
||||||
|
Player p = board.getCellPlayer(row, col);
|
||||||
|
g2.setColor(colorForCell(p));
|
||||||
|
g2.fill(hex);
|
||||||
|
|
||||||
|
g2.setColor(new Color(120, 120, 120));
|
||||||
|
g2.setStroke(new BasicStroke(1.2f));
|
||||||
|
g2.draw(hex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
g2.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Color colorForCell(Player p) {
|
||||||
|
if (p == Player.PLAYER1) return new Color(30, 90, 160); // bleu
|
||||||
|
if (p == Player.PLAYER2) return new Color(220, 50, 50); // rouge
|
||||||
|
return new Color(190, 190, 190); // gris
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pointy-top + décalage par ligne :
|
||||||
|
*
|
||||||
|
* centreX = margin + hexW/2 + col*hexW + (row%2)*(hexW/2)
|
||||||
|
* centreY = margin + s + row*(1.5*s)
|
||||||
|
*/
|
||||||
|
private Shape createHexShape(int row, int col) {
|
||||||
|
double cx = margin + (hexW / 2.0) + col * hexW + ((row % 2) * (hexW / 2.0));
|
||||||
|
double cy = margin + s + row * hexVStep;
|
||||||
|
|
||||||
|
Path2D.Double path = new Path2D.Double();
|
||||||
|
for (int i = 0; i < 6; i++) {
|
||||||
|
double angle = Math.toRadians(i * 60); // pointy-top
|
||||||
|
double x = cx + s * Math.cos(angle);
|
||||||
|
double y = cy + s * Math.sin(angle);
|
||||||
|
if (i == 0) path.moveTo(x, y);
|
||||||
|
else path.lineTo(x, y);
|
||||||
|
}
|
||||||
|
path.closePath();
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private void drawGoalBorders(Graphics2D g2) {
|
||||||
|
int n = board.getSize();
|
||||||
|
|
||||||
|
double leftX = margin - 12;
|
||||||
|
double rightX = margin + (hexW / 2.0) + (n - 1) * hexW + (hexW / 2.0) + (hexW / 2.0) + 12;
|
||||||
|
// explication: largeur n colonnes + potentiel décalage d'une demi-largeur
|
||||||
|
|
||||||
|
double topY = margin - 12;
|
||||||
|
double bottomY = margin + s + (n - 1) * hexVStep + s + 12;
|
||||||
|
|
||||||
|
g2.setStroke(new BasicStroke(6f, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND));
|
||||||
|
|
||||||
|
// Bleu: gauche / droite (objectif PLAYER1)
|
||||||
|
g2.setColor(new Color(30, 90, 160));
|
||||||
|
g2.drawLine((int) leftX, (int) topY, (int) leftX, (int) bottomY);
|
||||||
|
g2.drawLine((int) rightX, (int) topY, (int) rightX, (int) bottomY);
|
||||||
|
|
||||||
|
// Rouge: haut / bas (objectif PLAYER2)
|
||||||
|
g2.setColor(new Color(220, 50, 50));
|
||||||
|
g2.drawLine((int) leftX, (int) topY, (int) rightX, (int) topY);
|
||||||
|
g2.drawLine((int) leftX, (int) bottomY, (int) rightX, (int) bottomY);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
javaAPI/fr/iut_fbleau/HexGame/HexPly.java
Normal file
31
javaAPI/fr/iut_fbleau/HexGame/HexPly.java
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.GameAPI.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Représente un coup dans le jeu de Hex.
|
||||||
|
*/
|
||||||
|
public class HexPly extends AbstractPly {
|
||||||
|
|
||||||
|
private final int row;
|
||||||
|
private final int col;
|
||||||
|
|
||||||
|
public HexPly(Player j, int row, int col) {
|
||||||
|
super(j);
|
||||||
|
this.row = row;
|
||||||
|
this.col = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getRow() {
|
||||||
|
return this.row;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCol() {
|
||||||
|
return this.col;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "HexPly{player=" + getPlayer() + ", row=" + row + ", col=" + col + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
204
javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
Normal file
204
javaAPI/fr/iut_fbleau/HexGame/HexSimMain.java
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.GameAPI.*;
|
||||||
|
|
||||||
|
import java.io.BufferedWriter;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.EnumMap;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lance un grand nombre de parties Hex entre 2 bots aléatoires et affiche des stats.
|
||||||
|
*
|
||||||
|
* Exemples :
|
||||||
|
* java fr.iut_fbleau.HexGame.HexSimMain
|
||||||
|
* java fr.iut_fbleau.HexGame.HexSimMain --games 10000 --size 7 --seed 123
|
||||||
|
* java fr.iut_fbleau.HexGame.HexSimMain --games 5000 --size 11 --csv results.csv
|
||||||
|
*/
|
||||||
|
public class HexSimMain {
|
||||||
|
|
||||||
|
private static class Stats {
|
||||||
|
long win = 0;
|
||||||
|
long draw = 0;
|
||||||
|
long loss = 0;
|
||||||
|
|
||||||
|
long totalMoves = 0;
|
||||||
|
long minMoves = Long.MAX_VALUE;
|
||||||
|
long maxMoves = Long.MIN_VALUE;
|
||||||
|
|
||||||
|
void record(Result r, int moves) {
|
||||||
|
if (r == Result.WIN) win++;
|
||||||
|
else if (r == Result.DRAW) draw++;
|
||||||
|
else if (r == Result.LOSS) loss++;
|
||||||
|
totalMoves += moves;
|
||||||
|
minMoves = Math.min(minMoves, moves);
|
||||||
|
maxMoves = Math.max(maxMoves, moves);
|
||||||
|
}
|
||||||
|
|
||||||
|
long games() { return win + draw + loss; }
|
||||||
|
|
||||||
|
double winRate() { return games() == 0 ? 0.0 : (double) win / games(); }
|
||||||
|
double drawRate() { return games() == 0 ? 0.0 : (double) draw / games(); }
|
||||||
|
double lossRate() { return games() == 0 ? 0.0 : (double) loss / games(); }
|
||||||
|
double avgMoves() { return games() == 0 ? 0.0 : (double) totalMoves / games(); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("Games: ").append(games()).append("\n");
|
||||||
|
sb.append("WIN: ").append(win).append(String.format(" (%.2f%%)\n", 100.0 * winRate()));
|
||||||
|
sb.append("DRAW: ").append(draw).append(String.format(" (%.2f%%)\n", 100.0 * drawRate()));
|
||||||
|
sb.append("LOSS: ").append(loss).append(String.format(" (%.2f%%)\n", 100.0 * lossRate()));
|
||||||
|
sb.append(String.format("Moves: avg=%.2f, min=%d, max=%d\n", avgMoves(), minMoves, maxMoves));
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class Args {
|
||||||
|
int size = 7;
|
||||||
|
int games = 1000;
|
||||||
|
long seed = System.currentTimeMillis();
|
||||||
|
int progressEvery = 0; // 0 = pas de progress
|
||||||
|
String csvPath = null; // si non null, export par partie
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
Args a = parseArgs(args);
|
||||||
|
|
||||||
|
System.out.println("Hex random-vs-random simulation");
|
||||||
|
System.out.println(" size=" + a.size + " games=" + a.games + " seed=" + a.seed +
|
||||||
|
(a.csvPath != null ? " csv=" + a.csvPath : ""));
|
||||||
|
|
||||||
|
Random master = new Random(a.seed);
|
||||||
|
Stats stats = new Stats();
|
||||||
|
|
||||||
|
BufferedWriter csv = null;
|
||||||
|
try {
|
||||||
|
if (a.csvPath != null) {
|
||||||
|
csv = new BufferedWriter(new FileWriter(a.csvPath));
|
||||||
|
csv.write("game_index,result_p1,moves\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < a.games; i++) {
|
||||||
|
// Nouveau plateau, nouveaux bots (seeds dérivés du seed principal)
|
||||||
|
HexBoard board = new HexBoard(a.size);
|
||||||
|
|
||||||
|
EnumMap<Player, AbstractGamePlayer> players = new EnumMap<>(Player.class);
|
||||||
|
players.put(Player.PLAYER1, new RandomBot(Player.PLAYER1, new Random(master.nextLong())));
|
||||||
|
players.put(Player.PLAYER2, new RandomBot(Player.PLAYER2, new Random(master.nextLong())));
|
||||||
|
|
||||||
|
int moves = runOneGame(board, players);
|
||||||
|
|
||||||
|
Result res = board.getResult();
|
||||||
|
stats.record(res, moves);
|
||||||
|
|
||||||
|
if (csv != null) {
|
||||||
|
csv.write(i + "," + res + "," + moves + "\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (a.progressEvery > 0 && (i + 1) % a.progressEvery == 0) {
|
||||||
|
System.out.println("Progress: " + (i + 1) + "/" + a.games);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("\n=== SUMMARY (Result is from PLAYER1 perspective) ===");
|
||||||
|
System.out.println(stats);
|
||||||
|
|
||||||
|
} catch (IOException e) {
|
||||||
|
System.err.println("I/O error: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
} finally {
|
||||||
|
if (csv != null) {
|
||||||
|
try { csv.close(); } catch (IOException ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boucle de jeu (même logique que AbstractGame.run, mais on compte les coups).
|
||||||
|
* On ne modifie pas GameAPI.
|
||||||
|
*/
|
||||||
|
private static int runOneGame(IBoard board, EnumMap<Player, AbstractGamePlayer> players) {
|
||||||
|
int moves = 0;
|
||||||
|
int guardMaxMoves = ((HexBoard) board).getSize() * ((HexBoard) board).getSize(); // au pire : plateau rempli
|
||||||
|
|
||||||
|
while (!board.isGameOver()) {
|
||||||
|
AbstractGamePlayer p = players.get(board.getCurrentPlayer());
|
||||||
|
IBoard safe = board.safeCopy();
|
||||||
|
AbstractPly ply = p.giveYourMove(safe);
|
||||||
|
|
||||||
|
if (!board.isLegal(ply)) {
|
||||||
|
throw new IllegalStateException("Illegal move: " + ply + " by " + board.getCurrentPlayer());
|
||||||
|
}
|
||||||
|
board.doPly(ply);
|
||||||
|
moves++;
|
||||||
|
|
||||||
|
if (moves > guardMaxMoves) {
|
||||||
|
throw new IllegalStateException("Too many moves (" + moves + "), something is wrong.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return moves;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Args parseArgs(String[] args) {
|
||||||
|
Args a = new Args();
|
||||||
|
for (int i = 0; i < args.length; i++) {
|
||||||
|
String s = args[i];
|
||||||
|
switch (s) {
|
||||||
|
case "--size":
|
||||||
|
a.size = Integer.parseInt(nextArg(args, ++i, "--size requires a value"));
|
||||||
|
break;
|
||||||
|
case "--games":
|
||||||
|
a.games = Integer.parseInt(nextArg(args, ++i, "--games requires a value"));
|
||||||
|
break;
|
||||||
|
case "--seed":
|
||||||
|
a.seed = Long.parseLong(nextArg(args, ++i, "--seed requires a value"));
|
||||||
|
break;
|
||||||
|
case "--progress":
|
||||||
|
a.progressEvery = Integer.parseInt(nextArg(args, ++i, "--progress requires a value"));
|
||||||
|
break;
|
||||||
|
case "--csv":
|
||||||
|
a.csvPath = nextArg(args, ++i, "--csv requires a value");
|
||||||
|
break;
|
||||||
|
case "--help":
|
||||||
|
case "-h":
|
||||||
|
printHelpAndExit();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// compat: si l'utilisateur donne juste un nombre, on l'interprète comme size ou games
|
||||||
|
// ex: "7 10000"
|
||||||
|
if (isInt(s)) {
|
||||||
|
int v = Integer.parseInt(s);
|
||||||
|
if (a.size == 7) a.size = v;
|
||||||
|
else a.games = v;
|
||||||
|
} else {
|
||||||
|
System.err.println("Unknown arg: " + s);
|
||||||
|
printHelpAndExit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String nextArg(String[] args, int idx, String errMsg) {
|
||||||
|
if (idx < 0 || idx >= args.length) throw new IllegalArgumentException(errMsg);
|
||||||
|
return args[idx];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInt(String s) {
|
||||||
|
try { Integer.parseInt(s); return true; } catch (NumberFormatException e) { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void printHelpAndExit() {
|
||||||
|
System.out.println("Usage: java fr.iut_fbleau.HexGame.HexSimMain [options]\n" +
|
||||||
|
"Options:\n" +
|
||||||
|
" --size N Board size (default 7)\n" +
|
||||||
|
" --games N Number of games (default 1000)\n" +
|
||||||
|
" --seed N Random seed (default current time)\n" +
|
||||||
|
" --progress N Print progress every N games (default 0)\n" +
|
||||||
|
" --csv FILE Write per-game results to CSV\n" +
|
||||||
|
" -h, --help Show this help\n");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
68
javaAPI/fr/iut_fbleau/HexGame/HumanConsolePlayer.java
Normal file
68
javaAPI/fr/iut_fbleau/HexGame/HumanConsolePlayer.java
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
package fr.iut_fbleau.HexGame;
|
||||||
|
|
||||||
|
import fr.iut_fbleau.GameAPI.*;
|
||||||
|
|
||||||
|
import java.util.Scanner;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Joueur humain en console.
|
||||||
|
*
|
||||||
|
* Format attendu : "row col" (indices à partir de 0).
|
||||||
|
*/
|
||||||
|
public class HumanConsolePlayer extends AbstractGamePlayer {
|
||||||
|
|
||||||
|
private final Scanner in;
|
||||||
|
|
||||||
|
public HumanConsolePlayer(Player me, Scanner in) {
|
||||||
|
super(me);
|
||||||
|
this.in = in;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbstractPly giveYourMove(IBoard board) {
|
||||||
|
if (!(board instanceof HexBoard)) {
|
||||||
|
throw new IllegalArgumentException("Ce joueur attend un HexBoard.");
|
||||||
|
}
|
||||||
|
HexBoard hb = (HexBoard) board;
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
System.out.println(hb);
|
||||||
|
System.out.print("Joueur " + board.getCurrentPlayer() + " - entrez un coup (row col) : ");
|
||||||
|
|
||||||
|
String line = in.nextLine().trim();
|
||||||
|
if (line.equalsIgnoreCase("quit") || line.equalsIgnoreCase("exit")) {
|
||||||
|
throw new IllegalStateException("Partie interrompue par l'utilisateur.");
|
||||||
|
}
|
||||||
|
if (line.equalsIgnoreCase("help")) {
|
||||||
|
System.out.println("Entrez deux entiers : row col (0 <= row,col < " + hb.getSize() + ")");
|
||||||
|
System.out.println("Commandes: help, quit");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] parts = line.split("\\s+");
|
||||||
|
if (parts.length != 2) {
|
||||||
|
System.out.println("Format invalide. Exemple: 3 4");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
int r = Integer.parseInt(parts[0]);
|
||||||
|
int c = Integer.parseInt(parts[1]);
|
||||||
|
HexPly ply = new HexPly(board.getCurrentPlayer(), r, c);
|
||||||
|
|
||||||
|
if (!hb.isLegal(ply)) {
|
||||||
|
System.out.println("Coup illégal (case occupée / hors plateau / mauvais joueur). Réessayez.");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hb.isWinningMove(ply)) {
|
||||||
|
System.out.println("Coup gagnant !");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ply;
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
System.out.println("Veuillez entrer deux entiers.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
Normal file
44
javaAPI/fr/iut_fbleau/HexGame/RandomBot.java
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bot non intelligent : joue un coup légal au hasard.
|
||||||
|
*/
|
||||||
|
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) {
|
||||||
|
// On récupère tous les coups légaux via l'itérateur fourni par le plateau.
|
||||||
|
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()));
|
||||||
|
}
|
||||||
|
}
|
||||||
10001
results.csv
Normal file
10001
results.csv
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user