Advertisement
philRG

Connect 4 - Gameplay with bitboard

May 26th, 2021
136
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 7.52 KB | None | 0 0
  1. /*
  2.  * This file is part of Connect4 Game Solver <http://connect4.gamesolver.org>
  3.  * Copyright (C) 2007 Pascal Pons <contact@gamesolver.org>
  4.  *
  5.  * Connect4 Game Solver is free software: you can redistribute it and/or
  6.  * modify it under the terms of the GNU Affero General Public License as
  7.  * published by the Free Software Foundation, either version 3 of the
  8.  * License, or (at your option) any later version.
  9.  *
  10.  * Connect4 Game Solver is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU Affero General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU Affero General Public License
  16.  * along with Connect4 Game Solver. If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. #ifndef POSITION_HPP
  20. #define POSITION_HPP
  21.  
  22. #include <string>
  23. #include <cstdint>
  24.  
  25. namespace GameSolver { namespace Connect4 {
  26.   /**
  27.    * A class storing a Connect 4 position.
  28.    * Functions are relative to the current player to play.
  29.    * Position containing aligment are not supported by this class.
  30.    *
  31.    * A binary bitboard representationis used.
  32.    * Each column is encoded on HEIGH+1 bits.
  33.      *
  34.    * Example of bit order to encode for a 7x6 board
  35.      * .  .  .  .  .  .  .
  36.      * 5 12 19 26 33 40 47
  37.      * 4 11 18 25 32 39 46
  38.      * 3 10 17 24 31 38 45
  39.      * 2  9 16 23 30 37 44
  40.      * 1  8 15 22 29 36 43
  41.      * 0  7 14 21 28 35 42
  42.      *
  43.    * Position is stored as
  44.    * - a bitboard "mask" with 1 on any color stones
  45.    * - a bitboard "current_player" with 1 on stones of current player
  46.    *
  47.    * "current_player" bitboard can be transformed into a compact and non ambiguous key
  48.    * by adding an extra bit on top of the last non empty cell of each column.
  49.    * This allow to identify all the empty cells whithout needing "mask" bitboard
  50.    *
  51.      * current_player "x" = 1, opponent "o" = 0
  52.      * board     position  mask      key       bottom
  53.      *           0000000   0000000   0000000   0000000
  54.      * .......   0000000   0000000   0001000   0000000
  55.      * ...o...   0000000   0001000   0010000   0000000
  56.      * ..xx...   0011000   0011000   0011000   0000000
  57.      * ..ox...   0001000   0011000   0001100   0000000
  58.      * ..oox..   0000100   0011100   0000110   0000000
  59.      * ..oxxo.   0001100   0011110   1101101   1111111
  60.      *
  61.      * current_player "o" = 1, opponent "x" = 0
  62.      * board     position  mask      key       bottom
  63.      *           0000000   0000000   0001000   0000000
  64.      * ...x...   0000000   0001000   0000000   0000000
  65.      * ...o...   0001000   0001000   0011000   0000000
  66.      * ..xx...   0000000   0011000   0000000   0000000
  67.      * ..ox...   0010000   0011000   0010100   0000000
  68.      * ..oox..   0011000   0011100   0011010   0000000
  69.      * ..oxxo.   0010010   0011110   1110011   1111111
  70.      *
  71.      * key is an unique representation of a board key = position + mask + bottom
  72.      * in practice, as bottom is constant, key = position + mask is also a
  73.    * non-ambigous representation of the position.
  74.      */
  75.   class Position {
  76.     public:
  77.  
  78.       static const int WIDTH = 7;  // width of the board
  79.       static const int HEIGHT = 6; // height of the board
  80.       static_assert(WIDTH < 10, "Board's width must be less than 10");
  81.       static_assert(WIDTH*(HEIGHT+1) <= 64, "Board does not fit in 64bits bitboard");
  82.  
  83.       /**
  84.        * Indicates whether a column is playable.
  85.        * @param col: 0-based index of column to play
  86.        * @return true if the column is playable, false if the column is already full.
  87.        */
  88.       bool canPlay(int col) const
  89.       {
  90.         return (mask & top_mask(col)) == 0;
  91.       }
  92.  
  93.       /**
  94.        * Plays a playable column.
  95.        * This function should not be called on a non-playable column or a column making an alignment.
  96.        *
  97.        * @param col: 0-based index of a playable column.
  98.        */
  99.       void play(int col)
  100.       {
  101.         current_position ^= mask;
  102.         mask |= mask + bottom_mask(col);
  103.         moves++;
  104.       }
  105.  
  106.       /*
  107.        * Plays a sequence of successive played columns, mainly used to initilize a board.
  108.        * @param seq: a sequence of digits corresponding to the 1-based index of the column played.
  109.        *
  110.        * @return number of played moves. Processing will stop at first invalid move that can be:
  111.        *           - invalid character (non digit, or digit >= WIDTH)
  112.        *           - playing a colum the is already full
  113.        *           - playing a column that makes an aligment (we only solve non).
  114.        *         Caller can check if the move sequence was valid by comparing the number of
  115.        *         processed moves to the length of the sequence.
  116.        */
  117.       unsigned int play(std::string seq)
  118.       {
  119.         for(unsigned int i = 0; i < seq.size(); i++) {
  120.           int col = seq[i] - '1';
  121.           if(col < 0 || col >= Position::WIDTH || !canPlay(col) || isWinningMove(col)) return i; // invalid move
  122.           play(col);
  123.         }
  124.         return seq.size();
  125.       }
  126.  
  127.       /**
  128.        * Indicates whether the current player wins by playing a given column.
  129.        * This function should never be called on a non-playable column.
  130.        * @param col: 0-based index of a playable column.
  131.        * @return true if current player makes an alignment by playing the corresponding column col.
  132.        */
  133.       bool isWinningMove(int col) const
  134.       {
  135.         uint64_t pos = current_position;
  136.         pos |= (mask + bottom_mask(col)) & column_mask(col);
  137.         return alignment(pos);
  138.       }
  139.  
  140.       /**    
  141.        * @return number of moves played from the beginning of the game.
  142.        */
  143.       unsigned int nbMoves() const
  144.       {
  145.         return moves;
  146.       }
  147.  
  148.       /**    
  149.        * @return a compact representation of a position on WIDTH*(HEIGHT+1) bits.
  150.        */
  151.       uint64_t key() const
  152.       {
  153.         return current_position + mask;
  154.       }
  155.  
  156.       /**
  157.        * Default constructor, build an empty position.
  158.        */
  159.       Position() : current_position{0}, mask{0}, moves{0} {}
  160.  
  161.  
  162.  
  163.     private:
  164.       uint64_t current_position;
  165.       uint64_t mask;
  166.       unsigned int moves; // number of moves played since the beinning of the game.
  167.  
  168.       /**
  169.        * Test an alignment for current player (identified by one in the bitboard pos)
  170.        * @param a bitboard position of a player's cells.
  171.        * @return true if the player has a 4-alignment.
  172.        */
  173.       static bool alignment(uint64_t pos) {
  174.         // horizontal
  175.         uint64_t m = pos & (pos >> (HEIGHT+1));
  176.         if(m & (m >> (2*(HEIGHT+1)))) return true;
  177.  
  178.         // diagonal 1
  179.         m = pos & (pos >> HEIGHT);
  180.         if(m & (m >> (2*HEIGHT))) return true;
  181.  
  182.         // diagonal 2
  183.         m = pos & (pos >> (HEIGHT+2));
  184.         if(m & (m >> (2*(HEIGHT+2)))) return true;
  185.  
  186.         // vertical;
  187.         m = pos & (pos >> 1);
  188.         if(m & (m >> 2)) return true;
  189.  
  190.         return false;
  191.       }
  192.  
  193.       // return a bitmask containg a single 1 corresponding to the top cel of a given column
  194.       static uint64_t top_mask(int col) {
  195.         return (UINT64_C(1) << (HEIGHT - 1)) << col*(HEIGHT+1);
  196.       }
  197.  
  198.       // return a bitmask containg a single 1 corresponding to the bottom cell of a given column
  199.       static uint64_t bottom_mask(int col) {
  200.         return UINT64_C(1) << col*(HEIGHT+1);
  201.       }
  202.  
  203.       // return a bitmask 1 on all the cells of a given column
  204.       static uint64_t column_mask(int col) {
  205.         return ((UINT64_C(1) << HEIGHT)-1) << col*(HEIGHT+1);
  206.       }
  207.  
  208.   };
  209.  
  210. }} // end namespaces
  211.  
  212. #endif
  213.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement