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
Commits on Source (35)
Showing
with 633 additions and 206 deletions
......@@ -18,3 +18,5 @@ GUI/GUI.iml
*.dvi
*.bbl
*.blg
*.nav
*.snm
......@@ -28,17 +28,13 @@ namespace game
{
if(json_state["penguins"].count("red"))
{
state.p1_red = json_state["penguins"]["red"][0];
state.p2_red = json_state["penguins"]["red"][1];
state.p3_red = json_state["penguins"]["red"][2];
state.p4_red = json_state["penguins"]["red"][3];
for(int i = 0; i < 4; i++)
state.peng_red[i] = json_state["penguins"]["red"][i];
}
if(json_state["penguins"].count("blue"))
{
state.p1_blue = json_state["penguins"]["blue"][0];
state.p2_blue = json_state["penguins"]["blue"][1];
state.p3_blue = json_state["penguins"]["blue"][2];
state.p4_blue = json_state["penguins"]["blue"][3];
for(int i = 0; i < 4; i++)
state.peng_blue[i] = json_state["penguins"]["blue"][i];
}
}
if(json_state.count("score"))
......@@ -46,29 +42,16 @@ namespace game
if(json_state["score"].count("red")) { state.score_red = json_state["score"]["red"]; }
if(json_state["score"].count("blue")) { state.score_blue = json_state["score"]["blue"]; }
}
//Update moves on all penguins
uint64_t obstacles = (~(state.one_fish | state.two_fish | state.three_fish));
obstacles |= ((uint64_t) 1) << (state.p1_red & 63);
obstacles |= ((uint64_t) 1) << (state.p2_red & 63);
obstacles |= ((uint64_t) 1) << (state.p3_red & 63);
obstacles |= ((uint64_t) 1) << (state.p4_red & 63);
obstacles |= ((uint64_t) 1) << (state.p1_blue & 63);
obstacles |= ((uint64_t) 1) << (state.p2_blue & 63);
obstacles |= ((uint64_t) 1) << (state.p3_blue & 63);
obstacles |= ((uint64_t) 1) << (state.p4_blue & 63);
// We compute the moves for all penguin
uint64_t obstacles = create_obstacles_bitboard();
state.nb_moves_red = 0;
state.nb_moves_blue = 0;
state.nb_moves_red += update_moves(&state.p1_red, obstacles);
state.nb_moves_red += update_moves(&state.p2_red, obstacles);
state.nb_moves_red += update_moves(&state.p3_red, obstacles);
state.nb_moves_red += update_moves(&state.p4_red, obstacles);
state.nb_moves_blue += update_moves(&state.p1_blue, obstacles);
state.nb_moves_blue += update_moves(&state.p2_blue, obstacles);
state.nb_moves_blue += update_moves(&state.p3_blue, obstacles);
state.nb_moves_blue += update_moves(&state.p4_blue, obstacles);
for(int i = 0; i < 4; i++)
{
state.nb_moves_red += update_penguin_moves(&state.peng_red[i], obstacles);
state.nb_moves_blue += update_penguin_moves(&state.peng_blue[i], obstacles);
}
if (state.nb_moves_red == 0)
{
state.canPlay_red = false;
......@@ -139,140 +122,81 @@ namespace game
return state.current_player_red ? state.nb_moves_red : state.nb_moves_blue;
}
/* The penguin that will move if we want to play the #move_number move in the list of possible moves.
* What this function does:
* Find the penguin that will move.
* Change its total number of moves so that it is relative to that penguin's possible moves and not relative to ALL the penguins' moves.
* Return a pointer to that penguin
* */
uint32_t* penguin::penguin_that_moves(uint16_t move_number)
{
if(state.current_player_red)
{
if(((state.p1_red >> 6) & 63) > move_number)
{
uint32_t* p = &state.p1_red;
(*p) = (*p) & 0xFFFFF03F; //Reset move number for the penguin to 0
(*p) = (*p) | ((uint32_t) move_number) << 6;
return p;
}
move_number -= (state.p1_red >> 6) & 63;
if(((state.p2_red >> 6) & 63) > move_number)
{
uint32_t* p = &state.p2_red;
(*p) = (*p) & 0xFFFFF03F; //Reset move number for the penguin to 0
(*p) = (*p) | ((uint32_t) move_number) << 6;
return p;
}
move_number -= (state.p2_red >> 6) & 63;
if(((state.p3_red >> 6) & 63) > move_number)
{
uint32_t* p = &state.p3_red;
(*p) = (*p) & 0xFFFFF03F; //Reset move number for the penguin to 0
(*p) = (*p) | ((uint32_t) move_number) << 6;
return p;
}
move_number -= (state.p3_red >> 6) & 63;
uint32_t* p = &state.p4_red;
(*p) = (*p) & 0xFFFFF03F; //Reset move number for the penguin to 0
(*p) = (*p) | ((uint32_t) move_number) << 6;
return p;
}
else
{
if(((state.p1_blue >> 6) & 63) > move_number)
{
uint32_t* p = &state.p1_blue;
(*p) = (*p) & 0xFFFFF03F; //Reset move number for the penguin to 0
(*p) = (*p) | ((uint32_t) move_number) << 6;
return p;
}
move_number -= (state.p1_blue >> 6) & 63;
if(((state.p2_blue >> 6) & 63) > move_number)
{
uint32_t* p = &state.p2_blue;
(*p) = (*p) & 0xFFFFF03F; //Reset move number for the penguin to 0
(*p) = (*p) | ((uint32_t) move_number) << 6;
return p;
}
move_number -= (state.p2_blue >> 6) & 63;
if(((state.p3_blue >> 6) & 63) > move_number)
{
uint32_t* p = &state.p3_blue;
(*p) = (*p) & 0xFFFFF03F; //Reset move number for the penguin to 0
(*p) = (*p) | ((uint32_t) move_number) << 6;
return p;
}
move_number -= (state.p3_blue >> 6) & 63;
uint32_t* p = &state.p4_blue;
(*p) = (*p) & 0xFFFFF03F; //Reset move number for the penguin to 0
(*p) = (*p) | ((uint32_t) move_number) << 6;
return p;
}
}
void penguin::move_penguin(uint32_t* p)
/*
Moves the penguin p (modify its position value), making it do its rel_move move.
At the end of the function the penguin will be composed of just its new position (every other bit is at 0)
*/
void penguin::move_penguin(uint32_t* p, uint16_t rel_move)
{
uint8_t move_number = ((*p) >> 6) & 63; //Move number for the current penguin
uint32_t penguin_copy = (*p) >> 12;
//Direction A
if((penguin_copy & 7) > move_number)
if(PENGUIN_MOVES_A(*p) > rel_move) //If the penguin total moves in this direction are greater than the move we want to do for it (not equal because moves start at 0)
{
//Move direction A
(*p) = (7 * (move_number +1)) + ((*p) & 63);
(*p) = (7 * (rel_move +1)) + ((*p) & 63);
return;
}
move_number -= penguin_copy & 7;
penguin_copy = penguin_copy >> 3;
if((penguin_copy & 7) > move_number)
rel_move -= PENGUIN_MOVES_A(*p);
if(PENGUIN_MOVES_B(*p) > rel_move)
{
//Move direction B
(*p) = (-1 * (move_number +1)) + ((*p) & 63);
(*p) = (-1 * (rel_move +1)) + ((*p) & 63);
return;
}
move_number -= penguin_copy & 7;
penguin_copy = penguin_copy >> 3;
if((penguin_copy & 7) > move_number)
rel_move -= PENGUIN_MOVES_B(*p);
if(PENGUIN_MOVES_C(*p) > rel_move)
{
//Move direction C
(*p) = (-8 * (move_number +1)) + ((*p) & 63);
(*p) = (-8 * (rel_move +1)) + ((*p) & 63);
return;
}
move_number -= penguin_copy & 7;
penguin_copy = penguin_copy >> 3;
if((penguin_copy & 7) > move_number)
rel_move -= PENGUIN_MOVES_C(*p);
if(PENGUIN_MOVES_D(*p) > rel_move)
{
//Move direction D
(*p) = (-7 * (move_number +1)) + ((*p) & 63);
(*p) = (-7 * (rel_move +1)) + ((*p) & 63);
return;
}
move_number -= penguin_copy & 7;
penguin_copy = penguin_copy >> 3;
if((penguin_copy & 7) > move_number)
rel_move -= PENGUIN_MOVES_D(*p);
if(PENGUIN_MOVES_E(*p) > rel_move)
{
//Move direction E
(*p) = (1 * (move_number +1)) + ((*p) & 63);
(*p) = (1 * (rel_move +1)) + ((*p) & 63);
return;
}
move_number -= penguin_copy & 7;
penguin_copy = penguin_copy >> 3;
rel_move -= PENGUIN_MOVES_E(*p);
//Move direction F
(*p) = (8 * (move_number +1)) + ((*p) & 63);
(*p) = (8 * (rel_move +1)) + ((*p) & 63);
}
/* Create bitboard of obstacles: 1 if there is an obstacle, 0 if the penguin can
move freely on the tile
*/
uint64_t penguin::create_obstacles_bitboard()
{
uint64_t obstacles = (~(state.one_fish | state.two_fish | state.three_fish));
for(int i = 0; i < 4; i++)
{
obstacles |= ((uint64_t) 1) << PENGUIN_POS(state.peng_red[i]);
obstacles |= ((uint64_t) 1) << PENGUIN_POS(state.peng_blue[i]);
}
return obstacles;
}
int penguin::update_moves(uint32_t* p, uint64_t obstacles)
/* Updates the moves of a signle penguin.
This computes the moves in every direction according to the penguin position and the obstacles.
Parameters:
- p: a penguin that will be updated. Only his position is used and the rest is computed
- obstacles: a bitboard of obstacles: 1 means an obstacle in that tile
Returns:
Total moves of this penguin.
*/
int penguin::update_penguin_moves(uint32_t* p, uint64_t obstacles)
{
#define IsFree(i) (((obstacles >> (i)) & 1) == 0)
int pos = (*p) & 63;
#define IsFree(i) (((obstacles >> (i)) & 1) == 0)
int pos = PENGUIN_POS(*p);
(*p) = pos; //Reset the penguin to all zeros except the position
int i = pos;
uint32_t nbmoves = 0;
uint32_t nbmoves = 0; //Nb of moves in one direction
uint32_t total_moves = 0;
//Direction A
......@@ -294,7 +218,7 @@ namespace game
//Direction C
nbmoves = 0;
i = pos;
while((i-8 > 0) && (i%15 != 0) && IsFree(i-8))
while((i-8 >= 0) && (i%15 != 0) && IsFree(i-8))
{
i -= 8;
nbmoves++; total_moves++;
......@@ -335,68 +259,74 @@ namespace game
//Play the mth move in the possible moves list.
void penguin::play(uint16_t m)
{
//CAN WE PLAY?
// We check if we can effectively play: if yes, the move is parsed and player, otherwise we do nothing (the move can be whatever, we won't look at it, so the player actually skip the turn)
if ((state.current_player_red == true && state.canPlay_red) || ((state.current_player_red == false) && state.canPlay_blue))
{
//Find which penguin will move
uint32_t* p = penguin_that_moves(m);
uint8_t position = (*p) & 63;
//WHICH PENGUIN WILL MOVE?
uint32_t* peng; // The penguin that will move
uint16_t rel_move = m; // Move number relative to this penguin
int i = 0;
if(state.current_player_red)
{
/* We search for the first penguin that can make the move. If a penguin can't, we will decrese the move number
by the number of moves that he can do */
for(i = 0; (i < 3) && (PENGUIN_TOT_MOVES(state.peng_red[i]) <= rel_move); i ++) //While we didn't find the penguin
rel_move -= PENGUIN_TOT_MOVES(state.peng_red[i]);
// Now i is the number of the penguin that will move and rel_move is the move relative to this penguin (because we decreased it)
peng = &state.peng_red[i];
}
else //If blue
{
for(i = 0; (i < 3) && (PENGUIN_TOT_MOVES(state.peng_blue[i]) <= rel_move); i ++) //While we didn't find the penguin
rel_move -= PENGUIN_TOT_MOVES(state.peng_blue[i]);
peng = &state.peng_blue[i];
}
//Find the value of the tile the penguin is on and update score
if ((state.one_fish >> position) & 1)
// ADD PENGUIN TILE TO THE SCORE
if((state.one_fish >> PENGUIN_POS(*peng)) & 1) //If there is a one fish on this position
{
if(current_player() == RED)
if(state.current_player_red)
state.score_red += 1;
else
state.score_blue += 1;
//We replace this tile with an empty one (0 in the bitboard)
state.one_fish = state.one_fish & ~(((uint64_t) 1) << position);
state.one_fish = state.one_fish & ~(((uint64_t) 1) << PENGUIN_POS(*peng));
}
else if ((state.two_fish >> position) & 1)
else if((state.two_fish >> PENGUIN_POS(*peng)) & 1)
{
if(current_player() == RED)
if(state.current_player_red)
state.score_red += 2;
else
state.score_blue += 2;
//We replace this tile with an empty one (0 in the bitboard)
state.two_fish = state.two_fish & ~(((uint64_t) 1) << position);
state.two_fish = state.two_fish & ~(((uint64_t) 1) << PENGUIN_POS(*peng));
}
else
{
if(current_player() == RED)
if(state.current_player_red)
state.score_red += 3;
else
state.score_blue += 3;
//We replace this tile with an empty one (0 in the bitboard)
state.three_fish = state.three_fish & ~(((uint64_t) 1) << position);
state.three_fish = state.three_fish & ~(((uint64_t) 1) << PENGUIN_POS(*peng));
}
//Move the current penguin
move_penguin(p);
// MOVE THE PENGUIN
move_penguin(peng, rel_move);
}
//Update moves on all penguins of the next player
uint64_t obstacles = (~(state.one_fish | state.two_fish | state.three_fish));
obstacles |= ((uint64_t) 1) << (state.p1_red & 63);
obstacles |= ((uint64_t) 1) << (state.p2_red & 63);
obstacles |= ((uint64_t) 1) << (state.p3_red & 63);
obstacles |= ((uint64_t) 1) << (state.p4_red & 63);
obstacles |= ((uint64_t) 1) << (state.p1_blue & 63);
obstacles |= ((uint64_t) 1) << (state.p2_blue & 63);
obstacles |= ((uint64_t) 1) << (state.p3_blue & 63);
obstacles |= ((uint64_t) 1) << (state.p4_blue & 63);
// END CAN_WE PLAY. We will now compute the moves for every penguin and for the player.
uint64_t obstacles = create_obstacles_bitboard();
state.nb_moves_red = 0;
state.nb_moves_blue = 0;
if (state.current_player_red) //Red just played
{
if(state.canPlay_blue)
if(state.canPlay_blue) //If blue couldn't play last turn there is no way he could play this new turn
{
state.nb_moves_blue += update_moves(&state.p1_blue, obstacles);
state.nb_moves_blue += update_moves(&state.p2_blue, obstacles);
state.nb_moves_blue += update_moves(&state.p3_blue, obstacles);
state.nb_moves_blue += update_moves(&state.p4_blue, obstacles);
for(int i = 0; i < 4; i++)
state.nb_moves_blue += update_penguin_moves(&state.peng_blue[i], obstacles);
if (state.nb_moves_blue == 0)
{
state.canPlay_blue = false;
......@@ -405,19 +335,16 @@ namespace game
}
else
{
state.nb_moves_blue = 1;
state.nb_moves_blue = 1; //We create an artificial move so that the mcts works
}
state.current_player_red = false;
}
else //Blue just played
{
if(state.canPlay_red)
{
state.nb_moves_red += update_moves(&state.p1_red, obstacles);
state.nb_moves_red += update_moves(&state.p2_red, obstacles);
state.nb_moves_red += update_moves(&state.p3_red, obstacles);
state.nb_moves_red += update_moves(&state.p4_red, obstacles);
for(int i = 0; i < 4; i++)
state.nb_moves_red += update_penguin_moves(&state.peng_red[i], obstacles);
if (state.nb_moves_red == 0)
{
state.canPlay_red = false;
......@@ -428,7 +355,6 @@ namespace game
{
state.nb_moves_red = 1;
}
state.current_player_red = true;
}
}
......@@ -460,14 +386,11 @@ namespace game
json_state["bitboards"]["twofish"] = state.two_fish;
json_state["bitboards"]["threefish"] = state.three_fish;
json_state["penguins"]["red"][0] = state.p1_red;
json_state["penguins"]["red"][1] = state.p2_red;
json_state["penguins"]["red"][2] = state.p3_red;
json_state["penguins"]["red"][3] = state.p4_red;
json_state["penguins"]["blue"][0] = state.p1_blue;
json_state["penguins"]["blue"][1] = state.p2_blue;
json_state["penguins"]["blue"][2] = state.p3_blue;
json_state["penguins"]["blue"][3] = state.p4_blue;
for(int i = 0; i < 4; i++)
{
json_state["penguins"]["red"][i] = state.peng_red[i];
json_state["penguins"]["blue"][i] = state.peng_blue[i];
}
json_state["score"]["red"] = state.score_red;
json_state["score"]["blue"] = state.score_blue;
......@@ -476,7 +399,7 @@ namespace game
json_state["possible_moves"]["blue"] = state.nb_moves_blue;
json_state["current_player"] = state.current_player_red ? "Red" : "Blue";
json_state["can_play"]["red"] = state.canPlay_red;
json_state["can_play"]["blue"] = state.canPlay_blue;
......
......@@ -4,7 +4,7 @@
#include "game.hpp"
#include "json.hpp"
using json = nlohmann::json;
#include <random>
#include <random>
#include <array>
#include <iostream>
#include <memory>
......@@ -13,32 +13,36 @@ namespace game
{
struct penguin_state
{
uint64_t one_fish = 1152921504606846975; //Position of one-fish tiles (bitboard)
uint64_t one_fish = 1152921504606846975; //Position of one-fish tiles (bitboard)
uint64_t two_fish = 0; //Position of two-fish tiles (bitboard)
uint64_t three_fish = 0; //Position of three-fish tiles (bitboard)
//Penguins
uint32_t p1_red = 0;
uint32_t p2_red = 1;
uint32_t p3_red = 6;
uint32_t p4_red = 7;
uint32_t p1_blue = 59;
uint32_t p2_blue = 58;
uint32_t p3_blue = 53;
uint32_t p4_blue = 54;
uint32_t peng_red[4] = {0, 1, 6, 7};
uint32_t peng_blue[4] = {59, 58, 53, 54};
int score_red = 0;
int score_blue = 0;
bool current_player_red = true; //True if red must play now. Red always starts
int nb_moves_red = 0; //Number of moves the red player can play
int nb_moves_blue = 0;
bool canPlay_red = true;
bool canPlay_blue = true;
};
// Useful macros to get values out of penguins
#define PENGUIN_POS(penguin) ((penguin) & 63)
#define PENGUIN_TOT_MOVES(penguin) (((penguin) >> 6) & 63)
#define PENGUIN_MOVES_A(penguin) (((penguin) >> 12) & 7)
#define PENGUIN_MOVES_B(penguin) (((penguin) >> 15) & 7)
#define PENGUIN_MOVES_C(penguin) (((penguin) >> 18) & 7)
#define PENGUIN_MOVES_D(penguin) (((penguin) >> 21) & 7)
#define PENGUIN_MOVES_E(penguin) (((penguin) >> 24) & 7)
#define PENGUIN_MOVES_F(penguin) (((penguin) >> 27) & 7)
class penguin : public game<penguin_state>
{
public:
......@@ -65,16 +69,16 @@ namespace game
std::shared_ptr<game<penguin_state>> do_copy() const;
std::uint64_t hash(std::uint16_t m) const;
std::uint64_t hash() const;
private:
penguin_state state;
uint32_t* penguin_that_moves(uint16_t move_number);
void move_penguin(uint32_t* p);
int update_moves(uint32_t* p, uint64_t obstacles);
void move_penguin(uint32_t* p, uint16_t rel_move);
uint64_t create_obstacles_bitboard();
int update_penguin_moves(uint32_t* p, uint64_t obstacles);
const uint8_t RED = 0;
const uint8_t BLUE = 1;
};
std::ostream& operator<<(std::ostream& os, const penguin& pen);
}
......
/**
* Artificial Intelligence for the Penguin Game project.
* https://gitlab.insa-rennes.fr/francesco-bariatti/pingouins
* This AI, with the exception of game/penguin.hpp and game/penguin.cpp has been written by Pascal Garcia
* Copyright (C) 2016 Pascal Garcia
* It is licensed under the MIT license: you can find a copy in the file LICENSE.txt shipped with the whole project.
* ------------------------------------------------------------------------------------------------------------------------
* game/penguin.hpp and game/penguin.cpp have been written by Francesco Bariatti, Adrien Gasté, Mikael Le, Romain Lebouc.
* Copyright (C) 2016 Francesco Bariatti < francesco.bariatti@insa-rennes.fr >, Adrien Gasté < adrien.gaste@insa-rennes.fr >, Mikael Le < mikael.le@insa-rennes.fr >, Romain Lebouc < romain.lebouc@insa-rennes.fr >
* They are also licensed under the MIT license: you can find a copy in the file LICENSE.txt shipped with the whole project.
* -------------------------------------------------------------------------------------------------------------------------------------
* This program uses the jsoncpp library.
* You can find a copy of the library at https://github.com/open-source-parsers/jsoncpp
* The library is licensed under MIT license.
* Copyright (c) 2007-2010 Baptiste Lepilleur
*/
#include "penguin.hpp"
#include "test_two_players_game.hpp"
#include "test_fast_log.hpp"
......
File added
\documentclass[a4paper]{article}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PACKAGES
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{graphicx}
\usepackage[hidelinks]{hyperref}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\title{\textbf{Jeu du Pingouin: documentation technique}}
\author{Francesco Bariatti \and Adrien Gasté \and Mikaël Le \and Romain Lebouc \\ Encadrant: Pascal Garcia}
\date{2015-2016}
\begin{document}
\pagenumbering{gobble}
\maketitle
\tableofcontents
\newpage
\pagenumbering{arabic}
\section{Introduction}
Ce document est la documentation technique du projet d'études pratiques ``Jeu du Pingouin'' réalisé dans le cadre de la 3\ieme année de formation à l'\textsc{insa} de Rennes.
Le projet est composé de deux parties: une intelligence artificielle codée en C++ et une interface graphique codée en JavaFX. Dans ce document, les abréviations IA ou MCTS (pour Monte Carlo Tree Search, le nom de l'algorithme de recherche utilisé) vont être souvent utilisées pour évoquer l'intelligence artificielle.
Dans la section dédiée à l'IA, nous allons voir comment implémenter un jeu pour qu'il puisse utiliser le MCTS pour jouer contre un humain. Ensuite, nous présenterons comment le Jeu du Pingouin est implémenté.
\subsection*{Auteurs et license}
La partie MCTS a été codée par Pascal \textsc{Garcia}, notre encadrant. Notre groupe a codé la partie Jeu du Pinguin (\verb|penguin.cpp penguin.hpp|) et la partie Interface Graphique (dossier \verb|GUI|).
Toutes les parties sont sous licence MIT, avec attribution aux auteur respectifs.
Dans l'interface graphique, une bibliothèque JSON pour Java est utilisée: org.json. La librairie est accessible à l'adresse \url{http://mvnrepository.com/artifact/org.json/json}: \verb|Copyright (c) 2002 JSON.org|
le copyright complet est présent dans le fichier \verb|GUI/main/Main.java|
\newpage
\section{Implémentation d'un jeu}
\subsection{Implémentation d'un nouveau jeu: l'interface game}
Tous les fichiers évoqués dans cette section se situent dans le dossier \verb|AI/src|.
\paragraph*{}Un jeu jouable par le MCTS n'est rien d'autre qu'une classe qui implémente l'interface \verb|game|, définie dans \verb|game/game.hpp|.
Une fois la nouvelle classe codée, il suffit de changer une ligne dans le fichier \verb|main/main.cpp| pour lancer le jeu. Deux façons sont possibles pour lancer le jeu:
\begin{itemize}
\item \verb|game::run_test_two_players_game(game::MaClasse());| pour lancer un test à deux joueur: le MCTS ne va pas se lancer.
\item \verb|mcts::run_test_mcts_two_players(game::MaClasse());| pour lancer une partie contre le MCTS.
\end{itemize}
\paragraph*{} Dans l'interface, un joueur est représenté par un nombre sur 8bits (le premier joueur est le joueur 0, le deuxième le joueur 1). Chaque fin de partie
a une valeur qui est utilisée dans la construction de l'arbre de décision (classiquement 1 si le MCTS gagne, -1 s'il perd).
\paragraph*{} Pour implémenter l'interface, il faut implémenter toutes ses fonctions. Elles ne sont pas toutes indispensables ou dépendantes du nouveau jeu, c'est pourquoi
une partie d'entre elles pourront être directement copiées du fichier \verb|penguin.cpp|.
Les explications des fonctions indispensables sont listées ci-dessous:
\paragraph*{}
\begin{itemize}
\item \verb|bool end_of_game()| Indique si le jeu est terminé. Indépendamment de son résultat.
\item \verb|int value (uint8 player)| La valeur pour le joueur \verb|player| dans l'état actuel du jeu.
\item \verb|bool won/lost/draw(uint8 player)| Si le joueur \verb|player| a gagné/perdu/fait égalité.
\item \verb|uint8 current_player()| Le joueur dont c'est le tour.
\item \verb|uint16 number_of_moves()| Le nombre total de coups que le joueur actuel peut jouer.
\item \verb|play(uint16 move)| Joue le coup numéro \verb|move| pour le joueur actuel.
\item \verb|string player_to_string(uint8 player)| Représentation d'un joueur de façon compréhensible pour un humain.
\item \verb|string move_to_string(uint16 m)| Représentation d'un coup jouable de façon compréhensible pour un humain.
\item \verb|string to_string()| Représentation de l'état actuel du jeu de façon compréhensible pour un humain.
\end{itemize}
La fonction \verb|move_to_string| est importante car c'est la représentation que l'humain devra utiliser pour spécifier le coup qu'il veut jouer.
\paragraph*{} Il ne faut pas oublier d'ajouter les nouveaux fichiers dans le makefile!
\subsection{Notre implémentation: le Jeu du Pingouin}
Pour plus d'informations sur les structures et méthodes utilisées pour modéliser le Jeu du Pingouin, regarder le rapport du projet, qui se trouve dans le dossier \verb|Doc/fr/Rapport|.
\paragraph*{} Le fichier \verb|penguin.hpp| définit la structure \verb|penguin_state|, qui décrit un état du jeu. Cette structure définit les bitboards des poissons,
les bitboards des pingouins des 2 joueurs, les scores, le joueur courant et le nombre de coups de chaque joueur.
Les fichiers \verb|penguin.hpp| et \verb|penguin.cpp| définissent la classe \verb|penguin|, implémentant l'interface \verb|game|, qui décrit un jeu.
En plus des méthodes de l'interface, cette classe implémente les méthodes suivantes:
\begin{itemize}
\item \verb|penguin_state get_state()| Retourne l'état du jeu
\item \verb|void move_penguin(uint32_t* p, uint16_t rel_move)| Déplace le pingouin \verb|p| lui faisant faire son coup numéro \verb|rel_move|. À la fin de la fonction, le pingouin n'est plus composé que de sa nouvelle position (tous les autres bits sont à 0).
\item \verb|uint64_t create_obstacles_bitboard()| Crée le bitboard des obstacles : pour chaque case, le bit correspondant est à 1 s'il y a un obstacle, 0 sinon.
\item \verb|int update_penguin_moves(uint32_t* p, uint64_t obstacles)| Met à jour tous les coups du pingouin \verb|p| en fonction de sa position et du bitboard des obstacles. Retourne le nombre total de coups du pingouin.
\end{itemize}
Par ailleurs, le constructeur de la classe \verb|penguin| gère le chargement d'un état via l'entrée standard du programme.
\newpage
\section{L'interface graphique}
Tous les fichiers évoqués dans cette section se situent dans le dossier \verb|GUI/src|. L'interface graphique est développée en Java 1.8 (elle utilise notamment des lambda-expressions) et JavaFX.
L'interface graphique est codée suivant un modèle MVC (Modèle - Vue - Contrôleur).
\paragraph{Le modèle:} il sert à stocker toutes les données du jeu. Les classes sont utilisées comme conteneurs pour stocker les valeurs nécessaires au jeu.
La classe principale est la classe \verb|GameState| qui contient la plupart des informations relatives au jeu. Les valeurs sont stockés en forme de bitboards et utilise des opérations
binaires sont utilisées pour les extraire quand nécessaire.
Une méthode permet de mettre à jour un object \verb|GameState| à partir du JSON renvoyé par le MCTS.
\paragraph{La vue:} Le visuel de l'application est décrit dans le fichier \verb|view.fxml|, les différentes cases sont définies avec un positionnement absolu et
ont un id qui vont de \verb|tile0| à \verb|tile59|. Les autres éléments sont aussi positionnés de façon absolue.
Cette façon de faire n'est pas celle préconisée (elle n'est pas "propre", nous nous en excusons). L'interface graphique n'étant pas l'objectif initial du projet, nous y avons alloué moins de temps.
\paragraph{Le contrôleur:} il s'occupe de faire le lien entre le modèle, la vue et le programme en C++. Pour ce faire, à l'initialisation, l'IA sera exécutée comme sous-processus. Un \verb|UpdateThread| s'occupera de modifier le modèle à chaque fois que le sous-processus va écrire quelque chose dans sa sortie standard, et ensuite notifier la vue
de se mettre à jour aussi.
La classe \verb|Controller| est la classe ``principale'' du programme: elle s'occupe de créer le plateau, lancer la partie, écouter les clics du joueur au travers du \verb|EventHandler|, et de gérer la fin de partie (à la demande de l'\verb|UpdateThread|).
Lorsque l'interface graphique est fermée (peu importe le moment) le sous-processus est tué. Ceci se fait grâce à la fonction Java:
\begin{verbatim}
Runtime.getRuntime().addShutdownHook(new Thread()
{
public void run()
{
gameProcess.destroy();
}
});
\end{verbatim}
\end{document}
File added
\documentclass[a4paper]{article}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% PACKAGES
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage[french]{babel}
\usepackage{graphicx} %images
\usepackage[hidelinks]{hyperref}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\title{\textbf{Jeu du Pingouin: documentation utilisateur}}
\author{Francesco Bariatti \and Adrien Gasté \and Mikaël Le \and Romain Lebouc \\ Encadrant: Pascal Garcia}
\date{2015-2016}
\begin{document}
\pagenumbering{gobble}
\maketitle
\tableofcontents
\newpage
\pagenumbering{arabic}
\section{Introduction}
Le Jeu du Pingouin est un projet étudiant INSA de département Informatique, dans le cadres des études pratiques de 3\ieme année. Il est principalement composé de deux parties majeures : l'intelligence artificielle (en \emph{C++}) et l'interface graphique (en \emph{JavaFX}).
\subsection{Règles du jeu}
\subsubsection{Matériel}
Le plateau de jeu est composé d'une grille de 60 tuiles hexagonales, représentant la banquise. Chaque tuile comporte un nombre de
poissons compris entre 1 et 3. À 2 joueurs, chaque joueur possède 4 jetons de pingouins.
\subsubsection{Mise en place}
On crée le plateau de départ en répartissant les tuiles aléatoirement. Chaque joueur place ensuite ses pingouins tour à tour sur
les cases "1 Poisson". Dans notre implémentation, le joueur choisit l'emplacement de ses 4 pingouins, puis de ceux de l'ordinateur.
\subsubsection{Objectif}
Avoir mangé plus de poissons que son adversaire.
\subsubsection{Actions}
Les joueurs jouent chacun leur tour. Pendant son tour, un joueur doit choisir un de ses pingouins, et le déplacer sur une des cases qui lui sont accessibles.
Un pingouin peut se déplacer dans n'importe laquelle des 6 directions possibles, tant qu'il ne rencontre pas d'obstacles.
Un obstacle peut être un autre pingouin ou une case vide.
Quand le coup est joué, le joueur retire la case depuis laquelle le pingouin est parti, et gagne un nombre de points égal au nombre de poissons sur cette case.
La case devient alors une case vide, et le tour se termine.
\subsubsection{À noter}
Dans les règles officielles, à la fin de la partie, chaque joueur gagne le nombre de points des tuiles finales sur lesquelles sont leurs pingouins.
Pour des raisons d'implémentation, on ignorera cette règle.
\newpage
\section{Installation}
Le projet est disponible sous Gitlab à l'adresse \url{https://gitlab.insa-rennes.fr/francesco-bariatti/pingouins.git}; Sous Linux, il suffit de télécharger les sources et de les compiler.
\subsection{Dépendances}
\paragraph{IA} Une version de g++ 4.9 minimum est nécessaire.
\paragraph{GUI} L'interface graphique utilisant JavaFX, une version de Java supérieure (ou égale) à 1.8 est nécessaire. Par ailleurs, si le jeu est lancé à partir d'un IDE, le niveau de langage doit être de 8 minimum (utilisation des \emph{lambdas}) et le jar JSON doit être dans le java build path.
\subsection{Compilation et lancement}
Modifier le fichier \verb|AI/src/mcts/MCTS_SETTINGS.hpp| pour changer la quantité de mémoire allouée et le temps de réflexion de l'IA.
Il faudra sûrement ajouter de la mémoire pour que le jeu puisse s’exécuter jusqu'à la fin: si la mémoire n'est pas suffisante un message d'erreur disant ``That's it! I ragequit!'' s'affiche et le jeu s'arrête. L'idéal est de tester en lançant une partie jusqu'au moment de réflexion du pc et lancer une commande \verb|free -h| pour visualiser la quantité de mémoire utilisée.
En général 1 ou 2 Go suffisent.
Pour compiler, exécuter la commande \verb|make|.
En cas d'erreur avec la version du compilateur g++ ou java, changer la valeur des variables en début du \verb|Makefile|.
Pour lancer le jeu, exécuter la commande \verb|make run|.
Attention au volume du son au lancement !
\section{Utilisation}
Au démarrage du programme une boite de dialogue propose de charger un état (formaté en JSON). Si l'état est invalide, ou laissé vide, le plateau de départ est généré aléatoirement.
Le joueur peut alors placer successivement les 4 pingouins rouges, puis les 4 pingouins bleus en cliquant sur les cases.
Le joueur choisit ensuite sa couleur, et le jeu démarre par le tour des rouges.
À chaque tour, le joueur peut choisir un pingouin en cliquant dessus et le déplacer sur une des cases mises en relief. Il faut alors attendre que l'IA joue avant de continuer.
À chaque coup joué, l'état apparaît dans la console. Il est possible de copier n'importe lequel de ces états et de le recharger en démarrant une partie.
\section{Contact}
Si vous trouvez des bugs, n'hésitez pas à ouvrir des issues sur Gitlab (\url{https://gitlab.insa-rennes.fr/francesco-bariatti/pingouins.git})
\end{document}
\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