diff --git a/Makefile b/Makefile index f1a40f3c1104950808299b45b56d6109d5876d2d..c414ab9075d6b9761320e9520aa568316dd9f904 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,9 @@ -CC=g++ +CC=g++-4.9 BIN=bin INCLUDE=-I src/game -I src/util -I src/monte_carlo -I src/mcts -I src/minmax CFLAGS=-g -O3 -ffast-math -fopenmp -c -Wall -std=c++11 $(INCLUDE) LDFLAGS=-fopenmp -std=c++11 #-lprofiler -Wl,-no_pie -SOURCES=omp_util.cpp fast_log.cpp display_node.cpp morpion.cpp test_morpion.cpp connect4.cpp test_connect4.cpp monte_carlo.cpp test_monte_carlo.cpp test_fast_log.cpp\ +SOURCES=omp_util.cpp fast_log.cpp display_node.cpp connect4.cpp morpion.cpp test_two_players_game.cpp test_connect4.cpp monte_carlo.cpp test_monte_carlo.cpp test_fast_log.cpp\ statistics.cpp node.cpp allocator.cpp test_allocator.cpp openings.cpp mcts_two_players.cpp test_mcts_two_players.cpp test_minmax.cpp\ bits.cpp test_bits.cpp main.cpp OBJECTS=$(addprefix $(BIN)/, $(SOURCES:.cpp=.o)) diff --git a/doc/GameInterface.markdown b/doc/GameInterface.markdown index 2a9f8c9984c1d66c3b376b4c419fd974d806228f..a41d5eeee67bc88773d9587ab22bf5f5ffd0d8cc 100644 --- a/doc/GameInterface.markdown +++ b/doc/GameInterface.markdown @@ -74,7 +74,7 @@ Representation d'un joueur de façon comprehensible par un humain ### string move_to_string(int move) Representation d'un coup de façon comprehensible par un humain (par exemple B7). -**Note:** est-ce que le coup doit être compris comme le coup numéro `move` dans la liste des coups possibles ou `move` est la representation que l'ordi a du coup ? +`move` est l'indice du coup parmi tous les coups possibles ### string to_string() Representation de l'état actuel du jeu (c'est à dire, dessiner l'état actuel du jeu) @@ -111,4 +111,4 @@ void JEU::playout(mt19937& engine, int max_depth) ``` Choisit un coup au hasard parmi les coups possibles et le joue. - \ No newline at end of file + diff --git a/src/game/connect4.cpp b/src/game/connect4.cpp index c7d4a713ee93bf8e6ed6d5350696906a2c7b0f05..c4d05c85e6b0ce05879becd5c38048ca8a843f94 100644 --- a/src/game/connect4.cpp +++ b/src/game/connect4.cpp @@ -332,15 +332,15 @@ namespace game return buffer.str(); } - void connect4::playout(mt19937& engine, int max_depth) - { - while (!end_of_game()) - { - uniform_int_distribution<uint16_t> distribution(0, number_of_moves() - 1); - uint16_t move = distribution(engine); - play(move); - } - } + // void connect4::playout(mt19937& engine, int max_depth) + // { + // while (!end_of_game()) + // { + // uniform_int_distribution<uint16_t> distribution(0, number_of_moves() - 1); + // uint16_t move = distribution(engine); + // play(move); + // } + // } std::uint64_t connect4::hash() const { diff --git a/src/game/connect4.hpp b/src/game/connect4.hpp index 369bd8ecdd43c73ba7a4ca1da790885c023dde40..1ea92aedf328a7fb13d090f7dd9c39a54a7f8183 100644 --- a/src/game/connect4.hpp +++ b/src/game/connect4.hpp @@ -42,7 +42,7 @@ namespace game std::string player_to_string(std::uint8_t player) const; std::string move_to_string(std::uint16_t m) const; std::string to_string() const; - void playout(std::mt19937& engine, int max_depth = -1); + // 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); connect4_state get_state(); diff --git a/src/game/game.hpp b/src/game/game.hpp index 20f5e2f1938ca0d3a1eb50e4c97736e851189ae0..1854891fc6637a4452bc1772234229c7565df960 100644 --- a/src/game/game.hpp +++ b/src/game/game.hpp @@ -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 diff --git a/src/game/morpion.cpp b/src/game/morpion.cpp index 27030269fb70bf26e83dfd28350bfcc0c1de3b02..4662a6fc0689030159e852f4941b588ae13d64ea 100644 --- a/src/game/morpion.cpp +++ b/src/game/morpion.cpp @@ -172,8 +172,7 @@ namespace game string morpion::move_to_string(uint16_t m) const { - return std::to_string(m); - + return std::to_string((state.possible_moves >> (4 * m)) & 0xf); } set<int> morpion::to_input_vector() const @@ -187,7 +186,7 @@ namespace game string morpion::to_string() const { - string result = ""; + string result = "-------\n"; for (int row = 2; row >= 0; row--) { result += "|"; @@ -198,23 +197,13 @@ namespace game else if (((state.circle_bitboard >> (3*row)) >> col) & 1) result += player_to_string(CIRCLE)+"|"; else - result += " |"; + result += std::to_string(row * 3 + col) + "|"; } result += "\n-------\n"; } return result; } - void morpion::playout(mt19937& engine, int max_depth) - { - while (!end_of_game()) - { - uniform_int_distribution<uint16_t> distribution(0, number_of_moves() -1); - uint16_t move = distribution(engine); - play(move); - } - } - std::uint64_t morpion::hash() const { //TODO: Implement diff --git a/src/game/morpion.hpp b/src/game/morpion.hpp index 4ab8594c6d8434c9c5c4d7f3b48ae11142b0d61e..e210f5e5a419df9b26f084415ac58e454ddea600 100644 --- a/src/game/morpion.hpp +++ b/src/game/morpion.hpp @@ -38,7 +38,6 @@ namespace game 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) 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 diff --git a/src/game/test_morpion.cpp b/src/game/test_morpion.cpp deleted file mode 100644 index 705224015a9f26c2126df6c99b80fcb06f0ad51a..0000000000000000000000000000000000000000 --- a/src/game/test_morpion.cpp +++ /dev/null @@ -1,116 +0,0 @@ -#include "test_morpion.hpp" -#include "mcts_two_players.hpp" -#include "morpion.hpp" -#include <iostream> -#include <map> - -using namespace std; - - -namespace game -{ - test_morpion::test_morpion() - { - cout << "=== Play tic tac toe ==="<<endl; - cout << "Do you want to play a Player vs (P)layer or a Player vs (C)omputer match? (enter P or C) "<<endl; - string choice; - cin >> choice; - if (choice == "P") - play(); - else if (choice == "C") - playout(); - else - cout << "Bad choice." << endl; - } - - /* Start a complete Player vs Player game */ - void test_morpion::play() - { - morpion mor; - cout << "Player vs Player game!" << endl; - while(!mor.end_of_game()) - { - cout << mor << endl; - cout << "It's " << mor.player_to_string(mor.current_player()) << " turn." << endl; - map<string, int> movesMap; - //cout << "Number of possible moves: " << mor.number_of_moves() << endl; - cout << "Possible moves:"; - uint64_t possible_moves = mor.get_state().possible_moves; - for(int i = 0; i < mor.number_of_moves(); i++) - { - uint16_t move = possible_moves & ((uint64_t) 15); //15 = 1111 (a move is on 4 bits) - //cout << "Possible move: " << mor.move_to_string(move) << endl; - cout << " " << mor.move_to_string(move); - movesMap[mor.move_to_string(move)] = i; //In the map: the move as seen by the player and its representation for the computer - possible_moves = possible_moves >> 4; - } - cout << endl << "Choose your move: "; - string move; - cin >> move; - mor.play(movesMap[move]); - } - //Game ended: print the final state of the board - cout << mor << endl; - if (mor.won(0)) - cout << mor.player_to_string(0) << " has won" << endl; - else if (mor.won(1)) - cout << mor.player_to_string(1) << " has won" << endl; - else - cout << "Draw! too bad :(" << endl; - - } - - /* Start a complete Player vs Computer game */ - void test_morpion::playout() - { - morpion mor; - auto mctsPlayer = mcts::make_mcts_two_players(mor, 1000, 0.3, 4); // creation du player a partir du morpion. 1000: ms de reflexion 0.3 fateur d'exploration 4: nb_visit_before_expansion on attend d'etre arrive 4 fois sur un noeud avant de le developper - cout << "Player vs Computer game!" << endl; - cout << "Who's first? (P)layer or (C)omputer?" << endl; - string choice; - cin >> choice; - int IAPlayer = 1; //Default: the computer will play second - if (choice == "C") //If however we choosed to let the computer play first - IAPlayer = 0; - while(!mor.end_of_game()) - { - cout << mor << endl; - if(mor.current_player() == IAPlayer) //It's computer's turn - { - mctsPlayer.reset(); //Delete the entire tree: it will construct a new one starting from this state - uint16_t move = mctsPlayer.select_move(); - //cout << "Computer played move number " << move << endl; - mor.play(move); - } - else //Human turn to play - { - cout << "It's your time to shine!" << endl; - map<string, int> movesMap; - //cout << "Number of possible moves: " << mor.number_of_moves() << endl; - cout << "Possible moves:"; - uint64_t possible_moves = mor.get_state().possible_moves; - for(int i = 0; i < mor.number_of_moves(); i++) - { - uint16_t move = possible_moves & ((uint64_t) 15); //15 = 1111 (a move is on 4 bits) - //cout << "Possible move: " << mor.move_to_string(move) << endl; - cout << " " << mor.move_to_string(move); - movesMap[mor.move_to_string(move)] = i; //In the map: the move as seen by the player and - possible_moves = possible_moves >> 4; - } - cout << endl << "Choose your move: "; - string move; - cin >> move; - mor.play(movesMap[move]); - } - } - //Game ended: print the final state of the board - cout << mor << endl; - if (mor.won(0)) - cout << mor.player_to_string(0) << " has won" << endl; - else if (mor.won(1)) - cout << mor.player_to_string(1) << " has won" << endl; - else - cout << "Draw! too bad :(" << endl; - } - -} diff --git a/src/game/test_morpion.hpp b/src/game/test_morpion.hpp deleted file mode 100644 index 03966986fc5befd5e4c88fe9dc7d3cae98904501..0000000000000000000000000000000000000000 --- a/src/game/test_morpion.hpp +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef __TEST_MORPION_HPP__ -#define __TEST_MORPION_HPP__ - -namespace game -{ - class test_morpion - { - void playout(); - void play(); - public: - test_morpion(); - }; -} -#endif diff --git a/src/game/test_two_players_game.cpp b/src/game/test_two_players_game.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3a1cebabea372f759a6114bbf74dd82dc1765421 --- /dev/null +++ b/src/game/test_two_players_game.cpp @@ -0,0 +1,10 @@ +#include "game.hpp" +#include "test_two_players_game.hpp" +#include <iostream> +#include <map> + +using namespace std; + +namespace game +{ +} diff --git a/src/game/test_two_players_game.hpp b/src/game/test_two_players_game.hpp new file mode 100644 index 0000000000000000000000000000000000000000..ce7de78d460bc662b27556b3be10de6c2e4169db --- /dev/null +++ b/src/game/test_two_players_game.hpp @@ -0,0 +1,76 @@ +#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 = 1 - 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 diff --git a/src/main/main.cpp b/src/main/main.cpp index 075076f250c4410c14d665fd05d2c7c838320f60..fe3a17ba54ed227c095ae715f39ea9c1dac794a5 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -1,5 +1,7 @@ -//#include "test_connect4.hpp" -#include "test_morpion.hpp" +#include "test_connect4.hpp" +#include "connect4.hpp" +#include "morpion.hpp" +#include "test_two_players_game.hpp" #include "test_monte_carlo.hpp" #include "test_fast_log.hpp" #include "test_allocator.hpp" @@ -15,11 +17,15 @@ using namespace std; int main(int argc, char *argv[]) { // game::test_connect4(); - game::test_morpion(); // util::test_fast_log(100000000); // mcts::test_allocator(10, 2); // omp_set_num_threads(8); - //mcts::test_mcts_two_players(); + // game::run_test_two_players_game(game::connect4()); + //mcts::run_test_mcts_two_players(game::connect4()); + + //game::run_test_two_players_game(game::morpion()); + mcts::run_test_mcts_two_players(game::morpion()); + // minmax::test_minmax(); //util::test_bits(200000000); //game::connect4 c4; diff --git a/src/mcts/allocator.hpp b/src/mcts/allocator.hpp index 9ee278779ec061547a6383404507d7e29bb0a1ec..f887fdc79c0ac430980c76aee6ba995e94b13200 100644 --- a/src/mcts/allocator.hpp +++ b/src/mcts/allocator.hpp @@ -15,7 +15,7 @@ namespace mcts void copy(node* n1, node* n2, unsigned int prunning = 0); public: - allocator(unsigned int size = 20000000U); + allocator(unsigned int size = 100000000U); ~allocator(); node* allocate(unsigned int size); void clear(); diff --git a/src/mcts/allocator.hpp~ b/src/mcts/allocator.hpp~ deleted file mode 100644 index 150a2ffc1d220711051c77e21fe5d33779f145fa..0000000000000000000000000000000000000000 --- a/src/mcts/allocator.hpp~ +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef __ALLOCATOR_HPP__ -#define __ALLOCATOR_HPP__ - -#include "node.hpp" - -namespace mcts -{ - class allocator - { - node* node_arena; - node* limit; - node* free_pointer; - - node* allocate_unsafe(unsigned int size); - void copy(node* n1, node* n2, unsigned int prunning = 0); - - public: - allocator(unsigned int size = 1000000U); - ~allocator(); - node* allocate(unsigned int size); - void clear(); - node* move(node* root, unsigned int prunning = 0); - unsigned int size() const; - unsigned int free_space() const; - }; -} - -#endif diff --git a/src/mcts/mcts_two_players.hpp b/src/mcts/mcts_two_players.hpp index 29e4e96abf3daa5f696c514780f86da7919d9652..b640dc66e8939938e5cf3bafac89671d2c01ab5b 100644 --- a/src/mcts/mcts_two_players.hpp +++ b/src/mcts/mcts_two_players.hpp @@ -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, _); diff --git a/src/mcts/test_mcts_two_players.cpp b/src/mcts/test_mcts_two_players.cpp index 931ccd367b26876d83c84dfb889c024cd7b85ab9..c462a7bdff0253f6375a993f6566a376dd37d311 100644 --- a/src/mcts/test_mcts_two_players.cpp +++ b/src/mcts/test_mcts_two_players.cpp @@ -1,6 +1,6 @@ #include "mcts_two_players.hpp" #include "test_mcts_two_players.hpp" -#include "morpion.hpp" +#include "connect4.hpp" #include <iostream> #include <iomanip> #include <map> @@ -12,269 +12,269 @@ using namespace game; namespace mcts { - test_mcts_two_players::test_mcts_two_players() : openings_(morpion()) - { - //self_play(1000); - play(); - //test_openings(10000, 1000); - } + // test_mcts_two_players::test_mcts_two_players() : openings_(connect4()) + // { + // //self_play(1000); + // play(); + // //test_openings(10000, 1000); + // } - void test_mcts_two_players::test_openings(int nb_learning, int nb_testing) - { - // self_play(nb_testing); - self_play_learn_openings(nb_learning); - self_play(nb_testing, true); - } + // void test_mcts_two_players::test_openings(int nb_learning, int nb_testing) + // { + // // self_play(nb_testing); + // self_play_learn_openings(nb_learning); + // self_play(nb_testing, true); + // } - template <typename Game> - int test_mcts_two_players::select_move(Game& game) - { - cout << game.player_to_string(game.current_player()) << " move: "; - map<string, int> m; - for (int i = 0; i < game.number_of_moves(); ++i) - { - m[game.move_to_string(i)] = i; - } - string move; - getline(cin, move); - game.play(m[move]); - return m[move]; - } + // template <typename Game> + // int test_mcts_two_players::select_move(Game& game) + // { + // cout << game.player_to_string(game.current_player()) << " move: "; + // map<string, int> m; + // for (int i = 0; i < game.number_of_moves(); ++i) + // { + // m[game.move_to_string(i)] = i; + // } + // string move; + // getline(cin, move); + // game.play(m[move]); + // return m[move]; + // } - void test_mcts_two_players::play() - { - // ProfilerStart("theturk.prof"); - morpion mor; - auto the_turk = make_mcts_two_players(mor, 5000, 0.3, 4); - cout << "play one game" << endl; - cout << "who's first? (h)uman/(c)omputer "; - string ans; - getline(cin, ans); - cout << mor << endl; - int human_last_move = -1, computer_last_move = -1; - while (!mor.end_of_game()) - { - if ((ans == "h" && mor.current_player() == 0) || (ans == "c" && mor.current_player() == 1)) - { - human_last_move = select_move(mor); - } - else - { - if (human_last_move != -1 && computer_last_move != -1) - { - the_turk.last_moves(computer_last_move, human_last_move); - } - uint16_t move = the_turk.select_move(); - computer_last_move = move; - cout << mor.player_to_string(mor.current_player()) << " move: " << mor.move_to_string(move) << endl; - mor.play(move); - } - cout << mor << endl; - } - if (mor.value(0) == 1) cout << mor.player_to_string(0) << " won"; - else if (mor.value(1) == 1) cout << mor.player_to_string(1) << " won"; - else cout << "draw"; - cout << endl; - // ProfilerStop(); - } + // void test_mcts_two_players::play() + // { + // // ProfilerStart("theturk.prof"); + // connect4 c4; + // auto the_turk = make_mcts_two_players(c4, 5000, 0.3, 4); + // cout << "play one game" << endl; + // cout << "who's first? (h)uman/(c)omputer "; + // string ans; + // getline(cin, ans); + // cout << c4 << endl; + // int human_last_move = -1, computer_last_move = -1; + // while (!c4.end_of_game()) + // { + // if ((ans == "h" && c4.current_player() == 0) || (ans == "c" && c4.current_player() == 1)) + // { + // human_last_move = select_move(c4); + // } + // else + // { + // if (human_last_move != -1 && computer_last_move != -1) + // { + // the_turk.last_moves(computer_last_move, human_last_move); + // } + // uint16_t move = the_turk.select_move(); + // computer_last_move = move; + // cout << c4.player_to_string(c4.current_player()) << " move: " << c4.move_to_string(move) << endl; + // c4.play(move); + // } + // cout << c4 << endl; + // } + // if (c4.value(0) == 1) cout << c4.player_to_string(0) << " won"; + // else if (c4.value(1) == 1) cout << c4.player_to_string(1) << " won"; + // else cout << "draw"; + // cout << endl; + // // ProfilerStop(); + // } - void test_mcts_two_players::self_play_learn_openings(int n) - { - morpion mor; - vector<uint16_t> moves(200); - auto state = mor.get_state(); - auto the_turk_1 = make_mcts_two_players(mor, 1000, 0.6, 2); - auto the_turk_2 = make_mcts_two_players(mor, 1000, 0.6, 2); - map<set<int>, pair<uint32_t, double>> learning_examples; - for (int i = 0; i < n; ++i) - { - cout << i << endl; - cout << openings_ << endl << endl; - moves.clear(); - mor.set_state(state); - the_turk_1.reset(); - the_turk_1.init_with_openings(openings_); - the_turk_2.reset(); - the_turk_2.init_with_openings(openings_); - int the_turk_1_last_move = -1, the_turk_2_last_move = -1; - int k = 0; - while (!mor.end_of_game()) - { - if (k == 1) the_turk_2.last_move(the_turk_1_last_move); - if (k % 2 == 0) - { - if (the_turk_2_last_move != -1) - { - the_turk_1.last_moves(the_turk_1_last_move, the_turk_2_last_move); - } - uint16_t move = the_turk_1.select_move(); - moves.push_back(move); - the_turk_1_last_move = move; - mor.play(move); - } - else - { - if (the_turk_2_last_move != -1) - { - the_turk_2.last_moves(the_turk_2_last_move, the_turk_1_last_move); - } - uint16_t move = the_turk_2.select_move(); - moves.push_back(move); - the_turk_2_last_move = move; - mor.play(move); - } - ++k; - } - std::cout << mor << std::endl; - int v = mor.value(0); - cout << "value for first player " << v << endl; - mor.set_state(state); - openings_.update(mor, moves, v); - mor.set_state(state); - for (uint16_t m : moves) - { - mor.play(m); - v = -v; - set<int> input_vector = std::move(mor.to_input_vector()); - auto it = learning_examples.find(input_vector); - if (it == learning_examples.end()) - { - learning_examples[input_vector] = make_pair(1, v); - } - else - { - it->second.second = (it->second.second * it->second.first + v) / (it->second.first + 1); - it->second.first += 1; - } - } - } - cout << "number of learning examples: " << learning_examples.size() << endl; - ofstream output("learning_examples.txt"); - for (const auto& example : learning_examples) - { - output << example.second.second; - for (int index : example.first) - { - output << " " << index << ":" << 1; - } - output << endl; - } - output.close(); - } + // void test_mcts_two_players::self_play_learn_openings(int n) + // { + // connect4 c4; + // vector<uint16_t> moves(200); + // auto state = c4.get_state(); + // auto the_turk_1 = make_mcts_two_players(c4, 1000, 0.6, 2); + // auto the_turk_2 = make_mcts_two_players(c4, 1000, 0.6, 2); + // map<set<int>, pair<uint32_t, double>> learning_examples; + // for (int i = 0; i < n; ++i) + // { + // cout << i << endl; + // cout << openings_ << endl << endl; + // moves.clear(); + // c4.set_state(state); + // the_turk_1.reset(); + // the_turk_1.init_with_openings(openings_); + // the_turk_2.reset(); + // the_turk_2.init_with_openings(openings_); + // int the_turk_1_last_move = -1, the_turk_2_last_move = -1; + // int k = 0; + // while (!c4.end_of_game()) + // { + // if (k == 1) the_turk_2.last_move(the_turk_1_last_move); + // if (k % 2 == 0) + // { + // if (the_turk_2_last_move != -1) + // { + // the_turk_1.last_moves(the_turk_1_last_move, the_turk_2_last_move); + // } + // uint16_t move = the_turk_1.select_move(); + // moves.push_back(move); + // the_turk_1_last_move = move; + // c4.play(move); + // } + // else + // { + // if (the_turk_2_last_move != -1) + // { + // the_turk_2.last_moves(the_turk_2_last_move, the_turk_1_last_move); + // } + // uint16_t move = the_turk_2.select_move(); + // moves.push_back(move); + // the_turk_2_last_move = move; + // c4.play(move); + // } + // ++k; + // } + // std::cout << c4 << std::endl; + // int v = c4.value(0); + // cout << "value for first player " << v << endl; + // c4.set_state(state); + // openings_.update(c4, moves, v); + // c4.set_state(state); + // for (uint16_t m : moves) + // { + // c4.play(m); + // v = -v; + // set<int> input_vector = std::move(c4.to_input_vector()); + // auto it = learning_examples.find(input_vector); + // if (it == learning_examples.end()) + // { + // learning_examples[input_vector] = make_pair(1, v); + // } + // else + // { + // it->second.second = (it->second.second * it->second.first + v) / (it->second.first + 1); + // it->second.first += 1; + // } + // } + // } + // cout << "number of learning examples: " << learning_examples.size() << endl; + // ofstream output("learning_examples.txt"); + // for (const auto& example : learning_examples) + // { + // output << example.second.second; + // for (int index : example.first) + // { + // output << " " << index << ":" << 1; + // } + // output << endl; + // } + // output.close(); + // } - void test_mcts_two_players::self_play(int n, bool with_openings) - { - morpion mor; - auto state = mor.get_state(); - auto the_turk_v1 = make_mcts_two_players(mor, 1000, 0.6, 2); - auto the_turk_v2 = make_mcts_two_players(mor, 1000, 0.6, 2); - int nb_win_v1 = 0, nb_win_v2 = 0, nb_draw = 0; - for (int i = 0; i < n; ++i) - { - cout << i << endl; - mor.set_state(state); - the_turk_v1.reset(); - the_turk_v2.reset(); - if (with_openings) the_turk_v2.init_with_openings(openings_); - int the_turk_v1_last_move = -1, the_turk_v2_last_move = -1; - int k = 0; - while (!mor.end_of_game()) - { - if (with_openings && k == 1 && i % 2 == 0) the_turk_v2.last_move(the_turk_v1_last_move); - ++k; - // cout << mor << endl; - if ((i % 2 == 0 && mor.current_player() == 0) || (i % 2 == 1 && mor.current_player() == 1)) - { - if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) - { - the_turk_v1.last_moves(the_turk_v1_last_move, the_turk_v2_last_move); - } - uint16_t move = the_turk_v1.select_move(); - the_turk_v1_last_move = move; - mor.play(move); - } - else - { - if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) - { - the_turk_v2.last_moves(the_turk_v2_last_move, the_turk_v1_last_move); - } - uint16_t move = the_turk_v2.select_move(); - the_turk_v2_last_move = move; - mor.play(move); - } - } - if (mor.value(0) == 1) - { - if (i % 2 == 0) - { - /*cout << "v1 won" << endl;*/ ++nb_win_v1; - } - else - { - /*cout << "v2 won" << endl;*/ ++nb_win_v2; - } - } - else if (mor.value(1) == 1) - { - if (i % 2 == 0) - { - /*cout << "v2 won" << endl;*/ ++nb_win_v2; - } - else - { - /*cout << "v1 won" << endl;*/ ++nb_win_v1; - } - } - else - { - /*cout << "draw" << endl;*/ ++nb_draw; - } - cout << setw(10) << "v1 nb wins: " << nb_win_v1 << " v2 nb wins: " << nb_win_v2 << " nb draws: " << nb_draw << endl; - } - } + // void test_mcts_two_players::self_play(int n, bool with_openings) + // { + // connect4 c4; + // auto state = c4.get_state(); + // auto the_turk_v1 = make_mcts_two_players(c4, 1000, 0.6, 2); + // auto the_turk_v2 = make_mcts_two_players(c4, 1000, 0.6, 2); + // int nb_win_v1 = 0, nb_win_v2 = 0, nb_draw = 0; + // for (int i = 0; i < n; ++i) + // { + // cout << i << endl; + // c4.set_state(state); + // the_turk_v1.reset(); + // the_turk_v2.reset(); + // if (with_openings) the_turk_v2.init_with_openings(openings_); + // int the_turk_v1_last_move = -1, the_turk_v2_last_move = -1; + // int k = 0; + // while (!c4.end_of_game()) + // { + // if (with_openings && k == 1 && i % 2 == 0) the_turk_v2.last_move(the_turk_v1_last_move); + // ++k; + // // cout << c4 << endl; + // if ((i % 2 == 0 && c4.current_player() == 0) || (i % 2 == 1 && c4.current_player() == 1)) + // { + // if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) + // { + // the_turk_v1.last_moves(the_turk_v1_last_move, the_turk_v2_last_move); + // } + // uint16_t move = the_turk_v1.select_move(); + // the_turk_v1_last_move = move; + // c4.play(move); + // } + // else + // { + // if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) + // { + // the_turk_v2.last_moves(the_turk_v2_last_move, the_turk_v1_last_move); + // } + // uint16_t move = the_turk_v2.select_move(); + // the_turk_v2_last_move = move; + // c4.play(move); + // } + // } + // if (c4.value(0) == 1) + // { + // if (i % 2 == 0) + // { + // /*cout << "v1 won" << endl;*/ ++nb_win_v1; + // } + // else + // { + // /*cout << "v2 won" << endl;*/ ++nb_win_v2; + // } + // } + // else if (c4.value(1) == 1) + // { + // if (i % 2 == 0) + // { + // /*cout << "v2 won" << endl;*/ ++nb_win_v2; + // } + // else + // { + // /*cout << "v1 won" << endl;*/ ++nb_win_v1; + // } + // } + // else + // { + // /*cout << "draw" << endl;*/ ++nb_draw; + // } + // cout << setw(10) << "v1 nb wins: " << nb_win_v1 << " v2 nb wins: " << nb_win_v2 << " nb draws: " << nb_draw << endl; + // } + // } - void test_mcts_two_players::self_play() - { - morpion mor; - auto the_turk_v1 = make_mcts_two_players(mor, 1000, 1.2, 2); - auto the_turk_v2 = make_mcts_two_players(mor, 1000, 1.2, 2); - cout << "play one game" << endl; - cout << "who's first? the_turk_(v1)/the_turk_(v2) "; - string ans; - getline(cin, ans); - cout << mor << endl; - int the_turk_v1_last_move = -1, the_turk_v2_last_move = -1; - while (!mor.end_of_game()) - { - if ((ans == "v1" && mor.current_player() == 0) || (ans == "v2" && mor.current_player() == 1)) - { - if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) - { - the_turk_v1.last_moves(the_turk_v1_last_move, the_turk_v2_last_move); - } - uint16_t move = the_turk_v1.select_move(); - the_turk_v1_last_move = move; - cout << mor.player_to_string(mor.current_player()) << " move: " << mor.move_to_string(move) << endl; - mor.play(move); - } - else - { - if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) - { - the_turk_v2.last_moves(the_turk_v2_last_move, the_turk_v1_last_move); - } - uint16_t move = the_turk_v2.select_move(); - the_turk_v2_last_move = move; - cout << mor.player_to_string(mor.current_player()) << " move: " << mor.move_to_string(move) << endl; - mor.play(move); - } - cout << mor << endl; - } - if (mor.value(0) == 1) cout << mor.player_to_string(0) << " won"; - else if (mor.value(1) == 1) cout << mor.player_to_string(1) << " won"; - else cout << "draw"; - cout << endl; - } + // void test_mcts_two_players::self_play() + // { + // connect4 c4; + // auto the_turk_v1 = make_mcts_two_players(c4, 1000, 1.2, 2); + // auto the_turk_v2 = make_mcts_two_players(c4, 1000, 1.2, 2); + // cout << "play one game" << endl; + // cout << "who's first? the_turk_(v1)/the_turk_(v2) "; + // string ans; + // getline(cin, ans); + // cout << c4 << endl; + // int the_turk_v1_last_move = -1, the_turk_v2_last_move = -1; + // while (!c4.end_of_game()) + // { + // if ((ans == "v1" && c4.current_player() == 0) || (ans == "v2" && c4.current_player() == 1)) + // { + // if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) + // { + // the_turk_v1.last_moves(the_turk_v1_last_move, the_turk_v2_last_move); + // } + // uint16_t move = the_turk_v1.select_move(); + // the_turk_v1_last_move = move; + // cout << c4.player_to_string(c4.current_player()) << " move: " << c4.move_to_string(move) << endl; + // c4.play(move); + // } + // else + // { + // if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) + // { + // the_turk_v2.last_moves(the_turk_v2_last_move, the_turk_v1_last_move); + // } + // uint16_t move = the_turk_v2.select_move(); + // the_turk_v2_last_move = move; + // cout << c4.player_to_string(c4.current_player()) << " move: " << c4.move_to_string(move) << endl; + // c4.play(move); + // } + // cout << c4 << endl; + // } + // if (c4.value(0) == 1) cout << c4.player_to_string(0) << " won"; + // else if (c4.value(1) == 1) cout << c4.player_to_string(1) << " won"; + // else cout << "draw"; + // cout << endl; + // } } diff --git a/src/mcts/test_mcts_two_players.hpp b/src/mcts/test_mcts_two_players.hpp index c04f71f676f0036ac61d1194924437e02baa757c..e8c10331fc13e2409528acbebbcae0ff5c25c894 100644 --- a/src/mcts/test_mcts_two_players.hpp +++ b/src/mcts/test_mcts_two_players.hpp @@ -2,24 +2,305 @@ #define __TEST_MCTS_TWO_PLAYERS_HPP__ #include "openings.hpp" +#include "mcts_two_players.hpp" +#include <string> +#include <iostream> +#include <map> +#include <iomanip> +#include <set> +#include <fstream> namespace mcts { + template <typename Game> class test_mcts_two_players { openings openings_; - void play(); - void self_play(); - void self_play(int n, bool with_openings = false); - void self_play_learn_openings(int n); - void test_openings(int nb_learning, int nb_testing); - - template <typename Game> + void play(Game g); + void self_play(Game g); + void self_play(Game g, int n, bool with_openings = false); + void self_play_learn_openings(Game g, int n); + void test_openings(Game g, int nb_learning, int nb_testing); int select_move(Game& game); public: - test_mcts_two_players(); + test_mcts_two_players(const Game& g); }; + + template <typename Game> + test_mcts_two_players<Game>::test_mcts_two_players(const Game& g) : openings_(g) + { + //self_play(g, 1000); + play(g); + //test_openings(g, 10000, 1000); + } + + template <typename Game> + void run_test_mcts_two_players(const Game& g) + { + test_mcts_two_players<Game>{g}; + } + + + template <typename Game> + void test_mcts_two_players<Game>::test_openings(Game g, int nb_learning, int nb_testing) + { + // self_play(g, nb_testing); + self_play_learn_openings(g, nb_learning); + self_play(g, nb_testing, true); + } + + template <typename Game> + int test_mcts_two_players<Game>::select_move(Game& game) + { + std::cout << game.player_to_string(game.current_player()) << " move: "; + std::map<std::string, int> m; + for (int i = 0; i < game.number_of_moves(); ++i) + { + m[game.move_to_string(i)] = i; + } + std::string move; + getline(std::cin, move); + game.play(m[move]); + return m[move]; + } + + template <typename Game> + void test_mcts_two_players<Game>::play(Game g) + { + // ProfilerStart("theturk.prof"); + auto the_turk = make_mcts_two_players(g, 5000, 0.3, 4); + std::cout << "play one game" << std::endl; + std::cout << "who's first? (h)uman/(c)omputer "; + std::string ans; + getline(std::cin, ans); + std::cout << g.to_string() << std::endl; + int human_last_move = -1, computer_last_move = -1; + while (!g.end_of_game()) + { + if ((ans == "h" && g.current_player() == 0) || (ans == "c" && g.current_player() == 1)) + { + human_last_move = select_move(g); + } + else + { + if (human_last_move != -1 && computer_last_move != -1) + { + the_turk.last_moves(computer_last_move, human_last_move); + } + uint16_t move = the_turk.select_move(); + computer_last_move = move; + std::cout << g.player_to_string(g.current_player()) << " move: " << g.move_to_string(move) << std::endl; + g.play(move); + } + std::cout << g << 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; + // ProfilerStop(); + } + + template <typename Game> + void test_mcts_two_players<Game>::self_play_learn_openings(Game g, int n) + { + std::vector<uint16_t> moves(200); + auto state = g.get_state(); + auto the_turk_1 = make_mcts_two_players(g, 1000, 0.6, 2); + auto the_turk_2 = make_mcts_two_players(g, 1000, 0.6, 2); + std::map<std::set<int>, std::pair<std::uint32_t, double>> learning_examples; + for (int i = 0; i < n; ++i) + { + std::cout << i << std::endl; + std::cout << openings_ << std::endl << std::endl; + moves.clear(); + g.set_state(state); + the_turk_1.reset(); + the_turk_1.init_with_openings(openings_); + the_turk_2.reset(); + the_turk_2.init_with_openings(openings_); + int the_turk_1_last_move = -1, the_turk_2_last_move = -1; + int k = 0; + while (!g.end_of_game()) + { + if (k == 1) the_turk_2.last_move(the_turk_1_last_move); + if (k % 2 == 0) + { + if (the_turk_2_last_move != -1) + { + the_turk_1.last_moves(the_turk_1_last_move, the_turk_2_last_move); + } + std::uint16_t move = the_turk_1.select_move(); + moves.push_back(move); + the_turk_1_last_move = move; + g.play(move); + } + else + { + if (the_turk_2_last_move != -1) + { + the_turk_2.last_moves(the_turk_2_last_move, the_turk_1_last_move); + } + std::uint16_t move = the_turk_2.select_move(); + moves.push_back(move); + the_turk_2_last_move = move; + g.play(move); + } + ++k; + } + std::cout << g.to_string() << std::endl; + int v = g.value(0); + std::cout << "value for first player " << v << std::endl; + g.set_state(state); + openings_.update(g, moves, v); + g.set_state(state); + for (std::uint16_t m : moves) + { + g.play(m); + v = -v; + std::set<int> input_vector = std::move(g.to_input_vector()); + auto it = learning_examples.find(input_vector); + if (it == learning_examples.end()) + { + learning_examples[input_vector] = std::make_pair(1, v); + } + else + { + it->second.second = (it->second.second * it->second.first + v) / (it->second.first + 1); + it->second.first += 1; + } + } + } + std::cout << "number of learning examples: " << learning_examples.size() << std::endl; + std::ofstream output("learning_examples.txt"); + for (const auto& example : learning_examples) + { + output << example.second.second; + for (int index : example.first) + { + output << " " << index << ":" << 1; + } + output << std::endl; + } + output.close(); + } + + template <typename Game> + void test_mcts_two_players<Game>::self_play(Game g, int n, bool with_openings) + { + auto state = g.get_state(); + auto the_turk_v1 = make_mcts_two_players(g, 1000, 0.6, 2); + auto the_turk_v2 = make_mcts_two_players(g, 1000, 0.6, 2); + int nb_win_v1 = 0, nb_win_v2 = 0, nb_draw = 0; + for (int i = 0; i < n; ++i) + { + std::cout << i << std::endl; + g.set_state(state); + the_turk_v1.reset(); + the_turk_v2.reset(); + if (with_openings) the_turk_v2.init_with_openings(openings_); + int the_turk_v1_last_move = -1, the_turk_v2_last_move = -1; + int k = 0; + while (!g.end_of_game()) + { + if (with_openings && k == 1 && i % 2 == 0) the_turk_v2.last_move(the_turk_v1_last_move); + ++k; + // cout << c4 << endl; + if ((i % 2 == 0 && g.current_player() == 0) || (i % 2 == 1 && g.current_player() == 1)) + { + if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) + { + the_turk_v1.last_moves(the_turk_v1_last_move, the_turk_v2_last_move); + } + std::uint16_t move = the_turk_v1.select_move(); + the_turk_v1_last_move = move; + g.play(move); + } + else + { + if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) + { + the_turk_v2.last_moves(the_turk_v2_last_move, the_turk_v1_last_move); + } + std::uint16_t move = the_turk_v2.select_move(); + the_turk_v2_last_move = move; + play(move); + } + } + if (g.won(0)) + { + if (i % 2 == 0) + { + /*cout << "v1 won" << endl;*/ ++nb_win_v1; + } + else + { + /*cout << "v2 won" << endl;*/ ++nb_win_v2; + } + } + else if (g.won(1)) + { + if (i % 2 == 0) + { + /*cout << "v2 won" << endl;*/ ++nb_win_v2; + } + else + { + /*cout << "v1 won" << endl;*/ ++nb_win_v1; + } + } + else + { + /*cout << "draw" << endl;*/ ++nb_draw; + } + std::cout << std::setw(10) << "v1 nb wins: " << nb_win_v1 << " v2 nb wins: " << nb_win_v2 << " nb draws: " << nb_draw << std::endl; + } + } + + template <typename Game> + void test_mcts_two_players<Game>::self_play(Game g) + { + auto the_turk_v1 = make_mcts_two_players(g, 1000, 1.2, 2); + auto the_turk_v2 = make_mcts_two_players(g, 1000, 1.2, 2); + std::cout << "play one game" << std::endl; + std::cout << "who's first? the_turk_(v1)/the_turk_(v2) "; + std::string ans; + getline(std::cin, ans); + std::cout << g << std::endl; + int the_turk_v1_last_move = -1, the_turk_v2_last_move = -1; + while (!g.end_of_game()) + { + if ((ans == "v1" && g.current_player() == 0) || (ans == "v2" && g.current_player() == 1)) + { + if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) + { + the_turk_v1.last_moves(the_turk_v1_last_move, the_turk_v2_last_move); + } + std::uint16_t move = the_turk_v1.select_move(); + the_turk_v1_last_move = move; + std::cout << g.player_to_string(g.current_player()) << " move: " << g.move_to_string(move) << std::endl; + g.play(move); + } + else + { + if (the_turk_v1_last_move != -1 && the_turk_v2_last_move != -1) + { + the_turk_v2.last_moves(the_turk_v2_last_move, the_turk_v1_last_move); + } + std::uint16_t move = the_turk_v2.select_move(); + the_turk_v2_last_move = move; + std::cout << g.player_to_string(g.current_player()) << " move: " << g.move_to_string(move) << std::endl; + g.play(move); + } + std::cout << g << 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