Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • francesco-bariatti/pingouins
  • Samuel.Felton/pingouins
  • Lucas.Clement/pingouins
3 results
Show changes
Showing
with 653 additions and 0 deletions
File added
\documentclass[11pt]{beamer}
\usetheme{CambridgeUS}
\usecolortheme{beaver}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{graphicx}
\author[Bariatti - Gasté - Le - Lebouc]{Francesco BARIATTI \and Adrien GASTÉ \\ \and Mikael LE \and Romain LEBOUC}
\title[MCTS - Jeu du Pingouin]{Étude Pratique : Développement d’une Intelligence Artificielle à base de l’algorithme Monte Carlo Tree Search}
\logo{\includegraphics[width=0.2\linewidth]{images/insa}}
\institute[]{Encadrant : Pascal GARCIA}
\date[Année 2015/16]{Année scolaire 2015/16}
\AtBeginSection[]
{
\begin{frame}
\tableofcontents[currentsection]
\end{frame}
}
\setbeamertemplate{caption}{\raggedright\insertcaption\par}
\begin{document}
\begin{frame}
\titlepage
\end{frame}
\begin{frame}{Sommaire}
\setcounter{tocdepth}{1}
\tableofcontents
\setcounter{tocdepth}{2}
\end{frame}
\section{Introduction}
\subsection{Notre projet}
\begin{frame}{Notre projet}
\begin{itemize}
\item Développer une intelligence artificielle basée sur le Monte Carlo Tree Search (MCTS)
\item Transposer le Jeu du Pinguin dans un environnement virtuel
\end{itemize}
\begin{figure}
\includegraphics[scale=0.2]{images/boite_jeu}
\end{figure}
\end{frame}
\subsection{Le Jeu du Pingouin}
\begin{frame}{Principe du jeu}
\begin{itemize}
\item Jeu de plateau pour 2 à 4 joueurs.
\item 60 cases et 4 pingouins par joueur. \\
Une case peut avoir 1, 2 ou 3 poissons.
\item Le joueur avec le plus de poissons à la fin du jeu gagne
\item Le jeu se termine quand \textbf{aucun} joueur ne peut plus bouger
\end{itemize}
\begin{figure}
\includegraphics[scale=0.05]{images/tiles}
\caption{Les 3 types de cases}
\end{figure}
\end{frame}
\begin{frame}{Règles de déplacement}
\begin{block}{Règle de base}
\begin{itemize}
\item Un pingouin peut bouger dans les 6 directions de l'hexagone tant qu'il ne rencontre pas d'obstacles.
\item Le joueur gagne la case d'où le pingouin part.
\item Si un joueur ne peut plus bouger, il passe son tour.
\end{itemize}
\end{block}
Les obstacles sont:
\begin{itemize}
\item Un pingouin (allié comme adverse)
\item Un trou crée par l'absence de case
\end{itemize}
\end{frame}
\subsection{Le Monte Carlo Tree Search}
\begin{frame}{Le Monte Carlo Tree Search}
\begin{itemize}
\item Algorithme de recherche de décision utilisé dans jeux tels que le Go ou Ms. Pacman
\item Simule un grand nombre de parties pour construire un arbre
\item Choisit le meilleur chemin dans l'arbre construit
\end{itemize}
\begin{figure}
\includegraphics[width=0.4\linewidth]{images/go_board}
\end{figure}
\end{frame}
\begin{frame}
\begin{itemize}
\item À chaque tour l'IA simule des millions de boucles pour construire l'arbre
\item Chaque boucle consiste en 4 étapes
\end{itemize}
\begin{figure}
\includegraphics[width=\linewidth]{images/mcts}
\end{figure}
\end{frame}
\subsection{Objectifs}
\begin{frame}{Objectifs du projet}
\begin{itemize}
\item Comprendre le MCTS et son code en C++
\item Implementer les règles du jeu de façon efficace \\
$ \to $ bitboards
\item Créer une interface graphique
\end{itemize}
\begin{figure}
\includegraphics[width=0.4\linewidth]{images/reflection}
\end{figure}
\end{frame}
\section{Implémentation du MCTS pour le Jeu du Pingouin}
\subsection{Le plateau}
\begin{frame}{Le plateau}
\begin{itemize}
\item Position 0 en bas à droite, 59 en haut à gauche
\item Comptage ligne par ligne en montant
\end{itemize}
\begin{figure}
\includegraphics[scale=0.26]{images/board}
\end{figure}
\end{frame}
\begin{frame}{Gestion des déplacements}
\begin{itemize}
\item Déplacements calculés avec des formules simples sur la position
\item Bords reconnus grâce à l'opérateur modulo «\%»
\end{itemize}
\begin{figure}
\centering
\begin{minipage}{.7\textwidth}
\centering
\includegraphics[scale=0.20]{images/board_move}
\end{minipage}%
\begin{minipage}{.3\textwidth}
\centering
\includegraphics[scale=0.15]{images/moves}
\end{minipage}
\end{figure}
\end{frame}
\begin{frame}{Plateau en bitboard}
\begin{itemize}
\item 3 bitboards: un par type de case
\end{itemize}
\begin{figure}
\includegraphics[scale=0.3]{images/bitboard_creation} \\
\caption{Bitboard obtenu: $10010001 = 145$}
\end{figure}
\begin{itemize}
\item Opérations binaires pour avoir les obstacles
\end{itemize}
\end{frame}
\subsection{Les pingouins}
\begin{frame}{Les pingouins}
\begin{itemize}
\item 1 pingouin: 32 bits
\item Chaque « partie » interprétée différemment
\end{itemize}
\begin{figure}
\includegraphics[width=\linewidth]{images/penguin_bitboard}
\end{figure}
\begin{itemize}
\item Par exemple, pour avoir le nombre de coups: \texttt{(penguin \& 4032) >> 6 }
\end{itemize}
\end{frame}
\section{Interface graphique}
\subsection{Interaction IA/Interface}
\begin{frame}{Interaction avec l'IA}
\begin{itemize}
\item Chaque tour l'IA communique l'état de la partie\\
$\to$ format JSON par clarté
\item L'IA attend un numéro de coup parmi les coups possibles pour le joueur
\end{itemize}
\begin{figure}
\includegraphics[scale=0.3]{images/IA_comm}
\end{figure}
\begin{itemize}
\item Très difficile à interpréter pour un humain
\end{itemize}
\end{frame}
\begin{frame}{Interaction avec l'IA}
\begin{itemize}
\item L'interface graphique lance le programme et écoute sa sortie standard
\item Les actions du joueur sont envoyées à l'entrée standard
\end{itemize}
\begin{figure}
\includegraphics[scale=0.3]{images/IA_GUI}
\end{figure}
\end{frame}
\subsection{Architecture MVC}
\begin{frame}{Modèle - Vue - Contrôleur}
\begin{itemize}
\item Un modèle qui contient l'état du jeu \\
$\to$ des fonctions pour accéder facilement aux valeurs
\item Une vue pour afficher de façon claire l'état
\item Un contrôleur pour associer des actions aux clics du joueur
\end{itemize}
\begin{figure}
\includegraphics[scale=0.25]{images/board_game}
\end{figure}
\end{frame}
\section{Démonstration}
\begin{frame}{Let's play!}
\begin{figure}
\includegraphics[scale=0.6]{images/pingual}
\caption{Nutnut}
\end{figure}
\end{frame}
\section{Bilan}
\begin{frame}{Bilan}
\begin{itemize}
\item Objectifs atteints
\item Un joueur de bon niveau
\end{itemize}
Ouvertures possibles:
\begin{itemize}
\item Réflexion pendant le tour de l'humain
\item Apprentissage automatique d'heuristique (comme AlphaGo)
\item Implémenter un autre jeu
\end{itemize}
\end{frame}
\begin{frame}
\titlepage
\end{frame}
\end{document}
Doc/fr/Soutenance/images/IA_GUI.png

8.84 KiB

Doc/fr/Soutenance/images/IA_comm.png

13.3 KiB

Doc/fr/Soutenance/images/bitboard_creation.png

14.8 KiB

Doc/fr/Soutenance/images/board.png

34.6 KiB

Doc/fr/Soutenance/images/board_game.png

33.5 KiB

Doc/fr/Soutenance/images/board_move.png

45.1 KiB

Doc/fr/Soutenance/images/boite_jeu.jpg

165 KiB

Doc/fr/Soutenance/images/go_board.jpg

1.13 MiB

Doc/fr/Soutenance/images/insa.png

23 KiB

Doc/fr/Soutenance/images/mcts.png

91.8 KiB

Doc/fr/Soutenance/images/moves.png

11 KiB

Doc/fr/Soutenance/images/penguin_bitboard.png

15.3 KiB

Doc/fr/Soutenance/images/pingual.jpg

188 KiB

Doc/fr/Soutenance/images/reflection.jpg

2.25 MiB

Doc/fr/Soutenance/images/tiles.jpg

595 KiB

File added
File added
package controller;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.*;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.shape.Polygon;
import model.GameState;
import model.Move;
import model.Player;
import model.Tile;
import org.json.JSONException;
import org.json.JSONObject;
import sun.audio.AudioPlayer;
import sun.audio.AudioStream;
import view.TileView;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.ResourceBundle;
public class Controller implements Initializable
{
public static String iaProgramPath = "bin/penguin";
public static String introMusicFile = "resource/pingu_theme.wav";
public static String clickSoundFile = "resource/clic.wav";
private static final int max1Fish = 30, max2Fish = 20, max3Fish = 10;
private GameState gameState;
private Process gameProcess;
private PrintWriter gameInput;
private Tile[] board;
private TileView[] boardView;
private Player humanPlayer;
private int selectedTile;
private int penguinGenCounter; //Used when creating a random state
@FXML
private Polygon tile0, tile1, tile2, tile3, tile4, tile5, tile6, tile7, tile8, tile9, tile10, tile11, tile12, tile13, tile14, tile15, tile16, tile17, tile18, tile19, tile20, tile21, tile22, tile23, tile24, tile25, tile26, tile27, tile28, tile29, tile30, tile31, tile32, tile33, tile34, tile35, tile36, tile37, tile38, tile39, tile40, tile41, tile42, tile43, tile44, tile45, tile46, tile47, tile48, tile49, tile50, tile51, tile52, tile53, tile54, tile55, tile56, tile57, tile58, tile59;
@FXML
private Label nbFish0, nbFish1, nbFish2, nbFish3, nbFish4, nbFish5, nbFish6, nbFish7, nbFish8, nbFish9, nbFish10, nbFish11, nbFish12, nbFish13, nbFish14, nbFish15, nbFish16, nbFish17, nbFish18, nbFish19, nbFish20, nbFish21, nbFish22, nbFish23, nbFish24, nbFish25, nbFish26, nbFish27, nbFish28, nbFish29, nbFish30, nbFish31, nbFish32, nbFish33, nbFish34, nbFish35, nbFish36, nbFish37, nbFish38, nbFish39, nbFish40, nbFish41, nbFish42, nbFish43, nbFish44, nbFish45, nbFish46, nbFish47, nbFish48, nbFish49, nbFish50, nbFish51, nbFish52, nbFish53, nbFish54, nbFish55, nbFish56, nbFish57, nbFish58, nbFish59;
@FXML
private BorderPane mainPane;
@FXML
private Label scoreRedLabel, scoreBlueLabel, turnLabel, statusLabel;
@Override
public void initialize(URL location, ResourceBundle resources)
{
Polygon[] fxTiles = {tile0, tile1, tile2, tile3, tile4, tile5, tile6, tile7, tile8, tile9, tile10, tile11, tile12, tile13, tile14, tile15, tile16, tile17, tile18, tile19, tile20, tile21, tile22, tile23, tile24, tile25, tile26, tile27, tile28, tile29, tile30, tile31, tile32, tile33, tile34, tile35, tile36, tile37, tile38, tile39, tile40, tile41, tile42, tile43, tile44, tile45, tile46, tile47, tile48, tile49, tile50, tile51, tile52, tile53, tile54, tile55, tile56, tile57, tile58, tile59};
Label[] labels = {nbFish0, nbFish1, nbFish2, nbFish3, nbFish4, nbFish5, nbFish6, nbFish7, nbFish8, nbFish9, nbFish10, nbFish11, nbFish12, nbFish13, nbFish14, nbFish15, nbFish16, nbFish17, nbFish18, nbFish19, nbFish20, nbFish21, nbFish22, nbFish23, nbFish24, nbFish25, nbFish26, nbFish27, nbFish28, nbFish29, nbFish30, nbFish31, nbFish32, nbFish33, nbFish34, nbFish35, nbFish36, nbFish37, nbFish38, nbFish39, nbFish40, nbFish41, nbFish42, nbFish43, nbFish44, nbFish45, nbFish46, nbFish47, nbFish48, nbFish49, nbFish50, nbFish51, nbFish52, nbFish53, nbFish54, nbFish55, nbFish56, nbFish57, nbFish58, nbFish59};
this.board = new Tile[fxTiles.length];
this.boardView = new TileView[fxTiles.length];
for (int i = 0; i < fxTiles.length; i++)
{
board[i] = new Tile(i, Tile.PenguinPresence.NO_PENGUIN);
boardView[i] = new TileView(fxTiles[i], labels[i], board[i]);
}
this.gameState = new GameState();
try
{
this.gameProcess = new ProcessBuilder(iaProgramPath).start();
gameInput = new PrintWriter(new OutputStreamWriter(gameProcess.getOutputStream()), true);
Runtime.getRuntime().addShutdownHook(new Thread()
{
public void run()
{
gameProcess.destroy();
}
});
UpdateThread upT = new UpdateThread(gameProcess, this, this.gameState, statusLabel);
upT.setDaemon(true);
upT.start();
} catch (IOException e)
{
Alert alert = new Alert(Alert.AlertType.ERROR, "Can't run penguin program", ButtonType.OK);
alert.showAndWait();
e.printStackTrace();
System.exit(1);
}
//PLAY START SOUND
new Thread(() -> {
try
{
AudioPlayer.player.start(new AudioStream(getClass().getClassLoader().getResourceAsStream(introMusicFile)));
} catch (IOException e)
{
e.printStackTrace();
}
}).start();
//DO WE WANT TO LOAD A STATE?
TextInputDialog dialog = new TextInputDialog();
dialog.setHeaderText("Do you want to load a state?");
dialog.setContentText("Enter JSON (or leave blank for random)");
dialog.showAndWait();
String result = dialog.getResult();
if (result == null) //If the user clicked exit
System.exit(0);
try
{
new JSONObject(result); //Verify if it is valid json (if not, an exception is thrown)
//WE DO WANT TO LOAD A STATE
gameInput.println(result);
Platform.runLater(() -> startGame());
} catch (JSONException e)
{
//WE DON'T WANT TO LOAD A STATE
gameState.clearFish();
gameState.setScore(Player.Red, 0);
gameState.setScore(Player.Blue, 0);
//Generating random fish values
int[] fishCount = {0,0,0};
for (int i = 0; i < 60; i++)
{
List<Integer> possibleFish = new ArrayList<>();
if(fishCount[0] < max1Fish)
possibleFish.add(1);
if(fishCount[1] < max2Fish)
possibleFish.add(2);
if(fishCount[2] < max3Fish)
possibleFish.add(3);
int nbFish = possibleFish.get(new Random().nextInt(possibleFish.size()));
gameState.setFish(i, nbFish);
board[i].setNbFish(nbFish);
boardView[i].update();
fishCount[nbFish-1]++;
}
//Adding listener to add penguins
penguinGenCounter = 0;
for (TileView t : boardView)
{
PlacePenguinClickHandler phandl = new PlacePenguinClickHandler(t.getModelTile().getNumber());
t.getFxTile().setOnMouseClicked(phandl);
t.getFishLabel().setOnMouseClicked(phandl);
}
statusLabel.setText("Click to place RED penguins");
}
}
private void startGame()
{
ChoiceDialog<Player> playerChoice = new ChoiceDialog<>(Player.Red, Player.Red, Player.Blue);
playerChoice.setTitle("Penguin game!");
playerChoice.setHeaderText("Choose your color (red starts)");
playerChoice.showAndWait();
humanPlayer = playerChoice.getResult();
if (humanPlayer == null)
System.exit(0);
if (humanPlayer.equals(Player.Red))
gameInput.println("h");
else
gameInput.println("c");
gameState.setHumanPlayer(humanPlayer);
this.selectedTile = -1;
for (TileView t : boardView)
{
GameClickHandler gch = new GameClickHandler(t.getModelTile().getNumber());
t.getFxTile().setOnMouseClicked(gch);
t.getFishLabel().setOnMouseClicked(gch);
}
}
/**
* Updates model and view from the state
*/
public void updateModelAndView()
{
for (int i = 0; i < board.length; i++)
{
board[i].setNbFish(gameState.getNbFish(i));
board[i].setPenguinPresence(Tile.PenguinPresence.NO_PENGUIN);
}
for (int i = 0; i < 4; i++)
{
board[gameState.getPenguinPos(Player.Red, i)].setPenguinPresence(Tile.PenguinPresence.RED_PENGUIN);
board[gameState.getPenguinPos(Player.Blue, i)].setPenguinPresence(Tile.PenguinPresence.BLUE_PENGUIN);
}
for(TileView t : boardView) //No need to deselect or de-highlight tiles because it is done in the click listener
t.update();
scoreRedLabel.setText(Integer.toString(gameState.getScore(Player.Red)));
scoreBlueLabel.setText(Integer.toString(gameState.getScore(Player.Blue)));
turnLabel.setText(gameState.getCurrent_player()+ "'s turn");
}
//
//
public void gameEnd()
{
Player human = gameState.getHumanPlayer();
Player computer = human.equals(Player.Red) ? Player.Blue : Player.Red;
String message = "";
if (gameState.getScore(human) > gameState.getScore(computer))
message = "You won!!! I can't believe it!";
else if (gameState.getScore(human) == gameState.getScore(computer))
message = "That's a draw. Not bad!";
else
message = "You just lost the penguin game.";
statusLabel.setText("End of game");
Alert alert = new Alert(Alert.AlertType.INFORMATION, message, ButtonType.FINISH);
alert.showAndWait();
}
private class PlacePenguinClickHandler implements EventHandler<MouseEvent>
{
public int tileNumber;
public PlacePenguinClickHandler(int tileNumber)
{
this.tileNumber = tileNumber;
}
@Override
public void handle(MouseEvent event)
{
if (!board[tileNumber].getPenguinPresence().equals(Tile.PenguinPresence.NO_PENGUIN))
return;
if (penguinGenCounter < 4)
{
gameState.setPenguin(Player.Red, penguinGenCounter, tileNumber);
board[tileNumber].setPenguinPresence(Tile.PenguinPresence.RED_PENGUIN);
boardView[tileNumber].update();
} else if (penguinGenCounter < 8)
{
gameState.setPenguin(Player.Blue, penguinGenCounter - 4, tileNumber);
board[tileNumber].setPenguinPresence(Tile.PenguinPresence.BLUE_PENGUIN);
boardView[tileNumber].update();
}
penguinGenCounter++;
if(penguinGenCounter < 4)
statusLabel.setText("Red penguins left: "+Integer.toString(4-penguinGenCounter));
else if (penguinGenCounter == 4)
statusLabel.setText("Click to place BLUE penguins");
else if (penguinGenCounter < 8)
statusLabel.setText("Blue penguins left: "+Integer.toString(8-penguinGenCounter));
else
statusLabel.setText("");
//If we placed all penguins
if (penguinGenCounter >= 8)
{
gameInput.println(gameState.toGameInputJSON());
Platform.runLater(() -> startGame());
}
}
}
/**
* Event handler that will listen for a click on a tile during the game and move/select the penguin.
* It is associated with a TileView
*/
private class GameClickHandler implements EventHandler<MouseEvent>
{
private int tileNumber;
public GameClickHandler(int tileNumber)
{
this.tileNumber = tileNumber;
}
private void unHighlightTiles(List<Integer> tiles)
{
for (int tile : tiles)
{
boardView[tile].setHighlighted(false);
boardView[tile].setHighlightMove(null);
boardView[tile].update();
}
}
private void playClickSound()
{
try
{
AudioPlayer.player.start(new AudioStream(getClass().getClassLoader().getResourceAsStream(clickSoundFile)));
} catch (IOException e)
{
e.printStackTrace();
}
}
@Override
public void handle(MouseEvent event)
{
if (!gameState.getCurrent_player().equals(humanPlayer))
return;
if (selectedTile == tileNumber) //If we clicked again on the selected tile
{
playClickSound();
//UnSelect and un-highlight previously selected and highlighted tiles
boardView[selectedTile].setSelected(false);
boardView[selectedTile].update();
unHighlightTiles(gameState.getReachableTiles(humanPlayer, gameState.getPenguinOnTile(humanPlayer, selectedTile)));
selectedTile = -1;
return;
}
//We clicked on a tile that wasn't previously selected
//Selecting the tile
int penguinNb = gameState.getPenguinOnTile(humanPlayer, tileNumber);
if (penguinNb != -1) //There is a penguin on this tile: we want to select it
{
playClickSound();
//Unselect previously selected tiles
if (selectedTile != -1)
{
boardView[selectedTile].setSelected(false);
boardView[selectedTile].update();
unHighlightTiles(gameState.getReachableTiles(humanPlayer, gameState.getPenguinOnTile(humanPlayer, selectedTile)));
}
//Selecting new tile
boardView[tileNumber].setSelected(true);
boardView[tileNumber].update();
selectedTile = tileNumber;
for (int i = 1; i <= gameState.getNbMoves(humanPlayer, penguinNb, GameState.Direction.A); i++)
{
boardView[selectedTile + i * 7].setHighlighted(true);
boardView[selectedTile + i * 7].setHighlightMove(new Move(GameState.Direction.A, i));
boardView[selectedTile + i * 7].update();
}
for (int i = 1; i <= gameState.getNbMoves(humanPlayer, penguinNb, GameState.Direction.B); i++)
{
boardView[selectedTile - i].setHighlighted(true);
boardView[selectedTile - i].setHighlightMove(new Move(GameState.Direction.B, i));
boardView[selectedTile - i].update();
}
for (int i = 1; i <= gameState.getNbMoves(humanPlayer, penguinNb, GameState.Direction.C); i++)
{
boardView[selectedTile - i * 8].setHighlighted(true);
boardView[selectedTile - i * 8].setHighlightMove(new Move(GameState.Direction.C, i));
boardView[selectedTile - i * 8].update();
}
for (int i = 1; i <= gameState.getNbMoves(humanPlayer, penguinNb, GameState.Direction.D); i++)
{
boardView[selectedTile - i * 7].setHighlighted(true);
boardView[selectedTile - i * 7].setHighlightMove(new Move(GameState.Direction.D, i));
boardView[selectedTile - i * 7].update();
}
for (int i = 1; i <= gameState.getNbMoves(humanPlayer, penguinNb, GameState.Direction.E); i++)
{
boardView[selectedTile + i].setHighlighted(true);
boardView[selectedTile + i].setHighlightMove(new Move(GameState.Direction.E, i));
boardView[selectedTile + i].update();
}
for (int i = 1; i <= gameState.getNbMoves(humanPlayer, penguinNb, GameState.Direction.F); i++)
{
boardView[selectedTile + i * 8].setHighlighted(true);
boardView[selectedTile + i * 8].setHighlightMove(new Move(GameState.Direction.F, i));
boardView[selectedTile + i * 8].update();
}
return;
}
else if (boardView[tileNumber].isHighlighted()) //There is no penguin but this is a possible move for the penguin on the selected tile
{
playClickSound();
Move move = boardView[tileNumber].getHighlightMove();
int moveNb = gameState.getPlayerMoveNumber(humanPlayer, gameState.getPenguinOnTile(humanPlayer, selectedTile), move.getDirection(), move.getSteps());
//Before playing the move: deselect and de-highlight tiles
boardView[selectedTile].setSelected(false);
boardView[selectedTile].update();
unHighlightTiles(gameState.getReachableTiles(humanPlayer, gameState.getPenguinOnTile(humanPlayer, selectedTile)));
selectedTile = -1;
//Play the move
gameInput.println(moveNb);
}
}
}
}