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 (126)
Showing
with 10863 additions and 5 deletions
bin/
AI/bin/
__pycache__/
.metadata
GUI/.classpath
GUI/bin
GUI/.project
GUI/.settings
GUI/out
GUI/.idea
GUI/GUI.iml
#Latex generated files
*.log
*.out
*.synctex.gz
*.aux
*.tox
*.toc
*.dvi
*.bbl
*.blg
*.nav
*.snm
......@@ -25,7 +25,7 @@ namespace game
virtual std::string player_to_string(std::uint8_t player) const = 0;
virtual std::string move_to_string(std::uint16_t m) const = 0;
virtual std::string to_string() const = 0;
virtual void playout(std::mt19937& engine, int max_depth = -1) = 0;
virtual void playout(std::mt19937& engine, int max_depth = -1);
virtual std::set<int> to_input_vector() const = 0;
virtual void from_input_vector(const std::set<int>& input) = 0;
virtual State get_state() = 0;
......@@ -43,6 +43,15 @@ namespace game
{
return value(current_player());
}
template<typename State> void game<State>::playout(std::mt19937& engine, int max_depth)
{
while (!end_of_game())
{
std::uniform_int_distribution<uint16_t> distribution(0, number_of_moves() - 1);
std::uint16_t move = distribution(engine);
play(move);
}
}
}
#endif
#include "penguin.hpp"
#include <sstream>
using namespace std;
namespace game
{
/**
* Creates the game: load the state from standard input.
* Penguins in state can be composed of just their position: the moves will be updated automatically.
**/
penguin::penguin()
{
cout << "Enter penguin game state as JSON on one line" << endl;
string line;
getline(cin, line);
json json_state = json::parse(line);
//Charging every element of the state if it exists
if(json_state.count("bitboards"))
{
if(json_state["bitboards"].count("onefish")) {state.one_fish = json_state["bitboards"]["onefish"];}
if(json_state["bitboards"].count("twofish")) {state.two_fish = json_state["bitboards"]["twofish"];}
if(json_state["bitboards"].count("threefish")) {state.three_fish = json_state["bitboards"]["threefish"];}
}
if(json_state.count("current_player"))
state.current_player_red = json_state["current_player"] == "Red" ? true : false;
if(json_state.count("penguins"))
{
if(json_state["penguins"].count("red"))
{
for(int i = 0; i < 4; i++)
state.peng_red[i] = json_state["penguins"]["red"][i];
}
if(json_state["penguins"].count("blue"))
{
for(int i = 0; i < 4; i++)
state.peng_blue[i] = json_state["penguins"]["blue"][i];
}
}
if(json_state.count("score"))
{
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"]; }
}
// We compute the moves for all penguin
uint64_t obstacles = create_obstacles_bitboard();
state.nb_moves_red = 0;
state.nb_moves_blue = 0;
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;
state.nb_moves_red = 1; //We create an artificial move so that the mcts works
}
if (state.nb_moves_blue == 0)
{
state.canPlay_blue = false;
state.nb_moves_blue = 1; //We create an artificial move so that the mcts works
}
}
shared_ptr<game<penguin_state>> penguin::do_copy() const
{
return shared_ptr<penguin>(new penguin(*this));
}
penguin_state penguin::get_state()
{
return state;
}
void penguin::set_state(const penguin_state& s)
{
state = s;
}
bool penguin::end_of_game() const
{
return state.canPlay_red == false && state.canPlay_blue == false;
}
bool penguin::won(std::uint8_t player) const
{
if (player == RED) return state.score_red > state.score_blue;
return state.score_blue > state.score_red;
}
bool penguin::lost(std::uint8_t player) const
{
if(player == RED) return state.score_red < state.score_blue;
return state.score_blue < state.score_red;
}
bool penguin::draw(std::uint8_t player) const
{
return state.score_blue == state.score_red;
}
uint8_t penguin::current_player() const
{
return state.current_player_red ? RED : BLUE;
}
int penguin::value(uint8_t player) const
{
if (won(player))
return 1;
else if (lost(player))
return -1;
else
return 0;
}
/* Number of moves that you can play */
uint16_t penguin::number_of_moves() const
{
return state.current_player_red ? state.nb_moves_red : state.nb_moves_blue;
}
/*
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)
{
//Direction A
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 * (rel_move +1)) + ((*p) & 63);
return;
}
rel_move -= PENGUIN_MOVES_A(*p);
if(PENGUIN_MOVES_B(*p) > rel_move)
{
//Move direction B
(*p) = (-1 * (rel_move +1)) + ((*p) & 63);
return;
}
rel_move -= PENGUIN_MOVES_B(*p);
if(PENGUIN_MOVES_C(*p) > rel_move)
{
//Move direction C
(*p) = (-8 * (rel_move +1)) + ((*p) & 63);
return;
}
rel_move -= PENGUIN_MOVES_C(*p);
if(PENGUIN_MOVES_D(*p) > rel_move)
{
//Move direction D
(*p) = (-7 * (rel_move +1)) + ((*p) & 63);
return;
}
rel_move -= PENGUIN_MOVES_D(*p);
if(PENGUIN_MOVES_E(*p) > rel_move)
{
//Move direction E
(*p) = (1 * (rel_move +1)) + ((*p) & 63);
return;
}
rel_move -= PENGUIN_MOVES_E(*p);
//Move direction F
(*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;
}
/* 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 = PENGUIN_POS(*p);
(*p) = pos; //Reset the penguin to all zeros except the position
int i = pos;
uint32_t nbmoves = 0; //Nb of moves in one direction
uint32_t total_moves = 0;
//Direction A
while(((i+7) < 60) && (i%15 != 0) && IsFree(i+7))
{
i += 7;
nbmoves++; total_moves++;
}
(*p) = (*p) | nbmoves << 12;
//Direction B
nbmoves = 0;
i = pos;
while((i%15 != 0) && (i%15 != 8) && IsFree(i-1))
{
i --;
nbmoves++; total_moves++;
}
(*p) = (*p) | nbmoves << 15;
//Direction C
nbmoves = 0;
i = pos;
while((i-8 >= 0) && (i%15 != 0) && IsFree(i-8))
{
i -= 8;
nbmoves++; total_moves++;
}
(*p) = (*p) | nbmoves << 18;
//Direction D
nbmoves = 0;
i = pos;
while((i-7 > 0) && (i%15 != 7) && IsFree(i-7))
{
i -= 7;
nbmoves++; total_moves++;
}
(*p) = (*p) | nbmoves << 21;
//Direction E
nbmoves = 0;
i = pos;
while((i%15 != 7) && (i%15 != 14) && IsFree(i+1))
{
i ++;
nbmoves++; total_moves++;
}
(*p) = (*p) | nbmoves << 24;
//Direction F
nbmoves = 0;
i = pos;
while((i+8 < 60) && (i%15 != 7) && IsFree(i+8))
{
i += 8;
nbmoves++; total_moves++;
}
(*p) = (*p) | nbmoves << 27;
(*p) = (*p) | total_moves << 6;
return total_moves;
}
//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))
{
//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];
}
// ADD PENGUIN TILE TO THE SCORE
if((state.one_fish >> PENGUIN_POS(*peng)) & 1) //If there is a one fish on this position
{
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) << PENGUIN_POS(*peng));
}
else if((state.two_fish >> PENGUIN_POS(*peng)) & 1)
{
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) << PENGUIN_POS(*peng));
}
else
{
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) << PENGUIN_POS(*peng));
}
// MOVE THE PENGUIN
move_penguin(peng, rel_move);
}
// 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 blue couldn't play last turn there is no way he could play this new turn
{
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;
state.nb_moves_blue = 1; //We create an artificial move so that the mcts works
}
}
else
{
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)
{
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;
state.nb_moves_red = 1;
}
}
else
{
state.nb_moves_red = 1;
}
state.current_player_red = true;
}
}
string penguin::player_to_string(uint8_t player) const
{
return player == RED ? "Red" : "Blue";
}
string penguin::move_to_string(uint16_t m) const
{
return std::to_string(m);
}
set<int> penguin::to_input_vector() const
{
return set<int>();
}
void penguin::from_input_vector(const std::set<int>& input)
{
}
json penguin::to_JSON() const
{
json json_state;
json_state["bitboards"]["onefish"] = state.one_fish;
json_state["bitboards"]["twofish"] = state.two_fish;
json_state["bitboards"]["threefish"] = state.three_fish;
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;
json_state["possible_moves"]["red"] = state.nb_moves_red;
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;
return json_state;
}
string penguin::to_string() const
{
ostringstream os;
os << to_JSON() << endl;
return os.str();
}
std::uint64_t penguin::hash() const
{
return 0;
}
std::uint64_t penguin::hash(std::uint16_t m) const
{
return 0;
}
ostream& operator<<(ostream& os, const penguin& pen)
{
os << pen.to_string();
return os;
}
}
#ifndef __MORPION_HPP__
#define __MORPION_HPP__
#ifndef __PENGUIN_HPP__
#define __PENGUIN_HPP__
#include "game.hpp"
#include "json.hpp"
using json = nlohmann::json;
#include <random>
#include <array>
#include <iostream>
......@@ -9,69 +11,76 @@
namespace game
{
struct morpion_state
struct penguin_state
{
uint16_t cross_bitboard = 0; //bitboard with the played moves of the cross
uint16_t circle_bitboard = 0; //bitboard with the played moves of the circle
uint8_t total_moves = 0; //Total played moves (<= 9)
uint64_t possible_moves = 0x876543210; //List of possible moves left
bool first_player_win = false; //First player is always the cross
bool second_player_win = false; //Second player is always the circle
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 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;
};
class morpion : public game<morpion_state>
// 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:
morpion();
morpion(const morpion& mor) = default;
morpion& operator=(const morpion& mor) = default;
penguin();
penguin(const penguin& pen) = default;
penguin& operator=(const penguin& pen) = default;
bool end_of_game() const; //Is the game ended? (draw or won)
int value(std::uint8_t player) const; //Returns if the player win, loose or nothing
bool won(std::uint8_t player) const;
bool lost(std::uint8_t player) const;
bool draw(std::uint8_t player) const;
uint8_t current_player() const; //The player that has to play next (at the beginning, the first player)
std::uint16_t number_of_moves() const; //Moves played until now
void play(std::uint16_t m); //Play a move (updates the state)
std::uint16_t number_of_moves() const; //Number of moves that you can play
void play(std::uint16_t m); //Play the move m (updates the state).
void undo(std::uint16_t m) {}
std::string player_to_string(std::uint8_t player) const; //String representation of a player
std::string move_to_string(std::uint16_t m) const; //String representation of a move (for example, A1)
json to_JSON() const;
std::string to_string() const; //String representation of the entire game
void playout(std::mt19937& engine, int max_depth = -1);
std::set<int> to_input_vector() const;
void from_input_vector(const std::set<int>& input);
morpion_state get_state(); //Return the state
void set_state(const morpion_state& state); //Replace the current state with the one passed as a parameter
std::shared_ptr<game<morpion_state>> do_copy() const;
penguin_state get_state(); //Return the state
void set_state(const penguin_state& state); //Replace the current state with the one passed as a parameter
std::shared_ptr<game<penguin_state>> do_copy() const;
std::uint64_t hash(std::uint16_t m) const;
std::uint64_t hash() const;
private:
inline void update_win(); //Check if someone won and update the state
inline bool has_won(uint16_t bitboard); //Check if the player whose bitboard was passed as a param has won
inline bool get(uint16_t bitboard, uint8_t i, uint8_t j) const; //Get a case of the board
inline void update_moves(); //Update the list of all possible moves
const uint8_t CROSS = 0;
const uint8_t CIRCLE = 1;
penguin_state state;
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);
morpion_state state;
//WIN CONSTANTS
const uint16_t ROW0_MASK = 504;
const uint16_t ROW1_MASK = 455;
const uint16_t ROW2_MASK = 63;
const uint16_t COL0_MASK = 438;
const uint16_t COL1_MASK = 365;
const uint16_t COL2_MASK = 219;
const uint16_t DIA0_MASK = 238;
const uint16_t DIA1_MASK = 427;
const uint16_t ALL_ONES = 511;
const uint8_t RED = 0;
const uint8_t BLUE = 1;
static std::vector<std::vector<uint64_t>> cross_hash_values;
static std::vector<std::vector<uint64_t>> circle_hash_values;
};
std::ostream& operator<<(std::ostream& os, const morpion& mor);
std::ostream& operator<<(std::ostream& os, const penguin& pen);
}
#endif
#include "game.hpp"
#include "test_two_players_game.hpp"
#include <iostream>
#include <map>
using namespace std;
namespace game
{
}
#ifndef __TEST_TWO_PLAYERS_GAME_HPP__
#define __TEST_TWO_PLAYERS_GAME_HPP__
#include <random>
#include <iostream>
#include <map>
#include <string>
namespace game
{
template<typename Game>
class test_two_players_game
{
void playout(Game g);
void play(Game g);
public:
test_two_players_game(Game g);
};
template<typename Game>
test_two_players_game<Game>::test_two_players_game(Game g)
{
// playout(g);
play(g);
}
template <typename Game>
void run_test_two_players_game(const Game& g)
{
test_two_players_game<Game>{g};
}
template <typename Game>
void test_two_players_game<Game>::playout(Game g)
{
std::mt19937 mt;
std::cout << "playout" << std::endl;
for (int i = 0; i < 100; ++i)
{
uint8_t player = g.current_player();
auto state = g.get_state();
g.playout(mt);
std::cout << "value: " << g.value(player) << std::endl << g.to_string() << std::endl;
g.set_state(state);
std::string wait;
getline(std::cin, wait);
}
}
template<typename Game>
void test_two_players_game<Game>::play(Game g)
{
int player = 0;
std::cout << "play one game" << std::endl;
while (!g.end_of_game())
{
std::cout << g.to_string() << std::endl;
std::cout << g.player_to_string(player) << " move: ";
std::map<std::string, int> m;
for (int i = 0; i < g.number_of_moves(); ++i)
{
m[g.move_to_string(i)] = i;
}
std::string move;
std::cin >> move;
g.play(m[move]);
player = g.current_player();
}
std::cout << g.to_string() << std::endl;
if (g.won(0)) std::cout << g.player_to_string(0) << " won";
else if (g.won(1)) std::cout << g.player_to_string(1) << " won";
else std::cout << "draw";
std::cout << std::endl;
}
}
#endif
This diff is collapsed.
/**
* 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"
#include "test_allocator.hpp"
#include "test_mcts_two_players.hpp"
#include "test_bits.hpp"
#include "learning.hpp"
#include <omp.h>
using namespace std;
int main(int argc, char *argv[])
{
// util::test_fast_log(100000000);
// mcts::test_allocator(10, 2);
// omp_set_num_threads(8);
//game::run_test_two_players_game(game::penguin());
mcts::run_test_mcts_two_players(game::penguin());
//util::test_bits(200000000);
return 0;
}
#ifndef __MCTS_SETTINGS_HPP__
#define __MCTS_SETTINGS_HPP__
// Allocated memory for the mcts. If not enough the program may crash
#define MCTS_ALLOCATOR_SIZE 1000000U
// Reflection time for every turn of the mcts (in ms)
#define MCTS_TURN_TIME 5000
#endif
File moved
......@@ -2,6 +2,7 @@
#define __ALLOCATOR_HPP__
#include "node.hpp"
#include "MCTS_SETTINGS.hpp"
namespace mcts
{
......@@ -15,7 +16,7 @@ namespace mcts
void copy(node* n1, node* n2, unsigned int prunning = 0);
public:
allocator(unsigned int size = 20000000U);
allocator(unsigned int size = MCTS_ALLOCATOR_SIZE);
~allocator();
node* allocate(unsigned int size);
void clear();
......
File moved
......@@ -210,7 +210,7 @@ namespace mcts
}
// std::ofstream ofs ("graph.gv", ofstream::out);
// util::display_node::node_to_dot(ofs, this->root, 1000, 50);
util::display_node::node_to_ascii(cout, this->root, 2);
util::display_node::node_to_ascii(cout, this->root, 1);
// std::cout << "finished " << new_version_ << std::endl;
// string _;
// getline(cin, _);
......@@ -265,7 +265,7 @@ namespace mcts
}
else
{
this->root = alloc_.move(&this->root->get_children()[move]);
this->root = alloc_.move(&this->root->get_children()[move], 20);
}
}
......
File moved
File moved