Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions chess_engine.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#include <iostream>
#include <chrono>
#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<Color Us>
unsigned long long perft(Position& p, unsigned int depth) {
//gk int nmoves;
unsigned long long nodes = 0;

MoveList<Us> list(p);

if (depth == 1) return (unsigned long long) list.size();

for (Move move : list) {
p.play<Us>(move);
nodes += perft<~Us>(p, depth - 1);
p.undo<Us>(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<Color Us>
void perftdiv(Position& p, unsigned int depth) {
unsigned long long nodes = 0, pf;

MoveList<Us> list(p);

for (Move move : list) {
std::cout << move;

p.play<Us>(move);
pf = perft<~Us>(p, depth - 1);
std::cout << ": " << pf << " moves\n";
nodes += pf;
p.undo<Us>(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<WHITE>(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<std::chrono::microseconds>(diff).count())
<< "\n";
std::cout << "Time difference = "
<< std::chrono::duration_cast<std::chrono::microseconds>(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;
}
6 changes: 5 additions & 1 deletion src/chess_engine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
#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:
//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<Color Us>
unsigned long long perft(Position& p, unsigned int depth) {
int nmoves;
//gk int nmoves;
unsigned long long nodes = 0;

MoveList<Us> list(p);
Expand Down Expand Up @@ -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;
}
11 changes: 8 additions & 3 deletions src/position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,20 @@
#include "tables.h"
#include <sstream>

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];

//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<uint64_t>();
}

Expand Down Expand Up @@ -123,4 +128,4 @@ void Position::move_piece_quiet(Square from, Square to) {
board[from] = NO_PIECE;
}

} // namespace surge
24 changes: 17 additions & 7 deletions src/position.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
#include "tables.h"
#include <utility>

namespace surge {

//A psuedorandom number generator
//Source: Stockfish
class PRNG {
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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<KNIGHT>(our_king, all) & bitboard_of(Them, KNIGHT)
| pawn_attacks<Us>(our_king) & bitboard_of(Them, PAWN);
//gk additional parentheses
checkers = (attacks<KNIGHT>(our_king, all) & bitboard_of(Them, KNIGHT))
| (pawn_attacks<Us>(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 <candidates>
Bitboard candidates = attacks<ROOK>(our_king, them_bb) & their_orth_sliders
| attacks<BISHOP>(our_king, them_bb) & their_diag_sliders;
//gk additional parentheses
Bitboard candidates = (attacks<ROOK>(our_king, them_bb) & their_orth_sliders)
| (attacks<BISHOP>(our_king, them_bb) & their_diag_sliders);

pinned = 0;
while (candidates) {
Expand All @@ -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
Expand Down Expand Up @@ -698,3 +706,5 @@ class MoveList {
Move list[218];
Move *last;
};

} // namespace surge
26 changes: 18 additions & 8 deletions src/tables.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
#include "tables.h"
#include "types.h"
#include <iostream>
#include <cstring> //gk memcpy()

namespace surge {

//All piece tables are generated from a program written in Java

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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]];
}
Expand Down Expand Up @@ -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]];
}
Expand Down Expand Up @@ -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];
}
}
Expand Down Expand Up @@ -301,3 +309,5 @@ void initialise_all_databases() {
initialise_line();
initialise_pseudo_legal();
}

} // namespace surge
11 changes: 9 additions & 2 deletions src/tables.h
Original file line number Diff line number Diff line change
Expand Up @@ -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];
Expand All @@ -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);
Expand All @@ -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];
Expand All @@ -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:
Expand All @@ -80,3 +85,5 @@ template<Color C>
constexpr Bitboard pawn_attacks(Square s) {
return PAWN_ATTACKS[C][s];
}

} // namespace surge
27 changes: 18 additions & 9 deletions src/types.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#include "types.h"
#include <iostream>

namespace surge {

//Lookup tables of square names in algebraic chess notation
const char* SQSTR[65] = {
"a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1",
Expand Down Expand Up @@ -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;
Expand All @@ -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++;
Expand All @@ -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,
Expand All @@ -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.", "",
Expand All @@ -139,3 +147,4 @@ std::ostream& operator<<(std::ostream& os, const Move& m) {
return os;
}

} // namespace surge
Loading