diff --git a/AI/src/game/penguin.cpp b/AI/src/game/penguin.cpp index 29c6f7478ab4d69608a0b480c93187c903c24523..4eabcfcb948900bd2bdf54a779a8dcef4b71314d 100644 --- a/AI/src/game/penguin.cpp +++ b/AI/src/game/penguin.cpp @@ -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 @@ -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; diff --git a/AI/src/game/penguin.hpp b/AI/src/game/penguin.hpp index e1364fc8ffec7d84809fb8fba33859fbeea41855..50d2e91fe784e064bbdf828339b4d7dfd57b1193 100644 --- a/AI/src/game/penguin.hpp +++ b/AI/src/game/penguin.hpp @@ -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); }