Commit eb9ed47e authored by Franck TEMPET's avatar Franck TEMPET

Ajout du jeu Tic Tac Toe

Code Java pour l'implémentation de l'algorithme MCTS pour les jeux Tic Tac Toe et Ultimate Tic Tac Toe.
parent e8e8a973
# Algorithme Monte Carlo Tree Search
# Objectif
Votre objectif sera d’implémenter l’algorithme MonteCarlo Tree Search sur le jeu TicTacToe présent sur la plateforme [Codingame](https://www.codingame.com/start). Vous partirez d’un code Java existant afin de vous focaliser essentiellement sur cet algorithme.
# Récupérer le code Java
Les sources sont sur [Gvipers](https://gvipers.imt-nord-europe.fr/franck.tempet/aicoi).
Voici la commande pour cloner le dépôt :
```bash
git clone http://gvipers.imt-nord-europe.fr/franck.tempet/aicoi.git
```
## Quelques explications sur le code
### La classe Player
Cette classe contient la méthode **main** du programme. Elle crée un joueur «*humain*» ainsi qu’un joueur «*Ordinateur*». L’ordinateur joue les coups de manière aléatoire.
### La classe Plateau
Regarder les méthodes contenues dans cette classe ainsi que les commentaires associés. Cette classe est une classe abstraite qui liste les méthodes nécessaires à la manipulation d’un jeu de plateau à deux joueurs. Vous aurez besoin essentiellement des méthodes suivantes :
>- partieTerminee()
>- getListeCoups()
>- vainqueur()
>- joueCoup()
>- sauvegardePosition()
>- restaurePosition()
>- ….
### La classe GrilleTicTacToe3x3
Cette classe représente la grille du jeu TicTacToe de dimension 3x3. Cette classe hérite de Plateau, elle implémente donc toutes les méthodes abstraites présentes dans la classe Plateau. C’est cette grille que vous utiliserez dans un premier temps.
### La classe GrilleTicTacToe9x9
Cette classe représente la grille du jeu TicTacToe de dimension 9x9. Cette classe hérite de Plateau, elle implémente donc toutes les méthodes abstraites présentes dans la classe Plateau. Cette grille sera utilisée lorsque vous aurez franchi le premier niveau sur Codingame.
### La Classe Noeud
Cette classe représente un nœud dans l'arbre de recherche de l'algorithme Monte Carlo Tree Search (MCTS). Chaque nœud représente une position possible dans le jeu de Tic Tac Toe.
### La classe AlgoUCB
Cette classe représente l'algorithme UCB (Upper Confidence Bound). **Vous devrez compléter cette classe.**
### La classe MonteCarlo
Cette classe est implémentation générique de l'algorithme de recherche Monte Carlo Tree Search (MCTS). **Vous devrez compléter cette classe.**
# Implémenter l'algorithme
Regardez en détail le contenu de la classe **Player**. La création de l’IA ( *joueurOrdi* ) nécessite d’appeler la méthode **setAlgoRecherche** en lui passant en paramètre une instance d’une classe qui implémente l’interface **AlgoRecherche**. Dans notre cas c'est la classe **AlgoRechercheAleatoire** qui implémente cette interface. Elle implémente donc la méthode suivante :
```java
public Coup meilleurCoup(Plateau _plateau, Joueur _joueur)
```
On doit passer à cette méthode le plateau du jeu ( *ici GrilleTicTacToe3x3* ) , le joueur à qui c'est le tour de jouer.
L'algorithme aléatoire récupère la liste des coups possibles à partir de la position courante et en choisit un au hasard.
```java
Random rnd;
public Coup meilleurCoup(Plateau _plateau, Joueur _joueur ) {
ArrayList<Coup> coups = _plateau.getListeCoups(_joueur);
return coups.get(rnd.nextInt( coups.size()));
}
```
Il vous est demandé de terminer l'implémentation de la classe **AlgoUCB**, ainsi que de la classe **MonteCarlo** qui hérite de la classe **AlgoRecherche**.
# Tester son IA en local
La classe Player actuelle vous permet de jouer contre votre IA. Vous pouvez modifier facilement cette classe afin de faire jouer deux IA l’une contre l’autre.
# Tester son IA sur Codingame
Afin de tester votre IA sur *Codingame*, il vous faudra modifier le contenu de la classe **Player** pour être compatible avec le mode de fonctionnement de la plateforme, à savoir récupérer les données en entrée ( code déjà fourni par le site ) et retourner sur la sortie standard votre coup. La classe **Arbitre** ne sera plus nécessaire puisque se sera la plateforme Codingame qui fera office d'arbitre. La classe **Player** contient également la version pour le bon fonctionnement de votre IA sur Codingame. Il suffit de dé-commenter la partie *Codingame* et commenter le jeu en local
Pour générer un seul fichier à partir de l’ensemble de vos classes vous utiliserez le **builder.jar** en effectuant les commandes suivantes.
Lancez l’invite de commande **cmd.exe** et tapez les commandes suivantes :
```bash
# C est la lettre du disque dur où se trouve votre projet
C:
# Se positionner dans le répertoire src du projet
# exemple cd c:\netbean\TicTacToeCodingame\src
cd <nom_du_répertoire source>
# Lancer le builder.jar
# Cette commande va générer un fichier Player.java dans le répertoire courant.
# C’est ce fichier qu’il faudra poster sur codingame.
java –jar builder.jar .\tictactoecodingame\Player.java
```
package tictactoecodingame;
/**
* @author Franck
*
*/
public abstract class AlgoRecherche {
/**
*
* @param _plateau Grille du jeu
* @param _joueur c'est à ce joueur de jouer
* @return
*/
abstract public Coup meilleurCoup( Plateau _plateau , Joueur _joueur );
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tictactoecodingame;
import java.util.ArrayList;
import java.util.Random;
public class AlgoRechercheAleatoire extends AlgoRecherche{
Random rnd;
public AlgoRechercheAleatoire() {
rnd = new Random();
}
@Override
public Coup meilleurCoup(Plateau _plateau, Joueur _joueur) {
ArrayList<Coup> coups = _plateau.getListeCoups(_joueur);
return coups.get(rnd.nextInt( coups.size()));
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tictactoecodingame;
import java.util.ArrayList;
interface AlgoSelection {
// L'algo choisi un noeud (son index dans le tableau ) parmi les noeuds fils du noeud passes en parametre
public int choisir( Noeud _noeud );
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tictactoecodingame;
/**
* Cette classe implémente l'interface AlgoSelection et représente l'algorithme UCB (Upper Confidence Bound).
* L'algorithme UCB équilibre l'exploration et l'exploitation en sélectionnant le nœud enfant avec le score le plus élevé.
* Le score est calculé en fonction du nombre de victoires du nœud enfant, du nombre de fois que le nœud enfant a été joué,
* et du nombre de fois que le nœud parent a été joué.
*/
public class AlgoUCB implements AlgoSelection {
double C ;
/**
* Ce constructeur initialise le facteur d'exploration C pour l'algorithme UCB.
*
* @param _C Le facteur d'exploration pour l'algorithme UCB.
*/
public AlgoUCB( double _C ) {
C = _C;
}
@Override
/**
* Cette méthode est utilisée pour sélectionner le meilleur nœud enfant à partir du nœud donné en fonction de l'algorithme UCB.
*
* @param _noeud Le nœud parent à partir duquel sélectionner le meilleur nœud enfant.
* @return L'index du meilleur nœud enfant dans la liste des nœuds enfants du nœud parent.
*/
public int choisir( Noeud _noeud ) {
// TODO : A coder.
}
}
package tictactoecodingame;
public class Arbitre {
Joueur joueur1; // Joueur qui commence la partie
Joueur joueur2; // Adversaire
Joueur currentJoueur; // C'est à son tour de jouer
Plateau plateau; // Le plateau du jeu
public Arbitre(Plateau _plateau, Joueur _joueur1, Joueur _joueur2) {
plateau = _plateau;
joueur1 = _joueur1;
joueur2 = _joueur2;
currentJoueur = joueur1; // Le joueur1 commence la partie.
}
/**
* Demarre une nouvelle Partie
* Retourne le joueur qui gagne , null si c'est un match nul
*
* @param _trace si vrai affiche le plateau et le joueur courant
*/
public Joueur startNewGame( boolean _trace ) {
Coup coup;
plateau.init(); // Prépare le plateau pour le jeu.
while (!plateau.partieTerminee()) {
do {
if ( _trace ) {
System.out.println(plateau);
System.out.println(currentJoueur + " ( " +currentJoueur.getIdJoueur() + " ) joue : " );
}
coup = currentJoueur.joue(plateau);
if (!plateau.isValide(coup)) {
if ( _trace )
System.err.println("Coup non valide ! : " + currentJoueur);
else { // en mode batch le joueur perd la partie
System.err.println("Coup non valide ! : " + currentJoueur);
if (currentJoueur == joueur1) return joueur2; else return joueur1;
}
}
} while (!plateau.isValide(coup));
plateau.joueCoup(coup);
if (currentJoueur == joueur1) {
currentJoueur = joueur2;
} else {
currentJoueur = joueur1;
}
}
Joueur vainqueur = plateau.vainqueur();
if ( vainqueur != null )
System.out.println( vainqueur + " gagne la partie ");
else
System.out.println( "Partie nulle ");
return vainqueur;
}
public void startTournament( int _nbPartie , boolean _trace) {
double[] nbVictoire = new double[2];
Joueur vainqueur;
currentJoueur = joueur1;
int numJoueur = 0;
nbVictoire[0] = nbVictoire[1] = 0;
for (int i = 0 ; i < _nbPartie ; i++ ) {
vainqueur = startNewGame(_trace);
if ( vainqueur == joueur1 ) nbVictoire[0]++;
if ( vainqueur == joueur2 ) nbVictoire[1]++;
if ( vainqueur == null ) {
nbVictoire[0]+=0.5;
nbVictoire[1]+=0.5;
}
if ( numJoueur == 0 ) {
currentJoueur = joueur2;
numJoueur=1;
}
else {
currentJoueur = joueur1;
numJoueur=0;
}
System.out.println(joueur1 + " score : " + nbVictoire[0]);
System.out.println(joueur2 + " score : " + nbVictoire[1]);
}
System.out.println(joueur1 + " score : " + nbVictoire[0]);
System.out.println(joueur2 + " score : " + nbVictoire[1]);
if (nbVictoire[0] > nbVictoire[1])
System.out.println(joueur1 + " GAGNE ");
else
if (nbVictoire[1] > nbVictoire[0])
System.out.println(joueur2 + " GAGNE ");
else
System.out.println("Match nul");
}
public Joueur getCurrentJoueur() {
return currentJoueur;
}
}
package tictactoecodingame;
public class Case {
int colonne, ligne;
public Case(int _colonne, int _ligne) {
this.colonne = _colonne;
this.ligne = _ligne;
}
public int getColonne() {
return colonne;
}
public int getLigne() {
return ligne;
}
public void setLigne( int _ligne ) {
this.ligne = _ligne;
}
public void setColonne( int _colonne ) {
this.colonne = _colonne;
}
}
package tictactoecodingame;
public abstract class Coup {
private int note;
public Coup() {
note = Integer.MIN_VALUE; // un coup est tres mauvais tant qu'il n'est pas analysé.
}
public void setNote(int _note) {
note = _note;
}
public int getNote() {
return note;
}
abstract public String toString();
abstract public boolean equals(Object obj);
abstract public int hashCode();
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tictactoecodingame;
public class CoupTicTacToe extends Coup {
private int colonne, ligne;
private Jeton jeton;
public CoupTicTacToe( int _colonne , int _ligne , Jeton _jeton ) {
super();
colonne = _colonne;
ligne = _ligne;
jeton = _jeton;
}
public int getColonne() {
return colonne;
}
public int getLigne() {
return ligne;
}
public Jeton getJeton() {
return jeton;
}
public String toString() {
return "(" + colonne + "," + ligne + ")" ;
}
@Override
public boolean equals(Object obj) {
if ( obj == null ) return false;
if (this.getClass() != obj.getClass()) return false;
CoupTicTacToe coup = (CoupTicTacToe)obj;
return colonne == coup.getColonne() && ligne==coup.ligne && jeton.toString().equals(coup.getJeton().toString());
}
@Override
public int hashCode() {
return jeton.toString().hashCode() + colonne * 10 + ligne;
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tictactoecodingame;
import java.util.ArrayList;
public class GrilleTicTacToe3x3 extends Plateau {
Jeton[][] grille = new Jeton[3][3];
Jeton[][][] grilleSav = new Jeton[100][3][3]; // Pour sauvegardr la position. 100 au maximum
CoupTicTacToe[] dernierCoup;
int nbCoupJoue ,nbCouJoueSav;
Joueur vainqueur;
@Override
public void init() {
for (int c = 0; c < this.getNbColonnes(); c++)
for (int l = 0; l < this.getNbLignes(); l++)
grille[c][l] = null;
dernierCoup = new CoupTicTacToe[10];
vainqueur = null;
nbCoupJoue = 0;
}
@Override
public Piece getPiece(Case _case) {
return grille[_case.getColonne()][_case.getLigne()];
}
@Override
public void joueCoup(Coup _coup) {
CoupTicTacToe coup = (CoupTicTacToe) _coup;
grille[coup.getColonne()][coup.getLigne()] = coup.getJeton();
dernierCoup[nbCoupJoue] = coup;
nbCoupJoue++;
}
@Override
public void annuleDernierCoup() {
nbCoupJoue--;
grille[dernierCoup[nbCoupJoue].getColonne()][dernierCoup[nbCoupJoue].getLigne()] = null;
vainqueur = null;
}
@Override
public int getNbColonnes() {
return 3;
}
@Override
public int getNbLignes() {
return 3;
}
@Override
public boolean partieTerminee() {
if (partieGagnee()) {
return true;
}
return isGrillePleine();
}
@Override
public boolean partieGagnee() {
int[][] dir = {{1, 0}, {1, 1}, {0, 1}, {1, -1}};
int[][] dirOps = {{-1, 0}, {-1, -1}, {0, -1}, {-1, 1}};
int x, y;
int nbJetonAligne;
if (nbCoupJoue == 0) {
return false;
}
Joueur dernierJoueur = dernierCoup[nbCoupJoue-1].getJeton()
.getJoueur();
/* Regarde si le dernier coup est gagnant */
for (int d = 0; d < 4; d++) {
nbJetonAligne = 0;
x = dernierCoup[nbCoupJoue-1].getColonne();
y = dernierCoup[nbCoupJoue-1].getLigne();
while (x < this.getNbColonnes() && x >= 0 && y < this.getNbLignes() && y >= 0 && grille[x][y] != null && grille[x][y].getJoueur() == dernierJoueur) {
nbJetonAligne++;
if (nbJetonAligne >= 3) {
vainqueur = dernierJoueur;
return true;
}
x += dir[d][0];
y += dir[d][1];
}
//regarde dans la direction oppose
x = dernierCoup[nbCoupJoue-1].getColonne();
y = dernierCoup[nbCoupJoue-1].getLigne();
nbJetonAligne--;
while (x < this.getNbColonnes() && x >= 0 && y < this.getNbLignes() && y >= 0 && grille[x][y] != null && grille[x][y].getJoueur() == dernierJoueur) {
nbJetonAligne++;
if (nbJetonAligne >= 3) {
vainqueur = dernierJoueur;
return true;
}
x += dirOps[d][0];
y += dirOps[d][1];
}
}
return false;
}
@Override
public boolean partieNulle() {
if (partieGagnee()) {
return false;
}
return isGrillePleine();
}
@Override
public ArrayList<Coup> getListeCoups(Joueur _joueur) {
ArrayList<Coup> listeCoups = new ArrayList<Coup>();
for (int c = 0; c < this.getNbColonnes(); c++) {
for (int l = 0; l < this.getNbLignes(); l++) {
if (grille[c][l] == null)
listeCoups.add(new CoupTicTacToe(c, l, new Jeton(_joueur)));
}
}
return listeCoups;
}
@Override
public boolean isValide(Coup _coup) {
CoupTicTacToe coup = (CoupTicTacToe) _coup;
return grille[coup.getColonne()][coup.getLigne()] == null;
}
@Override
public Coup stringToCoup(String _coup, Joueur _joueur) {
int colonne = Integer.valueOf(_coup.charAt(0)+"");
int ligne = Integer.valueOf(_coup.charAt(1)+"");
return new CoupTicTacToe(colonne, ligne , new Jeton(_joueur) );
}
@Override
public void sauvegardePosition() {
for (int c = 0; c < this.getNbColonnes(); c++)
for (int l = 0; l < this.getNbLignes(); l++)
grilleSav[0][c][l] = grille[c][l];
nbCouJoueSav = nbCoupJoue;
}
@Override
public void restaurePosition() {
for (int c = 0; c < this.getNbColonnes(); c++)
for (int l = 0; l < this.getNbLignes(); l++)
grille[c][l] = grilleSav[0][c][l];
vainqueur = null;
nbCoupJoue = nbCouJoueSav;
}
private boolean isGrillePleine() {
for (int c = 0; c < this.getNbColonnes(); c++) {
for (int l = 0; l < this.getNbLignes(); l++) {
if (grille[c][l] == null) {
return false;
}
}
}
return true;
}
@Override
public Joueur vainqueur() {
return vainqueur;
}
@Override
public Coup getDernierCoup() {
if ( nbCoupJoue < 1 ) return null;
return dernierCoup[nbCoupJoue-1];
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tictactoecodingame;
import java.util.ArrayList;
public class GrilleTicTacToe9x9 extends Plateau {
Jeton[][] grille9x9 = new Jeton[9][9];
Jeton[][] grille3x3 = new Jeton[3][3]; // indique si un joueur à gagné un case de la grande grille
Jeton[][][] grille9x9Sav = new Jeton[100][9][9]; // Pour sauvegarder la position. 100 positions Max
Jeton[][][] grille3x3Sav = new Jeton[100][3][3]; // Pour sauvegarder la position
CoupTicTacToe[] dernierCoup;
int nbCoupJoue;
int nbCoupJoueSav;
CoupTicTacToe dernierCoupSav;
Joueur vainqueur;
boolean grilleGagnee;
boolean[] grilleGagneeSav; // vrai si dernier coup gagne une grille
@Override
public void init() {
for (int c = 0; c < this.getNbColonnes(); c++) {
for (int l = 0; l < this.getNbLignes(); l++) {
grille9x9[c][l] = null;
}
}
for (int c = 0; c < 3; c++) {
for (int l = 0; l < 3; l++) {
grille3x3[c][l] = null;
}
}
dernierCoup = new CoupTicTacToe[100];
nbCoupJoue = 0;
nbCoupJoueSav = 0;
grilleGagneeSav = new boolean[100];
vainqueur = null;
grilleGagnee = false;
}
@Override
public Piece getPiece(Case _case) {
return grille9x9[_case.getColonne()][_case.getLigne()];
}
@Override
public void joueCoup(Coup _coup) {
CoupTicTacToe coup = (CoupTicTacToe) _coup;
grille9x9[coup.getColonne()][coup.getLigne()] = coup.getJeton();
dernierCoup[nbCoupJoue] = coup;
grilleGagneeSav[nbCoupJoue] = grilleGagnee;
nbCoupJoue++;
int x0 = (coup.getColonne() / 3) * 3;
int y0 = (coup.getLigne() / 3) * 3;
// System.out.println("Test grande grille");
if (caseGagnante(grille9x9, x0, y0, coup.getColonne(), coup.getLigne())) {
grille3x3[coup.getColonne() / 3][coup.getLigne() / 3] = coup.getJeton();
grilleGagnee = true;
}
}
@Override
public void annuleDernierCoup() {
nbCoupJoue--;
grille9x9[dernierCoup[nbCoupJoue].getColonne()][dernierCoup[nbCoupJoue].getLigne()] = null;
grilleGagnee = grilleGagneeSav[nbCoupJoue];
grille3x3[dernierCoup[nbCoupJoue].getColonne() / 3][dernierCoup[nbCoupJoue].getLigne() / 3] = null;
vainqueur = null;
}
@Override
public int getNbColonnes() {
return 9;
}
@Override
public int getNbLignes() {
return 9;
}
@Override
public boolean partieTerminee() {
if (vainqueur != null) {
return true;
}
if (partieGagnee()) {
return true;
}
return isGrillePleine();
}
/**
* Regarde si le joueur vient de gagner sur une case en 3x3
*
* @param _grille soit la grille 9x9 soit la grille 3x3
* @param _x0 x min de la grille à analyser
* @param _y0 y min de la grille à analyser
* @param _coupX où le joueur a joué
* @param _coupY
* @return
*/
public boolean caseGagnante(Jeton[][] _grille, int _x0, int _y0, int _coupX, int _coupY) {
int[][] dir = {{1, 0}, {1, 1}, {0, 1}, {1, -1}};
int[][] dirOps = {{-1, 0}, {-1, -1}, {0, -1}, {-1, 1}};
int xMax = _x0 + 3, yMax = _y0 + 3;
int x, y;
int nbJetonAligne;
Joueur dernierJoueur = dernierCoup[nbCoupJoue-1].getJeton().getJoueur();
/* Regarde si le dernier coup est gagnant */
for (int d = 0; d < 4; d++) {
nbJetonAligne = 0;
x = _coupX;
y = _coupY;
while (x < xMax && x >= _x0 && y < yMax && y >= _y0 && _grille[x][y] != null && _grille[x][y].getJoueur() == dernierJoueur) {
nbJetonAligne++;
if (nbJetonAligne >= 3) {
return true;
}
x += dir[d][0];
y += dir[d][1];
}
//regarde dans la direction opposée
x = _coupX;
y = _coupY;
nbJetonAligne--;
while (x < xMax && x >= _x0 && y < yMax && y >= _y0 && _grille[x][y] != null && _grille[x][y].getJoueur() == dernierJoueur) {
nbJetonAligne++;
if (nbJetonAligne >= 3) {
return true;
}
x += dirOps[d][0];
y += dirOps[d][1];
}
}
return false;
}
@Override
public boolean partieGagnee() {
if (nbCoupJoue == 0) {
return false;
}
if ( grilleGagnee) { // si le dernier coup a gagne une grille on regarde si on gagne sur la grande grille
if (caseGagnante(grille3x3, 0, 0, dernierCoup[nbCoupJoue-1].getColonne() / 3, dernierCoup[nbCoupJoue-1].getLigne() / 3)) {
vainqueur = dernierCoup[nbCoupJoue-1].getJeton().getJoueur();
return true;
}
}
// Compte le nombre de cases remportées par chaque joueur
if (isGrillePleine()) {
int[] nbCase = new int[2];
Joueur[] joueur = new Joueur[2];
for (int c = 0; c < 3; c++) {
for (int l = 0; l < 3; l++) {
if (grille3x3[c][l] != null) {
nbCase[grille3x3[c][l].getJoueur().getIdJoueur()]++;
joueur[grille3x3[c][l].getJoueur().getIdJoueur()] = grille3x3[c][l].getJoueur();
}
}
}
if (nbCase[0] > nbCase[1]) {
vainqueur = joueur[0];
return true;
}
if (nbCase[1] > nbCase[0]) {
vainqueur = joueur[1];
return true;
}
vainqueur = null;
return true;
}
return false;
}
@Override
public boolean partieNulle() {
if (vainqueur != null) {
return false;
}
if (partieGagnee()) {
return false;
}
return isGrillePleine();
}
@Override
public ArrayList<Coup> getListeCoups(Joueur _joueur) {
ArrayList<Coup> listeCoups = new ArrayList<Coup>();
if (nbCoupJoue != 0 ) {
int x0 = (dernierCoup[nbCoupJoue-1].getColonne() % 3) * 3;
int y0 = (dernierCoup[nbCoupJoue-1].getLigne() % 3) * 3;
for (int c = x0; c < x0 + 3; c++) {
for (int l = y0; l < y0 + 3; l++) {
if ( grille3x3[(c/3)][(l/3)] != null ) continue; // on ne peut pas jouer dans une grille gagnée
if (grille9x9[c][l] == null) {
listeCoups.add(new CoupTicTacToe(c, l, new Jeton(_joueur)));
}
}
}
}
if (listeCoups.isEmpty()) {
for (int c = 0; c < this.getNbColonnes(); c++) {
for (int l = 0; l < this.getNbLignes(); l++) {
if ( grille3x3[(c/3)][(l/3)] != null ) continue; // on ne peut pas jouer dans une grille gagnée
if (grille9x9[c][l] == null) {
listeCoups.add(new CoupTicTacToe(c, l, new Jeton(_joueur)));
}
}
}
}
return listeCoups;
}
@Override
public boolean isValide(Coup _coup) {
CoupTicTacToe coup = (CoupTicTacToe) _coup;
if ( grille9x9[coup.getColonne()][coup.getLigne()] != null ) return false;
if ( nbCoupJoue == 0 ) return true;
int x0 = (dernierCoup[nbCoupJoue-1].getColonne() % 3 ) * 3;
int y0 = (dernierCoup[nbCoupJoue-1].getLigne() % 3 ) * 3;
boolean grillePleine = true;
for (int c = x0; c < x0+3 ; c++) {
for (int l = y0; l < y0+3 ; l++) {
if ( grille3x3[c/3][l/3] != null ) continue; // les grilles gagneés sont pleines
if ( grille9x9[c][l] == null ) {
grillePleine = false;
break;
}
}
}
if ( grillePleine ) {
return true;
}
return ( coup.getColonne() >= x0 && coup.getColonne() < x0+3 && coup.getLigne()>= y0 && coup.getLigne() < y0+3 );
}
@Override
public Coup stringToCoup(String _coup, Joueur _joueur) {
int colonne = Integer.valueOf(_coup.charAt(0) + "");
int ligne = Integer.valueOf(_coup.charAt(1) + "");
return new CoupTicTacToe(colonne, ligne, new Jeton(_joueur));
}
@Override
public void sauvegardePosition() {
for (int c = 0; c < this.getNbColonnes(); c++) {
for (int l = 0; l < this.getNbLignes(); l++) {
grille9x9Sav[0][c][l] = grille9x9[c][l];
}
}
for (int c = 0; c < 3; c++) {
for (int l = 0; l < 3; l++) {
grille3x3Sav[0][c][l] = grille3x3[c][l];
}
}
grilleGagneeSav[nbCoupJoue] = grilleGagnee;
nbCoupJoueSav = nbCoupJoue;
}
@Override
public void restaurePosition( ) {
for (int c = 0; c < this.getNbColonnes(); c++) {
for (int l = 0; l < this.getNbLignes(); l++) {
grille9x9[c][l] = grille9x9Sav[0][c][l];
}
}
for (int c = 0; c < 3; c++) {
for (int l = 0; l < 3; l++) {
grille3x3[c][l] = grille3x3Sav[0][c][l];
}
}
vainqueur = null;
grilleGagnee = grilleGagneeSav[nbCoupJoueSav];
nbCoupJoue = nbCoupJoueSav;
}
private boolean isGrillePleine() {
for (int c = 0; c < this.getNbColonnes(); c++) {
for (int l = 0; l < this.getNbLignes(); l++) {
if ( grille3x3[c/3][l/3] != null ) continue; // les grilles gagnées sont pleines
if (grille9x9[c][l] == null) {
return false;
}
}
}
return true;
}
@Override
public Joueur vainqueur() {
return vainqueur;
}
void trace() {
for (int l = 2; l >= 0; l--) {
for (int c = 0; c < 3; c++) {
System.out.print(grille3x3[c][l] + " ");
}
System.out.println("");
}
}
@Override
public Coup getDernierCoup() {
if ( nbCoupJoue < 1 ) return null;
return dernierCoup[nbCoupJoue-1];
}
}
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package tictactoecodingame;
public class Jeton extends Piece {
public Jeton( Joueur _joueur ) {
super( _joueur );
}
public String toString() {
if ((getJoueur().getIdJoueur()) == 1) {
return "X";
}
else {
return "O";
}
}
}
\ No newline at end of file
package tictactoecodingame;
public abstract class Joueur {
private String nom;
private static int nbJoueur = 0;
private int idJoueur;
public Joueur( String _nom ) {
idJoueur = nbJoueur;
nbJoueur++;
this.nom = _nom;
}
public void setNom ( String _nom ) {
this.nom = _nom;
}
public String getNom() {
return nom;
}
public int getIdJoueur() {
return idJoueur;
}
abstract public Coup joue( Plateau p_plateau );
public String toString() {
return nom;
}
}
package tictactoecodingame;
import java.io.InputStreamReader;
import java.io.IOException;
public class JoueurHumain extends Joueur {
public JoueurHumain( String _joueur ) {
super( _joueur );
}
public Coup joue(Plateau _plateau) {
InputStreamReader clavier = new InputStreamReader(System.in);
char buffer[] = new char[10];
String monCoup = null;
// Demande au joueur de saisir son Coup
System.out.println("Votre coup : ");
try {
int nbCar = clavier.read(buffer, 0, 5);
monCoup = new String(buffer, 0, nbCar-1);
return _plateau.stringToCoup(monCoup, this);
} catch (IOException e) {
return null;
}
}
}
package tictactoecodingame;
public class JoueurOrdi extends Joueur {
boolean ponder; // Si vrai l'ordinateur réfléchi pendant la reflexion de son adversaire
AlgoRecherche algoRecherche;
public JoueurOrdi(String _nom) {
super(_nom);
ponder = false;
}
public JoueurOrdi( String _nom , AlgoRecherche _algo , boolean _ponder ) {
super( _nom );
algoRecherche = _algo;
ponder = _ponder;
}
public JoueurOrdi( String _nom , AlgoRecherche _algo ) {
this( _nom , _algo , false );
}
public AlgoRecherche getAlgoRecherche() {
return algoRecherche;
}
public void setAlgoRecherche(AlgoRecherche _algoRecherche) {
algoRecherche = _algoRecherche;
}
public Coup joue(Plateau _plateau ) {
Coup coupOrdi;
coupOrdi = algoRecherche.meilleurCoup(_plateau , this );
return coupOrdi;
}
}
package tictactoecodingame;
import java.util.Random;
/**
* Cette classe est une implémentation générique de l'algorithme Monte Carlo Tree Search (MCTS).
*/
public class MonteCarlo extends AlgoRecherche {
Noeud racine; // Noeud racine de l'arbre
Joueur[] joueur; // Les 2 joueurs
int joueurActuel; // celui qui doit jouer
Plateau plateau;
int tempsReflexion; // en miliseconde
AlgoSelection algoSelection;
Random rnd = new Random();
/**
* Constructeur de la classe MonteCarlo.
*
* @param _j1 Le premier joueur.
* @param _j2 Le deuxième joueur.
* @param _algo L'algorithme de sélection à utiliser.
* @param _temps Le temps de réflexion en millisecondes.
*/
public MonteCarlo( Joueur _j1 , Joueur _j2 , AlgoSelection _algo , int _temps ) {
joueur = new Joueur[2];
joueur[0] = _j1;
joueur[1] = _j2;
algoSelection = _algo;
rnd = new Random();
tempsReflexion = _temps;
}
/**
* Cette méthode sélectionne un nœud de l'arbre pour une exploration plus approfondie.
*
* @param _noeud Le nœud à partir duquel la sélection commence.
* @return Le nœud sélectionné pour lequel aucune simulation n'a encore été réalisée.
*/
public Noeud selection(Noeud _noeud) {
// TODO : A coder
}
/**
* Cette méthode génère tous les nœuds enfants possibles (c'est-à-dire tous les coups possibles) pour le nœud donné.
*
* @param _noeud Le nœud à partir duquel l'expansion commence.
*/
public void expansion(Noeud _noeud) {
// TODO : A coder
}
/**
* Cette méthode simule une partie à partir du nœud donné jusqu'à ce qu'une condition de fin de partie soit atteinte (par exemple, un joueur a gagné ou la partie est nulle).
* La simulation est effectuée en choisissant des coups au hasard pour chaque joueur jusqu'à la fin de la partie.
*
* @param _noeud Le nœud à partir duquel la simulation commence.
* @return L'identifiant du joueur qui a gagné la simulation. Si la partie est nulle, retourne -1.
*/
public int simulation(Noeud _noeud) {
// TODO : A coder
}
/**
* Après une simulation, cette méthode met à jour les statistiques de tous les nœuds de l'arbre de recherche depuis le nœud de la simulation jusqu'à la racine.
* Les statistiques mises à jour comprennent le nombre de fois qu'un nœud a été joué et le nombre de victoires obtenues à partir de ce nœud.
*
* @param _noeud Le nœud à partir duquel la rétropropagation commence.
* @param _vainqueur L'identifiant du joueur qui a gagné la simulation. Si la partie est nulle, c'est -1.
*/
public void backPropagation(Noeud _noeud , int _vainqueur ) {
// TODO : A coder
}
@Override
/**
* Cette méthode est utilisée pour déterminer le meilleur coup à jouer en fonction des résultats des simulations effectuées par l'algorithme de recherche Monte Carlo Tree Search (MCTS).
*
* Elle parcourt tous les fils du nœud racine (chaque fils représente un coup possible) et calcule un score pour chaque fils.
* Ce score est le ratio entre le nombre de victoires et le nombre total de jeux pour ce fils.
*
* Le coup qui a le plus grand score est considéré comme le meilleur coup.
*
* @return Le meilleur coup à jouer.
*/
public Coup meilleurCoup(Plateau _plateau , Joueur _joueur ) {
// TODO : A coder
}
public void joueCoup( Coup _coup ) {
plateau.joueCoup( _coup );
joueurActuel = (joueurActuel + 1 )%2;
}
}
package tictactoecodingame;
import java.util.ArrayList;
/**
* Cette classe représente un nœud dans l'arbre de recherche de l'algorithme Monte Carlo Tree Search (MCTS).
* Chaque nœud représente une position possible dans le jeu de Tic Tac Toe.
*/
public class Noeud {
double nbVictoire; // Nombre de victoires obtenues à partir de cette position pour le joueur du nœud parent
int nbJoue; // Nombre de simulations effectuées à partir de cette position
int joueur; // Numéro du joueur qui doit jouer à partir de cette position
Noeud pere; // Nœud parent de ce nœud dans l'arbre de recherche
ArrayList<Coup> coups; // Liste des coups possibles à partir de cette position
ArrayList<Noeud> fils; // fils[n] est le nœud résultant du coup coups[n]
/**
* Constructeur pour un nœud avec un joueur spécifié et un nœud parent.
*
* @param _joueur Le numéro du joueur qui doit jouer à partir de cette position.
* @param _pere Le nœud parent de ce nœud dans l'arbre de recherche.
*/
public Noeud(int _joueur , Noeud _pere) {
nbVictoire = nbJoue = 0;
joueur = _joueur;
pere = _pere;
coups = new ArrayList<Coup>();
fils = new ArrayList<Noeud>();
}
/**
* Constructeur pour un nœud avec un joueur spécifié et sans nœud parent.
*
* @param _joueur Le numéro du joueur qui doit jouer à partir de cette position.
*/
public Noeud( int _joueur ) {
this(_joueur , null);
}
/**
* Cette méthode vérifie si ce nœud a des nœuds enfants dans l'arbre de recherche.
*
* @return Vrai si ce nœud a des nœuds enfants, faux sinon.
*/
public boolean aDesFils() {
return !fils.isEmpty();
}
}
\ No newline at end of file
package tictactoecodingame;
public abstract class Piece {
private Joueur joueur;
public Piece( Joueur _joueur ) {
this.joueur = _joueur;
}
public void setJoueur(Joueur _joueur) {
joueur = _joueur;
}
public Joueur getJoueur() {
return joueur;
}
abstract public String toString();
}
package tictactoecodingame;
import java.util.ArrayList;
public abstract class Plateau {
abstract public void init(); // Initialise le plateau pour le démarrage d'une nouvelle partie.
abstract public Piece getPiece(Case _case); // Retourne la pièce presente sur la case _case
abstract public void joueCoup(Coup _coup); // Joue le coup _coup sur le plateau
abstract public void annuleDernierCoup(); // Annule le dernier coup joué
abstract public int getNbColonnes(); // Retourne le nombre de colonnes du plateau
abstract public int getNbLignes(); // Retourne le nombre de lignes du plateau
abstract public boolean partieTerminee(); // Vrai si la partie est terminee ( soit un gagnant soit un match nulle )
abstract public boolean partieGagnee(); // Vrai si le dernier joueur a gagné la partie
abstract public boolean partieNulle(); // Vrai si la partie est nulle
abstract public Joueur vainqueur(); // Retourne le joueur qui a gagné la partie
abstract public ArrayList<Coup> getListeCoups(Joueur _joueur); // Retourne la liste des coups possibles.
abstract public boolean isValide(Coup _coup); // Retourne Vrai si le coup est valide.
abstract public Coup stringToCoup(String _coup, Joueur _joueur); // Convertion d'une chaine de caractères en un Coup
abstract public void sauvegardePosition(); // Sauvegarde la position courante
abstract public void restaurePosition(); // restaure la position sauvegarde
abstract public Coup getDernierCoup(); // Retourne le dernierCoup joue
public String toString() { // Donne une représentation du plateau sous forme d'une chaine de caractères
Piece piece;
String chainePlateau = new String();
for (int ligne = getNbLignes() - 1; ligne >= 0; ligne--) {
chainePlateau += ligne;
for (int colonne = 0; colonne < getNbColonnes(); colonne++) {
piece = getPiece(new Case(colonne, ligne));
if (piece == null) {
chainePlateau += "| ";
} else {
chainePlateau += "|" + piece + " ";
}
}
chainePlateau += "|\n ";
for (int colonne = 0; colonne < getNbColonnes(); colonne++) {
chainePlateau += "____";
}
chainePlateau += "\n";
}
for (int colonne = 0; colonne < getNbColonnes(); colonne++)
chainePlateau += " "+ colonne + " ";
return chainePlateau;
}
}
package tictactoecodingame;
import java.util.Scanner;
/*--------------------------------------------------------*/
/* Version jeu en local */
/*--------------------------------------------------------*/
public class Player {
public static void main(String args[]) {
JoueurHumain humain = new JoueurHumain("Humain");
JoueurOrdi joueurOrdi = new JoueurOrdi("Ordi");
// Remplacer ici l'algorithme aléatoire par votre algorithme MCTS
AlgoRechercheAleatoire alea = new AlgoRechercheAleatoire( ); // L'ordinateur joue au hasard
//MonteCarlo mcts = new MonteCarlo(humain, joueurOrdi, new AlgoUCB(1), 15);
joueurOrdi.setAlgoRecherche(alea);
//joueurOrdi.setAlgoRecherche(mcts);
GrilleTicTacToe3x3 grille = new GrilleTicTacToe3x3();
grille.init();
Arbitre a = new Arbitre(grille, joueurOrdi, humain);
a.startNewGame(true); // Demarre une partie en affichant la grille du jeu
// Pour lancer un tournooi de 100 parties sans afficher la grille du jeu
// a.startTournament(100 , false);
}
}
/*--------------------------------------------------------*/
/* Version Codingame */
/*--------------------------------------------------------*/
/*
import java.util.Scanner;
class Player {
public static void main(String args[]) {
Scanner in = new Scanner(System.in);
CoupTicTacToe coup;
JoueurHumain adversaire = new JoueurHumain("Adversaire");
JoueurOrdi joueurOrdi = new JoueurOrdi("Ordi");
//AlgoRechercheAleatoire alea = new AlgoRechercheAleatoire( ); // L'ordinateur joue au hasard
MonteCarlo mcts = new MonteCarlo(joueurOrdi, adversaire, new AlgoUCB(0.5), 90);
joueurOrdi.setAlgoRecherche(mcts);
//GrilleTicTacToe3x3 grille = new GrilleTicTacToe3x3();
GrilleTicTacToe9x9 grille = new GrilleTicTacToe9x9();
grille.init();
while (true) {
int opponentRow = in.nextInt();
int opponentCol = in.nextInt();
int validActionCount = in.nextInt();
for (int i = 0; i < validActionCount; i++) {
int row = in.nextInt();
int col = in.nextInt();
}
if ( opponentCol != -1 ) {
coup = new CoupTicTacToe(opponentCol, opponentRow, new Jeton(adversaire));
grille.joueCoup(coup);
}
coup = (CoupTicTacToe) joueurOrdi.joue(grille);
grille.joueCoup(coup);
System.out.println(coup.getLigne() + " " + coup.getColonne() );
System.out.flush();
}
}
}
*/
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment