diff --git a/chess_engine.cpp b/chess_engine.cpp new file mode 100644 index 0000000..fab2ece --- /dev/null +++ b/chess_engine.cpp @@ -0,0 +1,79 @@ +#include +#include +#include "tables.h" +#include "position.h" +#include "types.h" + + +//Computes the perft of the position for a given depth, using bulk-counting +//According to the https://www.chessprogramming.org/Perft site: +//Perft is a debugging function to walk the move generation tree of strictly legal moves to count +//all the leaf nodes of a certain depth, which can be compared to predetermined values and used to isolate bugs +template +unsigned long long perft(Position& p, unsigned int depth) { + //gk int nmoves; + unsigned long long nodes = 0; + + MoveList list(p); + + if (depth == 1) return (unsigned long long) list.size(); + + for (Move move : list) { + p.play(move); + nodes += perft<~Us>(p, depth - 1); + p.undo(move); + } + + return nodes; +} + +//A variant of perft, listing all moves and for each move, the perft of the decremented depth +//It is used solely for debugging +template +void perftdiv(Position& p, unsigned int depth) { + unsigned long long nodes = 0, pf; + + MoveList list(p); + + for (Move move : list) { + std::cout << move; + + p.play(move); + pf = perft<~Us>(p, depth - 1); + std::cout << ": " << pf << " moves\n"; + nodes += pf; + p.undo(move); + } + + std::cout << "\nTotal: " << nodes << " moves\n"; +} + +void test_perft() { + Position p; + //gk Position::set("rnbqkbnr/pppppppp/8/8/8/8/PPPP1PPP/RNBQKBNR w KQkq -", p); + Position::set("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq -", p); + std::cout << p; + + std::chrono::steady_clock::time_point begin = std::chrono::steady_clock::now(); + auto n = perft(p, 6); + std::chrono::steady_clock::time_point end = std::chrono::steady_clock::now(); + auto diff = end - begin; + + std::cout << "Nodes: " << n << "\n"; + std::cout << "NPS: " + << int(n * 1000000.0 / std::chrono::duration_cast(diff).count()) + << "\n"; + std::cout << "Time difference = " + << std::chrono::duration_cast(diff).count() << " [microseconds]\n"; +} + +int main() { + //Make sure to initialise all databases before using the library! + initialise_all_databases(); + zobrist::initialise_zobrist_keys(); + + //gk call test_perft() + test_perft(); + + return 0; +} diff --git a/src/chess_engine.cpp b/src/chess_engine.cpp index a9ca8c0..c6b92d7 100644 --- a/src/chess_engine.cpp +++ b/src/chess_engine.cpp @@ -4,6 +4,7 @@ #include "position.h" #include "types.h" +using namespace surge; //Computes the perft of the position for a given depth, using bulk-counting //According to the https://www.chessprogramming.org/Perft site: @@ -11,7 +12,7 @@ //all the leaf nodes of a certain depth, which can be compared to predetermined values and used to isolate bugs template unsigned long long perft(Position& p, unsigned int depth) { - int nmoves; + //gk int nmoves; unsigned long long nodes = 0; MoveList list(p); @@ -70,6 +71,9 @@ int main() { //Make sure to initialise all databases before using the library! initialise_all_databases(); zobrist::initialise_zobrist_keys(); + + //gk call test_perft() + test_perft(); return 0; } diff --git a/src/position.cpp b/src/position.cpp index 3bfd2d2..c3327cd 100644 --- a/src/position.cpp +++ b/src/position.cpp @@ -2,6 +2,8 @@ #include "tables.h" #include +namespace surge { + //Zobrist keys for each piece and each square //Used to incrementally update the hash key of a position uint64_t zobrist::zobrist_table[NPIECES][NSQUARES]; @@ -9,8 +11,11 @@ uint64_t zobrist::zobrist_table[NPIECES][NSQUARES]; //Initializes the zobrist table with random 64-bit numbers void zobrist::initialise_zobrist_keys() { PRNG rng(70026072); - for (int i = 0; i < NPIECES; i++) - for (int j = 0; j < NSQUARES; j++) + //gk comparison of integer expressions of different signedness + //gk for (int i = 0; i < NPIECES; i++) + //gk for (int j = 0; j < NSQUARES; j++) + for (size_t i = 0; i < NPIECES; i++) + for (size_t j = 0; j < NSQUARES; j++) zobrist::zobrist_table[i][j] = rng.rand(); } @@ -123,4 +128,4 @@ void Position::move_piece_quiet(Square from, Square to) { board[from] = NO_PIECE; } - +} // namespace surge \ No newline at end of file diff --git a/src/position.h b/src/position.h index cd43bfd..16746bc 100644 --- a/src/position.h +++ b/src/position.h @@ -6,6 +6,8 @@ #include "tables.h" #include +namespace surge { + //A psuedorandom number generator //Source: Stockfish class PRNG { @@ -85,8 +87,11 @@ class Position { Bitboard pinned; - Position() : piece_bb{ 0 }, side_to_play(WHITE), game_ply(0), board{}, - hash(0), pinned(0), checkers(0) { +//gk adapted order of initialization +//gk Position() : piece_bb{ 0 }, side_to_play(WHITE), game_ply(0), board{}, +//gk hash(0), pinned(0), checkers(0) { + Position() : piece_bb{ 0 }, board{}, side_to_play(WHITE), game_ply(0), + hash(0), checkers(0), pinned(0) { //Sets all squares on the board as empty for (int i = 0; i < 64; i++) board[i] = NO_PIECE; @@ -421,13 +426,15 @@ Move* Position::generate_legals(Move* list) { //Checkers of each piece type are identified by: //1. Projecting attacks FROM the king square //2. Intersecting this bitboard with the enemy bitboard of that piece type - checkers = attacks(our_king, all) & bitboard_of(Them, KNIGHT) - | pawn_attacks(our_king) & bitboard_of(Them, PAWN); + //gk additional parentheses + checkers = (attacks(our_king, all) & bitboard_of(Them, KNIGHT)) + | (pawn_attacks(our_king) & bitboard_of(Them, PAWN)); //Here, we identify slider checkers and pinners simultaneously, and candidates for such pinners //and checkers are represented by the bitboard - Bitboard candidates = attacks(our_king, them_bb) & their_orth_sliders - | attacks(our_king, them_bb) & their_diag_sliders; + //gk additional parentheses + Bitboard candidates = (attacks(our_king, them_bb) & their_orth_sliders) + | (attacks(our_king, them_bb) & their_diag_sliders); pinned = 0; while (candidates) { @@ -438,7 +445,8 @@ Move* Position::generate_legals(Move* list) { //If not, add the slider to the checker bitboard if (b1 == 0) checkers ^= SQUARE_BB[s]; //If there is only one of our pieces between them, add our piece to the pinned bitboard - else if ((b1 & b1 - 1) == 0) pinned ^= b1; + //gk additional parentheses + else if ((b1 & (b1 - 1)) == 0) pinned ^= b1; } //This makes it easier to mask pieces @@ -698,3 +706,5 @@ class MoveList { Move list[218]; Move *last; }; + +} // namespace surge \ No newline at end of file diff --git a/src/tables.cpp b/src/tables.cpp index 17fac6a..5e00a7c 100644 --- a/src/tables.cpp +++ b/src/tables.cpp @@ -1,6 +1,9 @@ #include "tables.h" #include "types.h" #include +#include //gk memcpy() + +namespace surge { //All piece tables are generated from a program written in Java @@ -88,10 +91,11 @@ const Bitboard BLACK_PAWN_ATTACKS[64] = { //Reverses a bitboard Bitboard reverse(Bitboard b) { - b = (b & 0x5555555555555555) << 1 | (b >> 1) & 0x5555555555555555; - b = (b & 0x3333333333333333) << 2 | (b >> 2) & 0x3333333333333333; - b = (b & 0x0f0f0f0f0f0f0f0f) << 4 | (b >> 4) & 0x0f0f0f0f0f0f0f0f; - b = (b & 0x00ff00ff00ff00ff) << 8 | (b >> 8) & 0x00ff00ff00ff00ff; + //gk additional parentheses around arithmetic in operand of ‘|’ + b = (b & 0x5555555555555555) << 1 | ((b >> 1) & 0x5555555555555555); + b = (b & 0x3333333333333333) << 2 | ((b >> 2) & 0x3333333333333333); + b = (b & 0x0f0f0f0f0f0f0f0f) << 4 | ((b >> 4) & 0x0f0f0f0f0f0f0f0f); + b = (b & 0x00ff00ff00ff00ff) << 8 | ((b >> 8) & 0x00ff00ff00ff00ff); return (b << 48) | ((b & 0xffff0000) << 16) | ((b >> 16) & 0xffff0000) | (b >> 48); @@ -157,7 +161,8 @@ void initialise_rook_attacks() { } //Returns the attacks bitboard for a rook at a given square, using the magic lookup table -constexpr Bitboard get_rook_attacks(Square square, Bitboard occ) { +//gk constexpr Bitboard get_rook_attacks(Square square, Bitboard occ) { +Bitboard get_rook_attacks(Square square, Bitboard occ) { return ROOK_ATTACKS[square][((occ & ROOK_ATTACK_MASKS[square]) * ROOK_MAGICS[square]) >> ROOK_ATTACK_SHIFTS[square]]; } @@ -223,7 +228,8 @@ void initialise_bishop_attacks() { } //Returns the attacks bitboard for a bishop at a given square, using the magic lookup table -constexpr Bitboard get_bishop_attacks(Square square, Bitboard occ) { +//gk constexpr Bitboard get_bishop_attacks(Square square, Bitboard occ) { +Bitboard get_bishop_attacks(Square square, Bitboard occ) { return BISHOP_ATTACKS[square][((occ & BISHOP_ATTACK_MASKS[square]) * BISHOP_MAGICS[square]) >> BISHOP_ATTACK_SHIFTS[square]]; } @@ -265,11 +271,13 @@ void initialise_line() { for (Square sq2 = a1; sq2 <= h8; ++sq2) { if (file_of(sq1) == file_of(sq2) || rank_of(sq1) == rank_of(sq2)) LINE[sq1][sq2] = - get_rook_attacks_for_init(sq1, 0) & get_rook_attacks_for_init(sq2, 0) + //gk additional parentheses + (get_rook_attacks_for_init(sq1, 0) & get_rook_attacks_for_init(sq2, 0)) | SQUARE_BB[sq1] | SQUARE_BB[sq2]; else if (diagonal_of(sq1) == diagonal_of(sq2) || anti_diagonal_of(sq1) == anti_diagonal_of(sq2)) LINE[sq1][sq2] = - get_bishop_attacks_for_init(sq1, 0) & get_bishop_attacks_for_init(sq2, 0) + //gk additional parentheses + (get_bishop_attacks_for_init(sq1, 0) & get_bishop_attacks_for_init(sq2, 0)) | SQUARE_BB[sq1] | SQUARE_BB[sq2]; } } @@ -301,3 +309,5 @@ void initialise_all_databases() { initialise_line(); initialise_pseudo_legal(); } + +} // namespace surge \ No newline at end of file diff --git a/src/tables.h b/src/tables.h index 2bfe44d..62a18e7 100644 --- a/src/tables.h +++ b/src/tables.h @@ -2,6 +2,8 @@ #include "types.h" +namespace surge { + extern const Bitboard KING_ATTACKS[NSQUARES]; extern const Bitboard KNIGHT_ATTACKS[NSQUARES]; extern const Bitboard WHITE_PAWN_ATTACKS[NSQUARES]; @@ -18,7 +20,8 @@ extern Bitboard ROOK_ATTACKS[NSQUARES][4096]; extern void initialise_rook_attacks(); -extern constexpr Bitboard get_rook_attacks(Square square, Bitboard occ); +//gk extern constexpr Bitboard get_rook_attacks(Square square, Bitboard occ); +extern Bitboard get_rook_attacks(Square square, Bitboard occ); extern Bitboard get_xray_rook_attacks(Square square, Bitboard occ, Bitboard blockers); extern Bitboard get_bishop_attacks_for_init(Square square, Bitboard occ); @@ -29,7 +32,8 @@ extern Bitboard BISHOP_ATTACKS[NSQUARES][512]; extern void initialise_bishop_attacks(); -extern constexpr Bitboard get_bishop_attacks(Square square, Bitboard occ); +//gk extern constexpr Bitboard get_bishop_attacks(Square square, Bitboard occ); +extern Bitboard get_bishop_attacks(Square square, Bitboard occ); extern Bitboard get_xray_bishop_attacks(Square square, Bitboard occ, Bitboard blockers); extern Bitboard SQUARES_BETWEEN_BB[NSQUARES][NSQUARES]; @@ -55,6 +59,7 @@ constexpr Bitboard attacks(Square s, Bitboard occ) { //Returns a bitboard containing all squares that a piece on a square can move to, in the given position //Used when the piece type is not known at compile-time +//gk (needs at least c++14) constexpr Bitboard attacks(PieceType pt, Square s, Bitboard occ) { switch (pt) { case ROOK: @@ -80,3 +85,5 @@ template constexpr Bitboard pawn_attacks(Square s) { return PAWN_ATTACKS[C][s]; } + +} // namespace surge \ No newline at end of file diff --git a/src/types.cpp b/src/types.cpp index cc2ff96..3259f68 100644 --- a/src/types.cpp +++ b/src/types.cpp @@ -1,6 +1,8 @@ #include "types.h" #include +namespace surge { + //Lookup tables of square names in algebraic chess notation const char* SQSTR[65] = { "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", @@ -83,7 +85,8 @@ const Bitboard k4 = 0x0f0f0f0f0f0f0f0f; const Bitboard kf = 0x0101010101010101; //Returns number of set bits in the bitboard -inline int pop_count(Bitboard x) { +//gk inline int pop_count(Bitboard x) { +int pop_count(Bitboard x) { x = x - ((x >> 1) & k1); x = (x & k2) + ((x >> 2) & k2); x = (x + (x >> 4)) & k4; @@ -92,7 +95,8 @@ inline int pop_count(Bitboard x) { } //Returns number of set bits in the bitboard. Faster than pop_count(x) when the bitboard has few set bits -inline int sparse_pop_count(Bitboard x) { +//gk inline int sparse_pop_count(Bitboard x) { +int sparse_pop_count(Bitboard x) { int count = 0; while (x) { count++; @@ -101,7 +105,8 @@ inline int sparse_pop_count(Bitboard x) { return count; } -const int DEBRUIJN64[64] = { +//gk const int DEBRUIJN64[64] = { +constexpr int DEBRUIJN64[64] = { 0, 47, 1, 56, 48, 27, 2, 60, 57, 49, 41, 37, 28, 16, 3, 61, 54, 58, 35, 52, 50, 42, 21, 44, @@ -114,18 +119,21 @@ const int DEBRUIJN64[64] = { const Bitboard MAGIC = 0x03f79d71b4cb0a89; +//Returns the index of the least significant bit in the bitboard +//constexpr Square bsf(Bitboard b) { +Square bsf(Bitboard b) { + return Square(DEBRUIJN64[MAGIC * (b ^ (b - 1)) >> 58]); +} + //Returns the index of the least significant bit in the bitboard, and removes the bit from the bitboard -inline Square pop_lsb(Bitboard* b) { +//gk should be taken to the include file when declared "inline" +//gk inline Square pop_lsb(Bitboard* b) { +Square pop_lsb(Bitboard* b) { int lsb = bsf(*b); *b &= *b - 1; return Square(lsb); } -//Returns the index of the least significant bit in the bitboard -constexpr Square bsf(Bitboard b) { - return Square(DEBRUIJN64[MAGIC * (b ^ (b - 1)) >> 58]); -} - //Returns the representation of the move type in algebraic chess notation. (capture) is used for debugging const char* MOVE_TYPESTR[16] = { "", "", " O-O", " O-O-O", "N", "B", "R", "Q", " (capture)", "", " e.p.", "", @@ -139,3 +147,4 @@ std::ostream& operator<<(std::ostream& os, const Move& m) { return os; } +} // namespace surge \ No newline at end of file diff --git a/src/types.h b/src/types.h index 973d6ca..722d52d 100644 --- a/src/types.h +++ b/src/types.h @@ -7,6 +7,8 @@ #include #include +namespace surge { + const size_t NCOLORS = 2; enum Color : int { WHITE, BLACK @@ -104,13 +106,17 @@ extern const Bitboard k2; extern const Bitboard k4; extern const Bitboard kf; -extern inline int pop_count(Bitboard x); -extern inline int sparse_pop_count(Bitboard x); -extern inline Square pop_lsb(Bitboard* b); +//gk extern inline int pop_count(Bitboard x); +extern int pop_count(Bitboard x); +//gk extern inline int sparse_pop_count(Bitboard x); +extern int sparse_pop_count(Bitboard x); +//gk extern inline Square pop_lsb(Bitboard* b); +extern Square pop_lsb(Bitboard* b); extern const int DEBRUIJN64[64]; extern const Bitboard MAGIC; -extern constexpr Square bsf(Bitboard b); +//gk extern constexpr Square bsf(Bitboard b); +extern Square bsf(Bitboard b); constexpr Rank rank_of(Square s) { return Rank(s >> 3); } constexpr File file_of(Square s) { return File(s & 0b111); } @@ -269,3 +275,5 @@ constexpr Bitboard ooo_blockers_mask() { } template constexpr Bitboard ignore_ooo_danger() { return C == WHITE ? 0x2 : 0x200000000000000; } + +} // namespace surge \ No newline at end of file