stellar

Stellar - UCI Chess engine written in C++20
git clone git://git.dimitrijedobrota.com/stellar.git
Log | Files | Refs | README | LICENSE

commit 397202fe942470ad24b0bc52461a1c1526cefd84
parent 9c244b30d8dce60a01473b95f7b6eb76cf29ed2c
Author: Dimitrije Dobrota <mail@dimitrijedobrota.com>
Date:   Fri, 23 Sep 2022 22:06:26 +0200

Split engine into attack and utils. Check if square is under attack

Diffstat:
Ainclude/attack.h | 19+++++++++++++++++++
Ainclude/utils.h | 64++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/attack.c | 384+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/engine.c | 517+++++++++----------------------------------------------------------------------
Asrc/utils.c | 29+++++++++++++++++++++++++++++
5 files changed, 554 insertions(+), 459 deletions(-)

diff --git a/include/attack.h b/include/attack.h @@ -0,0 +1,19 @@ +#ifndef ATTACK_H +#define ATTACK_H + +#include "utils.h" + +typedef U64 (*attack_f)(Square square, U64 occupancy); + +void init_leapers_attacks(void); +void init_sliders_attacks(void); + +U64 get_wpawn_attacks(Square square, U64 occupancy); +U64 get_bpawn_attacks(Square square, U64 occupancy); +U64 get_knight_attacks(Square square, U64 occupancy); +U64 get_king_attacks(Square square, U64 occupancy); +U64 get_bishop_attacks(Square square, U64 occupancy); +U64 get_rook_attacks(Square square, U64 occupancy); +U64 get_queen_attacks(Square square, U64 occupancy); + +#endif diff --git a/include/utils.h b/include/utils.h @@ -0,0 +1,64 @@ +#ifndef __UTILS_H +#define __UTILS_H + +// useful macros +#define MAX(a, b) ((a > b) ? a : b) +#define MIN(a, b) ((a < b) ? a : b) + +// define number types +typedef unsigned long long U64; // define bitboard data type +#define C64(constantU64) constantU64##ULL // define shorthand for constants + +typedef unsigned int U32; +#define C32(constantU32) constantU32##U + +// useful bit patterns +extern const U64 universe; +extern const U64 notAFile; +extern const U64 notHFile; + +// useful bit operations +#define bit_get(bitboard, square) (((bitboard) >> (square)) & C64(1)) +#define bit_set(bitboard, square) ((bitboard) |= C64(1) << (square)) +#define bit_pop(bitboard, square) ((bitboard) &= ~(C64(1) << (square))) + +// squares +// clang-format off +enum enumSquare { + a1, b1, c1, d1, e1, f1, g1, h1, + a2, b2, c2, d2, e2, f2, g2, h2, + a3, b3, c3, d3, e3, f3, g3, h3, + a4, b4, c4, d4, e4, f4, g4, h4, + a5, b5, c5, d5, e5, f5, g5, h5, + a6, b6, c6, d6, e6, f6, g6, h6, + a7, b7, c7, d7, e7, f7, g7, h7, + a8, b8, c8, d8, e8, f8, g8, h8, no_sq +}; +// clang-format on +typedef enum enumSquare Square; + +extern const char *square_to_coordinates[]; + +// board moving +typedef U64 (*direction_f)(U64); +U64 soutOne(U64 b); +U64 nortOne(U64 b); +U64 eastOne(U64 b); +U64 westOne(U64 b); +U64 soEaOne(U64 b); +U64 soWeOne(U64 b); +U64 noEaOne(U64 b); +U64 noWeOne(U64 b); + +// board rotation +U64 rotateLeft(U64 x, int s); +U64 rotateRight(U64 x, int s); + +// enum types for color and piece type +enum enumColor { WHITE = 0, BLACK }; +enum enumPiece { PAWN = 0, KNIGHT, BISHOP, ROOK, QUEEN, KING }; + +typedef enum enumColor eColor; +typedef enum enumPiece ePiece; + +#endif diff --git a/src/attack.c b/src/attack.c @@ -0,0 +1,384 @@ +#include "string.h" + +#include "attack.h" +#include "utils.h" + +#define UNUSED(x) (void)(x) + +// Magic constants + +// clang-format off +const int bishop_relevant_bits[64] = { + 6, 5, 5, 5, 5, 5, 5, 6, + 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 7, 7, 7, 7, 5, 5, + 5, 5, 7, 9, 9, 7, 5, 5, + 5, 5, 7, 9, 9, 7, 5, 5, + 5, 5, 7, 7, 7, 7, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, + 6, 5, 5, 5, 5, 5, 5, 6, +}; + +const U64 bishop_magic_numbers[64] = { + C64(0x40040844404084), C64(0x2004208a004208), C64(0x10190041080202), + C64(0x108060845042010), C64(0x581104180800210), C64(0x2112080446200010), + C64(0x1080820820060210), C64(0x3c0808410220200), C64(0x4050404440404), + C64(0x21001420088), C64(0x24d0080801082102), C64(0x1020a0a020400), + C64(0x40308200402), C64(0x4011002100800), C64(0x401484104104005), + C64(0x801010402020200), C64(0x400210c3880100), C64(0x404022024108200), + C64(0x810018200204102), C64(0x4002801a02003), C64(0x85040820080400), + C64(0x810102c808880400), C64(0xe900410884800), C64(0x8002020480840102), + C64(0x220200865090201), C64(0x2010100a02021202), C64(0x152048408022401), + C64(0x20080002081110), C64(0x4001001021004000), C64(0x800040400a011002), + C64(0xe4004081011002), C64(0x1c004001012080), C64(0x8004200962a00220), + C64(0x8422100208500202), C64(0x2000402200300c08), C64(0x8646020080080080), + C64(0x80020a0200100808), C64(0x2010004880111000), C64(0x623000a080011400), + C64(0x42008c0340209202), C64(0x209188240001000), C64(0x400408a884001800), + C64(0x110400a6080400), C64(0x1840060a44020800), C64(0x90080104000041), + C64(0x201011000808101), C64(0x1a2208080504f080), C64(0x8012020600211212), + C64(0x500861011240000), C64(0x180806108200800), C64(0x4000020e01040044), + C64(0x300000261044000a), C64(0x802241102020002), C64(0x20906061210001), + C64(0x5a84841004010310), C64(0x4010801011c04), C64(0xa010109502200), + C64(0x4a02012000), C64(0x500201010098b028), C64(0x8040002811040900), + C64(0x28000010020204), C64(0x6000020202d0240), C64(0x8918844842082200), + C64(0x4010011029020020), +}; + +const int rook_relevant_bits[64] = { + 12, 11, 11, 11, 11, 11, 11, 12, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 11, 10, 10, 10, 10, 10, 10, 11, + 12, 11, 11, 11, 11, 11, 11, 12, +}; + +const U64 rook_magic_numbers[64] = { + C64(0x8a80104000800020), C64(0x140002000100040), C64(0x2801880a0017001), + C64(0x100081001000420), C64(0x200020010080420), C64(0x3001c0002010008), + C64(0x8480008002000100), C64(0x2080088004402900), C64(0x800098204000), + C64(0x2024401000200040), C64(0x100802000801000), C64(0x120800800801000), + C64(0x208808088000400), C64(0x2802200800400), C64(0x2200800100020080), + C64(0x801000060821100), C64(0x80044006422000), C64(0x100808020004000), + C64(0x12108a0010204200), C64(0x140848010000802), C64(0x481828014002800), + C64(0x8094004002004100), C64(0x4010040010010802), C64(0x20008806104), + C64(0x100400080208000), C64(0x2040002120081000), C64(0x21200680100081), + C64(0x20100080080080), C64(0x2000a00200410), C64(0x20080800400), + C64(0x80088400100102), C64(0x80004600042881), C64(0x4040008040800020), + C64(0x440003000200801), C64(0x4200011004500), C64(0x188020010100100), + C64(0x14800401802800), C64(0x2080040080800200), C64(0x124080204001001), + C64(0x200046502000484), C64(0x480400080088020), C64(0x1000422010034000), + C64(0x30200100110040), C64(0x100021010009), C64(0x2002080100110004), + C64(0x202008004008002), C64(0x20020004010100), C64(0x2048440040820001), + C64(0x101002200408200), C64(0x40802000401080), C64(0x4008142004410100), + C64(0x2060820c0120200), C64(0x1001004080100), C64(0x20c020080040080), + C64(0x2935610830022400), C64(0x44440041009200), C64(0x280001040802101), + C64(0x2100190040002085), C64(0x80c0084100102001), C64(0x4024081001000421), + C64(0x20030a0244872), C64(0x12001008414402), C64(0x2006104900a0804), + C64(0x1004081002402), +}; + + +// Utility functions + +int hash(U64 key, U64 magic, int relevant_bits) { + return (key * magic) >> (64 - relevant_bits); +} + +static inline int bit_count(U64 bitboard) { + int count = 0; + + while (bitboard > 0) { + count++; + bitboard &= bitboard - 1; + } + + return count; +} + +static inline int bit_lsb_index(U64 bitboard) { + if (!bitboard) + return -1; + + return bit_count((bitboard & -bitboard) - 1); +} + +// pseudo random numbers + +U32 state = C32(1804289383); + +U32 get_random_U32_number() { + U32 number = state; + + number ^= number << 13; + number ^= number >> 17; + number ^= number << 5; + + return state = number; +} + +U64 get_random_U64_number() { + U64 n1, n2, n3, n4; + + n1 = (U64)(get_random_U32_number()) & C64(0xFFFF); + n2 = (U64)(get_random_U32_number()) & C64(0xFFFF); + n3 = (U64)(get_random_U32_number()) & C64(0xFFFF); + n4 = (U64)(get_random_U32_number()) & C64(0xFFFF); + + return n1 | (n2 << 16) | (n3 << 32) | (n4 << 48); +} + +// pawn attack table [side][square] +U64 pawn_attacks[2][64]; + +// knight attack table [square] +U64 knight_attacks[64]; + +// king attack table [square] +U64 king_attacks[64]; + +// bishop attack mask +U64 bishop_masks[64]; + +// rook attack mask +U64 rook_masks[64]; + +// bishop attack table [square][occupancies] +U64 bishop_attacks[64][512]; // 256 K + +// rook attack table [square][occupancies] +U64 rook_attacks[64][4096]; // 2048K + +U64 get_wpawn_attacks(Square square, U64 occupancy){ + UNUSED(occupancy); + return pawn_attacks[WHITE][square]; +} + +U64 get_bpawn_attacks(Square square, U64 occupancy){ + UNUSED(occupancy); + return pawn_attacks[BLACK][square]; +} + +U64 get_knight_attacks(Square square, U64 occupancy){ + UNUSED(occupancy); + return knight_attacks[square]; +} + +U64 get_king_attacks(Square square, U64 occupancy){ + UNUSED(occupancy); + return king_attacks[square]; +} + +U64 get_bishop_attacks(Square square, U64 occupancy){ + occupancy &= bishop_masks[square]; + occupancy = hash(occupancy, bishop_magic_numbers[square], + bishop_relevant_bits[square]); + return bishop_attacks[square][occupancy]; +} + +U64 get_rook_attacks(Square square, U64 occupancy){ + occupancy &= rook_masks[square]; + occupancy = + hash(occupancy, rook_magic_numbers[square], rook_relevant_bits[square]); + return rook_attacks[square][occupancy]; +} + +U64 get_queen_attacks(Square square, U64 occupancy){ + return (get_bishop_attacks(square, occupancy) | + get_rook_attacks(square, occupancy)); +} + + +// generate pawn attack +U64 mask_pawn_attacks(int side, Square square) { + U64 bitboard = C64(0); + + bit_set(bitboard, square); + if (side == WHITE) + return noWeOne(bitboard) | noEaOne(bitboard); + else + return soWeOne(bitboard) | soEaOne(bitboard); +} + +U64 mask_knight_attacks(Square square) { + U64 bitboard = C64(0), attacks = C64(0), tmp; + + bit_set(bitboard, square); + tmp = nortOne(nortOne(bitboard)); + attacks |= westOne(tmp) | eastOne(tmp); + tmp = soutOne(soutOne(bitboard)); + attacks |= westOne(tmp) | eastOne(tmp); + tmp = westOne(westOne(bitboard)); + attacks |= soutOne(tmp) | nortOne(tmp); + tmp = eastOne(eastOne(bitboard)); + attacks |= soutOne(tmp) | nortOne(tmp); + + return attacks; +} + +U64 mask_king_attacks(Square square) { + U64 bitboard = C64(0), attacks = C64(0); + + bit_set(bitboard, square); + attacks |= westOne(bitboard) | eastOne(bitboard); + attacks |= soutOne(bitboard) | nortOne(bitboard); + attacks |= soutOne(bitboard) | nortOne(bitboard); + attacks |= soEaOne(bitboard) | noEaOne(bitboard); + attacks |= soWeOne(bitboard) | noWeOne(bitboard); + + return attacks; +} + +U64 mask_slide_attacks(Square square, U64 block, const direction_f dir[4], + int len[4]) { + U64 bitboard = C64(0), attacks = C64(0), tmp; + int i, j; + + bit_set(bitboard, square); + for (i = 0; i < 4; i++) { + for (j = 0, tmp = bitboard; j < len[i]; j++) { + attacks |= tmp = (dir[i])(tmp); + if (tmp & block) + break; + } + } + return attacks; +} + +const direction_f bishop_direction[4] = {noEaOne, noWeOne, soEaOne, soWeOne}; +const direction_f rook_direction[4] = {westOne, soutOne, eastOne, nortOne}; + +U64 mask_bishop_attacks(Square square) { + int tr = square / 8, tf = square % 8; + int len[4] = {MIN(7 - tf, 7 - tr) - 1, MIN(tf, 7 - tr) - 1, + MIN(7 - tf, tr) - 1, MIN(tf, tr) - 1}; + return mask_slide_attacks(square, C64(0), bishop_direction, len); +} + +U64 mask_rook_attacks(Square square) { + int tr = square / 8, tf = square % 8; + int len[4] = {tf - 1, tr - 1, 6 - tf, 6 - tr}; + + return mask_slide_attacks(square, C64(0), rook_direction, len); +} + +U64 bishop_attacks_on_the_fly(Square square, U64 block) { + int tr = square / 8, tf = square % 8; + int len[4] = {MIN(7 - tf, 7 - tr), MIN(tf, 7 - tr), MIN(7 - tf, tr), + MIN(tf, tr)}; + + return mask_slide_attacks(square, block, bishop_direction, len); +} + +U64 rook_attacks_on_the_fly(Square square, U64 block) { + int tr = square / 8, tf = square % 8; + int len[4] = {tf, tr, 7 - tf, 7 - tr}; + + return mask_slide_attacks(square, block, rook_direction, len); +} + +void init_leapers_attacks(void) { + for (Square square = 0; square < 64; square++) { + pawn_attacks[WHITE][square] = mask_pawn_attacks(WHITE, square); + pawn_attacks[BLACK][square] = mask_pawn_attacks(BLACK, square); + knight_attacks[square] = mask_knight_attacks(square); + king_attacks[square] = mask_king_attacks(square); + } +} + +U64 set_occupancy(int index, int bits_in_mask, U64 attack_mask) { + U64 occupancy = C64(0); + + for (int count = 0; count < bits_in_mask; count++) { + Square square = bit_lsb_index(attack_mask); + bit_pop(attack_mask, square); + + if (index & (1 << count)) + bit_set(occupancy, square); + } + + return occupancy; +} + +void init_sliders_attacks_internal(int bishop) { + for (Square square = 0; square < 64; square++) { + U64 attack_mask; + + if (bishop) { + bishop_masks[square] = mask_bishop_attacks(square); + attack_mask = bishop_masks[square]; + } else { + rook_masks[square] = mask_rook_attacks(square); + attack_mask = rook_masks[square]; + } + + int relevant_bits = bit_count(attack_mask); + int occupancy_indicies = 1 << relevant_bits; + + for (int index = 0; index < occupancy_indicies; index++) { + U64 occupancy = set_occupancy(index, relevant_bits, attack_mask); + if (bishop) { + int magic_index = (occupancy * bishop_magic_numbers[square]) >> + (64 - bishop_relevant_bits[square]); + bishop_attacks[square][magic_index] = + bishop_attacks_on_the_fly(square, occupancy); + } else { + int magic_index = hash(occupancy, rook_magic_numbers[square], + rook_relevant_bits[square]); + rook_attacks[square][magic_index] = + rook_attacks_on_the_fly(square, occupancy); + } + } + } +} + +void init_sliders_attacks(void) { + init_sliders_attacks_internal(0); + init_sliders_attacks_internal(1); +} + +// magic numbers + +U64 generate_magic_number() { + return get_random_U64_number() & get_random_U64_number() & + get_random_U64_number(); +} + +U64 find_magic_number(Square square, int relevant_bits, int bishop) { + U64 occupancies[4096], attacks[4096], used_attacks[4096]; + U64 attack_mask = + bishop ? mask_bishop_attacks(square) : mask_rook_attacks(square); + int occupancy_indicies = 1 << relevant_bits; + + for (int index = 0; index < occupancy_indicies; index++) { + occupancies[index] = set_occupancy(index, relevant_bits, attack_mask); + attacks[index] = bishop + ? bishop_attacks_on_the_fly(square, occupancies[index]) + : rook_attacks_on_the_fly(square, occupancies[index]); + } + + for (int random_count = 0; random_count < 100000000; random_count++) { + U64 magic_number = generate_magic_number(); + if (bit_count((attack_mask * magic_number) & C64(0xFF00000000000000)) < 6) + continue; + + memset(used_attacks, C64(0), sizeof(used_attacks)); + int index, fail; + + for (index = 0, fail = 0; !fail && index < occupancy_indicies; index++) { + int magic_index = hash(occupancies[index], magic_number, relevant_bits); + + if (used_attacks[magic_index] == C64(0)) + used_attacks[magic_index] = attacks[index]; + else if (used_attacks[magic_index] != attacks[index]) + fail = 1; + } + + if (!fail) + return magic_number; + } + + return C64(0); +} diff --git a/src/engine.c b/src/engine.c @@ -2,6 +2,9 @@ #include <stdio.h> #include <string.h> +#include "attack.h" +#include "utils.h" + #include <cii/assert.h> #include <cii/except.h> #include <cii/mem.h> @@ -19,128 +22,15 @@ #define cmk_position \ "r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 " -/* DEFINITIONS */ - -// useful macros -#define MAX(a, b) ((a > b) ? a : b) -#define MIN(a, b) ((a < b) ? a : b) - -// define number types -typedef unsigned long long U64; // define bitboard data type -#define C64(constantU64) constantU64##ULL // define shorthand for constants - -typedef unsigned int U32; -#define C32(constantU32) constantU32##U - -// useful bit patterns -const U64 universe = C64(0xffffffffffffffff); // -const U64 notAFile = C64(0xfefefefefefefefe); // ~0x0101010101010101 -const U64 notHFile = C64(0x7f7f7f7f7f7f7f7f); // ~0x8080808080808080 - -// useful bit operations -#define bit_get(bitboard, square) (((bitboard) >> (square)) & C64(1)) -#define bit_set(bitboard, square) ((bitboard) |= C64(1) << (square)) -#define bit_pop(bitboard, square) ((bitboard) &= ~(C64(1) << (square))) - -static inline int bit_count(U64 bitboard) { - int count = 0; - - while (bitboard > 0) { - count++; - bitboard &= bitboard - 1; - } - - return count; -} - -static inline int bit_lsb_index(U64 bitboard) { - if (!bitboard) - return -1; - - return bit_count((bitboard & -bitboard) - 1); -} - -// pseudo random numbers - -U32 state = C32(1804289383); - -U32 get_random_U32_number() { - U32 number = state; - - number ^= number << 13; - number ^= number >> 17; - number ^= number << 5; - - return state = number; -} - -U64 get_random_U64_number() { - U64 n1, n2, n3, n4; - - n1 = (U64)(get_random_U32_number()) & C64(0xFFFF); - n2 = (U64)(get_random_U32_number()) & C64(0xFFFF); - n3 = (U64)(get_random_U32_number()) & C64(0xFFFF); - n4 = (U64)(get_random_U32_number()) & C64(0xFFFF); - - return n1 | (n2 << 16) | (n3 << 32) | (n4 << 48); -} - -// board moving -typedef U64 (*direction_f)(U64); -U64 soutOne(U64 b) { return b >> 8; } -U64 nortOne(U64 b) { return b << 8; } -U64 eastOne(U64 b) { return (b & notHFile) << 1; } -U64 westOne(U64 b) { return (b & notAFile) >> 1; } -U64 soEaOne(U64 b) { return (b & notHFile) >> 7; } -U64 soWeOne(U64 b) { return (b & notAFile) >> 9; } -U64 noEaOne(U64 b) { return (b & notHFile) << 9; } -U64 noWeOne(U64 b) { return (b & notAFile) << 7; } - -// board rotation -U64 rotateLeft(U64 x, int s) { return (x << s) | (x >> (64 - s)); } -U64 rotateRight(U64 x, int s) { return (x >> s) | (x << (64 - s)); } - -// squares -// clang-format off -enum enumSquare { - a1, b1, c1, d1, e1, f1, g1, h1, - a2, b2, c2, d2, e2, f2, g2, h2, - a3, b3, c3, d3, e3, f3, g3, h3, - a4, b4, c4, d4, e4, f4, g4, h4, - a5, b5, c5, d5, e5, f5, g5, h5, - a6, b6, c6, d6, e6, f6, g6, h6, - a7, b7, c7, d7, e7, f7, g7, h7, - a8, b8, c8, d8, e8, f8, g8, h8, no_sq -}; -typedef enum enumSquare Square; - -const char *square_to_coordinates[]={ - "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", - "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", - "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3", - "a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4", - "a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5", - "a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6", - "a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7", - "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", " " -}; -// clang-format on - -// enum types for color and piece type -enum enumColor { WHITE = 0, BLACK }; -enum enumPiece { PAWN = 0, KNIGHT, BISHOP, ROOK, QUEEN, KING }; - -typedef enum enumColor eColor; -typedef enum enumPiece ePiece; - // piece representation typedef struct Piece_T *Piece_T; struct Piece_T { - ePiece piece; - eColor color; - char code; - char asci; - char *unicode; + ePiece piece; + eColor color; + char code; + char asci; + char *unicode; + attack_f attacks; }; ePiece Piece_piece(Piece_T pt) { return pt->piece; } @@ -153,20 +43,20 @@ char *Piece_unicode(Piece_T pt) { return pt->unicode; } // clang-format off struct Piece_T Pieces[2][6] = { { - {.color = WHITE, .code = 'P', .asci = 'P', .unicode = "♙ ", .piece = PAWN }, - {.color = WHITE, .code = 'N', .asci = 'N', .unicode = "♘ ", .piece = KNIGHT }, - {.color = WHITE, .code = 'B', .asci = 'B', .unicode = "♗ ", .piece = BISHOP }, - {.color = WHITE, .code = 'R', .asci = 'R', .unicode = "♖ ", .piece = ROOK }, - {.color = WHITE, .code = 'Q', .asci = 'Q', .unicode = "♕ ", .piece = QUEEN }, - {.color = WHITE, .code = 'K', .asci = 'K', .unicode = "♔ ", .piece = KING }, + {.color = WHITE, .code = 'P', .asci = 'P', .unicode = "♙ ", .piece = PAWN, .attacks = get_wpawn_attacks}, + {.color = WHITE, .code = 'N', .asci = 'N', .unicode = "♘ ", .piece = KNIGHT, .attacks = get_knight_attacks}, + {.color = WHITE, .code = 'B', .asci = 'B', .unicode = "♗ ", .piece = BISHOP, .attacks = get_bishop_attacks}, + {.color = WHITE, .code = 'R', .asci = 'R', .unicode = "♖ ", .piece = ROOK, .attacks = get_rook_attacks}, + {.color = WHITE, .code = 'Q', .asci = 'Q', .unicode = "♕ ", .piece = QUEEN, .attacks = get_queen_attacks}, + {.color = WHITE, .code = 'K', .asci = 'K', .unicode = "♔ ", .piece = KING, .attacks = get_king_attacks}, }, { - {.color = BLACK, .code = 'p', .asci = 'p', .unicode = "♟ ", .piece = PAWN }, - {.color = BLACK, .code = 'n', .asci = 'n', .unicode = "♞ ", .piece = KNIGHT }, - {.color = BLACK, .code = 'b', .asci = 'b', .unicode = "♝ ", .piece = BISHOP }, - {.color = BLACK, .code = 'r', .asci = 'r', .unicode = "♜ ", .piece = ROOK }, - {.color = BLACK, .code = 'q', .asci = 'q', .unicode = "♛ ", .piece = QUEEN }, - {.color = BLACK, .code = 'k', .asci = 'k', .unicode = "♚ ", .piece = KING }, + {.color = BLACK, .code = 'p', .asci = 'p', .unicode = "♟ ", .piece = PAWN, .attacks = get_bpawn_attacks}, + {.color = BLACK, .code = 'n', .asci = 'n', .unicode = "♞ ", .piece = KNIGHT, .attacks = get_knight_attacks}, + {.color = BLACK, .code = 'b', .asci = 'b', .unicode = "♝ ", .piece = BISHOP, .attacks = get_bishop_attacks}, + {.color = BLACK, .code = 'r', .asci = 'r', .unicode = "♜ ", .piece = ROOK, .attacks = get_rook_attacks}, + {.color = BLACK, .code = 'q', .asci = 'q', .unicode = "♛ ", .piece = QUEEN, .attacks = get_queen_attacks}, + {.color = BLACK, .code = 'k', .asci = 'k', .unicode = "♚ ", .piece = KING, .attacks = get_king_attacks}, }, }; // clang-format on @@ -192,18 +82,6 @@ struct CBoard_T { eCastle castle; }; -// clang-format off -struct CBoard_T Cboard_new = { - .side = WHITE, - .enpassant = no_sq, - .castle = WK | WQ | BK| BQ, - .colorBB = { C64(0x000000000000FFFF), C64(0xFFFF000000000000) }, - .pieceBB = { C64(0x00FF00000000FF00), C64(0x4200000000000042), - C64(0x2400000000000024), C64(0x8100000000000081), - C64(0x800000000000008), C64(0x1000000000000010) }, -}; -// clang-format on - U64 CBoard_getPieceSet(CBoard_T self, Piece_T piece) { return self->pieceBB[Piece_color(piece)] & self->pieceBB[Piece_color(piece)]; } @@ -231,7 +109,7 @@ CBoard_T CBoard_fromFEN(CBoard_T board, char *fen) { int file = 0, rank = 7; for (Piece_T piece; *fen != ' '; fen++) { - int square = rank * 8 + file; + Square square = rank * 8 + file; if (isalpha(*fen)) { if (!(piece = Piece_fromCode(*fen))) assert(0); @@ -274,10 +152,22 @@ CBoard_T CBoard_fromFEN(CBoard_T board, char *fen) { return board; } +int CBoard_square_isAttack(CBoard_T self, Square square, eColor side) { + U64 occupancy = self->colorBB[WHITE] | self->colorBB[BLACK]; + + for (int i = 0; i < 6; i++) { + if (Pieces[!side][i].attacks(square, occupancy) & self->pieceBB[i] & + self->colorBB[side]) + return 1; + } + + return 0; +} + void CBoard_print(CBoard_T self) { for (int rank = 0; rank < 8; rank++) { for (int file = 0; file < 8; file++) { - int square = (7 - rank) * 8 + file; + Square square = (7 - rank) * 8 + file; Piece_T piece = NULL; int color = -1; @@ -311,348 +201,57 @@ void CBoard_print(CBoard_T self) { printf("\n"); } -void bitboard_print(U64 bitboard) { +void CBoard_print_attacked(CBoard_T self, eColor side) { for (int rank = 0; rank < 8; rank++) { for (int file = 0; file < 8; file++) { - int square = (7 - rank) * 8 + file; + Square square = (7 - rank) * 8 + file; if (!file) printf(" %d ", 8 - rank); - printf("%d ", bit_get(bitboard, square) ? 1 : 0); + printf("%d ", CBoard_square_isAttack(self, square, side)); } printf("\n"); } - - printf("\n A B C D E F G H\n\n"); - printf(" Bitboard: %llud\n\n", bitboard); -} - -/* ATTACKS */ - -// clang-format off -const int bishop_relevant_bits[64] = { - 6, 5, 5, 5, 5, 5, 5, 6, - 5, 5, 5, 5, 5, 5, 5, 5, - 5, 5, 7, 7, 7, 7, 5, 5, - 5, 5, 7, 9, 9, 7, 5, 5, - 5, 5, 7, 9, 9, 7, 5, 5, - 5, 5, 7, 7, 7, 7, 5, 5, - 5, 5, 5, 5, 5, 5, 5, 5, - 6, 5, 5, 5, 5, 5, 5, 6, -}; - -const U64 bishop_magic_numbers[64] = { - C64(0x40040844404084), C64(0x2004208a004208), C64(0x10190041080202), - C64(0x108060845042010), C64(0x581104180800210), C64(0x2112080446200010), - C64(0x1080820820060210), C64(0x3c0808410220200), C64(0x4050404440404), - C64(0x21001420088), C64(0x24d0080801082102), C64(0x1020a0a020400), - C64(0x40308200402), C64(0x4011002100800), C64(0x401484104104005), - C64(0x801010402020200), C64(0x400210c3880100), C64(0x404022024108200), - C64(0x810018200204102), C64(0x4002801a02003), C64(0x85040820080400), - C64(0x810102c808880400), C64(0xe900410884800), C64(0x8002020480840102), - C64(0x220200865090201), C64(0x2010100a02021202), C64(0x152048408022401), - C64(0x20080002081110), C64(0x4001001021004000), C64(0x800040400a011002), - C64(0xe4004081011002), C64(0x1c004001012080), C64(0x8004200962a00220), - C64(0x8422100208500202), C64(0x2000402200300c08), C64(0x8646020080080080), - C64(0x80020a0200100808), C64(0x2010004880111000), C64(0x623000a080011400), - C64(0x42008c0340209202), C64(0x209188240001000), C64(0x400408a884001800), - C64(0x110400a6080400), C64(0x1840060a44020800), C64(0x90080104000041), - C64(0x201011000808101), C64(0x1a2208080504f080), C64(0x8012020600211212), - C64(0x500861011240000), C64(0x180806108200800), C64(0x4000020e01040044), - C64(0x300000261044000a), C64(0x802241102020002), C64(0x20906061210001), - C64(0x5a84841004010310), C64(0x4010801011c04), C64(0xa010109502200), - C64(0x4a02012000), C64(0x500201010098b028), C64(0x8040002811040900), - C64(0x28000010020204), C64(0x6000020202d0240), C64(0x8918844842082200), - C64(0x4010011029020020), -}; - -const int rook_relevant_bits[64] = { - 12, 11, 11, 11, 11, 11, 11, 12, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 11, 10, 10, 10, 10, 10, 10, 11, - 12, 11, 11, 11, 11, 11, 11, 12, -}; - -const U64 rook_magic_numbers[64] = { - C64(0x8a80104000800020), C64(0x140002000100040), C64(0x2801880a0017001), - C64(0x100081001000420), C64(0x200020010080420), C64(0x3001c0002010008), - C64(0x8480008002000100), C64(0x2080088004402900), C64(0x800098204000), - C64(0x2024401000200040), C64(0x100802000801000), C64(0x120800800801000), - C64(0x208808088000400), C64(0x2802200800400), C64(0x2200800100020080), - C64(0x801000060821100), C64(0x80044006422000), C64(0x100808020004000), - C64(0x12108a0010204200), C64(0x140848010000802), C64(0x481828014002800), - C64(0x8094004002004100), C64(0x4010040010010802), C64(0x20008806104), - C64(0x100400080208000), C64(0x2040002120081000), C64(0x21200680100081), - C64(0x20100080080080), C64(0x2000a00200410), C64(0x20080800400), - C64(0x80088400100102), C64(0x80004600042881), C64(0x4040008040800020), - C64(0x440003000200801), C64(0x4200011004500), C64(0x188020010100100), - C64(0x14800401802800), C64(0x2080040080800200), C64(0x124080204001001), - C64(0x200046502000484), C64(0x480400080088020), C64(0x1000422010034000), - C64(0x30200100110040), C64(0x100021010009), C64(0x2002080100110004), - C64(0x202008004008002), C64(0x20020004010100), C64(0x2048440040820001), - C64(0x101002200408200), C64(0x40802000401080), C64(0x4008142004410100), - C64(0x2060820c0120200), C64(0x1001004080100), C64(0x20c020080040080), - C64(0x2935610830022400), C64(0x44440041009200), C64(0x280001040802101), - C64(0x2100190040002085), C64(0x80c0084100102001), C64(0x4024081001000421), - C64(0x20030a0244872), C64(0x12001008414402), C64(0x2006104900a0804), - C64(0x1004081002402), -}; -// clang-format on - -// pawn attack table [side][square] -U64 pawn_attacks[2][64]; - -// knight attack table [square] -U64 knight_attacks[64]; - -// king attack table [square] -U64 king_attacks[64]; - -// bishop attack mask -U64 bishop_masks[64]; - -// rook attack mask -U64 rook_masks[64]; - -// bishop attack table [square][occupancies] -U64 bishop_attacks[64][512]; // 256 K - -// rook attack table [square][occupancies] -U64 rook_attacks[64][4096]; // 2048K - -// generate pawn attack -U64 mask_pawn_attacks(int side, int square) { - U64 bitboard = C64(0); - - bit_set(bitboard, square); - if (side == WHITE) - return noWeOne(bitboard) | noEaOne(bitboard); - else - return soWeOne(bitboard) | soEaOne(bitboard); -} - -U64 mask_knight_attacks(int square) { - U64 bitboard = C64(0), attacks = C64(0), tmp; - - bit_set(bitboard, square); - tmp = nortOne(nortOne(bitboard)); - attacks |= westOne(tmp) | eastOne(tmp); - tmp = soutOne(soutOne(bitboard)); - attacks |= westOne(tmp) | eastOne(tmp); - tmp = westOne(westOne(bitboard)); - attacks |= soutOne(tmp) | nortOne(tmp); - tmp = eastOne(eastOne(bitboard)); - attacks |= soutOne(tmp) | nortOne(tmp); - - return attacks; -} - -U64 mask_king_attacks(int square) { - U64 bitboard = C64(0), attacks = C64(0); - - bit_set(bitboard, square); - attacks |= westOne(bitboard) | eastOne(bitboard); - attacks |= soutOne(bitboard) | nortOne(bitboard); - attacks |= soutOne(bitboard) | nortOne(bitboard); - attacks |= soEaOne(bitboard) | noEaOne(bitboard); - attacks |= soWeOne(bitboard) | noWeOne(bitboard); - - return attacks; -} - -U64 mask_slide_attacks(int square, U64 block, const direction_f dir[4], - int len[4]) { - U64 bitboard = C64(0), attacks = C64(0), tmp; - int i, j; - - bit_set(bitboard, square); - for (i = 0; i < 4; i++) { - for (j = 0, tmp = bitboard; j < len[i]; j++) { - attacks |= tmp = (dir[i])(tmp); - if (tmp & block) - break; - } - } - return attacks; -} - -const direction_f bishop_direction[4] = {noEaOne, noWeOne, soEaOne, soWeOne}; -const direction_f rook_direction[4] = {westOne, soutOne, eastOne, nortOne}; - -U64 mask_bishop_attacks(int square) { - int tr = square / 8, tf = square % 8; - int len[4] = {MIN(7 - tf, 7 - tr) - 1, MIN(tf, 7 - tr) - 1, - MIN(7 - tf, tr) - 1, MIN(tf, tr) - 1}; - return mask_slide_attacks(square, C64(0), bishop_direction, len); -} - -U64 mask_rook_attacks(int square) { - int tr = square / 8, tf = square % 8; - int len[4] = {tf - 1, tr - 1, 6 - tf, 6 - tr}; - - return mask_slide_attacks(square, C64(0), rook_direction, len); -} - -U64 bishop_attacks_on_the_fly(int square, U64 block) { - int tr = square / 8, tf = square % 8; - int len[4] = {MIN(7 - tf, 7 - tr), MIN(tf, 7 - tr), MIN(7 - tf, tr), - MIN(tf, tr)}; - - return mask_slide_attacks(square, block, bishop_direction, len); -} - -U64 rook_attacks_on_the_fly(int square, U64 block) { - int tr = square / 8, tf = square % 8; - int len[4] = {tf, tr, 7 - tf, 7 - tr}; - - return mask_slide_attacks(square, block, rook_direction, len); -} - -void init_leapers_attacks(void) { - for (int square = 0; square < 64; square++) { - pawn_attacks[WHITE][square] = mask_pawn_attacks(WHITE, square); - pawn_attacks[BLACK][square] = mask_pawn_attacks(BLACK, square); - knight_attacks[square] = mask_knight_attacks(square); - king_attacks[square] = mask_king_attacks(square); - } -} - -U64 set_occupancy(int index, int bits_in_mask, U64 attack_mask) { - U64 occupancy = C64(0); - - for (int count = 0; count < bits_in_mask; count++) { - int square = bit_lsb_index(attack_mask); - bit_pop(attack_mask, square); - - if (index & (1 << count)) - bit_set(occupancy, square); - } - - return occupancy; -} - -int hash(U64 key, U64 magic, int relevant_bits) { - return (key * magic) >> (64 - relevant_bits); + printf(" A B C D E F G H\n"); + printf("\n"); } -void init_sliders_attacks(int bishop) { - for (int square = 0; square < 64; square++) { - U64 attack_mask; +void bitboard_print(U64 bitboard) { + for (int rank = 0; rank < 8; rank++) { + for (int file = 0; file < 8; file++) { + Square square = (7 - rank) * 8 + file; - if (bishop) { - bishop_masks[square] = mask_bishop_attacks(square); - attack_mask = bishop_masks[square]; - } else { - rook_masks[square] = mask_rook_attacks(square); - attack_mask = rook_masks[square]; - } + if (!file) + printf(" %d ", 8 - rank); - int relevant_bits = bit_count(attack_mask); - int occupancy_indicies = 1 << relevant_bits; - - for (int index = 0; index < occupancy_indicies; index++) { - U64 occupancy = set_occupancy(index, relevant_bits, attack_mask); - if (bishop) { - int magic_index = (occupancy * bishop_magic_numbers[square]) >> - (64 - bishop_relevant_bits[square]); - bishop_attacks[square][magic_index] = - bishop_attacks_on_the_fly(square, occupancy); - } else { - int magic_index = hash(occupancy, rook_magic_numbers[square], - rook_relevant_bits[square]); - rook_attacks[square][magic_index] = - rook_attacks_on_the_fly(square, occupancy); - } + printf("%d ", bit_get(bitboard, square) ? 1 : 0); } + printf("\n"); } -} - -static inline U64 get_bishop_attacks(int square, U64 occupancy) { - occupancy &= bishop_masks[square]; - occupancy = hash(occupancy, bishop_magic_numbers[square], - bishop_relevant_bits[square]); - return bishop_attacks[square][occupancy]; -} - -static inline U64 get_rook_attacks(int square, U64 occupancy) { - occupancy &= rook_masks[square]; - occupancy = - hash(occupancy, rook_magic_numbers[square], rook_relevant_bits[square]); - return rook_attacks[square][occupancy]; -} - -static inline U64 get_queen_attacks(int square, U64 occupancy) { - return (get_bishop_attacks(square, occupancy) | - get_rook_attacks(square, occupancy)); -} -// magic numbers -U64 generate_magic_number() { - return get_random_U64_number() & get_random_U64_number() & - get_random_U64_number(); + printf("\n A B C D E F G H\n\n"); + printf(" Bitboard: %llud\n\n", bitboard); } -U64 find_magic_number(int square, int relevant_bits, int bishop) { - U64 occupancies[4096], attacks[4096], used_attacks[4096]; - U64 attack_mask = - bishop ? mask_bishop_attacks(square) : mask_rook_attacks(square); - int occupancy_indicies = 1 << relevant_bits; - - for (int index = 0; index < occupancy_indicies; index++) { - occupancies[index] = set_occupancy(index, relevant_bits, attack_mask); - attacks[index] = bishop - ? bishop_attacks_on_the_fly(square, occupancies[index]) - : rook_attacks_on_the_fly(square, occupancies[index]); - } - - for (int random_count = 0; random_count < 100000000; random_count++) { - U64 magic_number = generate_magic_number(); - if (bit_count((attack_mask * magic_number) & C64(0xFF00000000000000)) < 6) - continue; - - memset(used_attacks, C64(0), sizeof(used_attacks)); - int index, fail; - - for (index = 0, fail = 0; !fail && index < occupancy_indicies; index++) { - int magic_index = hash(occupancies[index], magic_number, relevant_bits); - - if (used_attacks[magic_index] == C64(0)) - used_attacks[magic_index] = attacks[index]; - else if (used_attacks[magic_index] != attacks[index]) - fail = 1; - } - - if (!fail) - return magic_number; - } - - printf("Magic number fail!\n"); - return C64(0); -} +// clang-format on void init_all() { init_leapers_attacks(); - init_sliders_attacks(0); - init_sliders_attacks(1); + init_sliders_attacks(); } int main(void) { init_all(); - CBoard_print(&Cboard_new); - CBoard_T board; - NEW(board); - CBoard_print(CBoard_fromFEN(board, cmk_position)); - bitboard_print( - get_queen_attacks(d8, board->colorBB[WHITE] | board->colorBB[BLACK])); + board = CBoard_fromFEN( + NULL, "rnbqkbnr/ppp1pppp/8/3p4/4P3/8/PPPP1PPP/RNBQKBNR w KQkq - 0 1 "); - FREE(board); + CBoard_print(board); + CBoard_print_attacked(board, WHITE); + CBoard_print_attacked(board, BLACK); + FREE(board); return 0; } diff --git a/src/utils.c b/src/utils.c @@ -0,0 +1,29 @@ +#include "utils.h" + +const U64 universe = C64(0xffffffffffffffff); // +const U64 notAFile = C64(0xfefefefefefefefe); // ~0x0101010101010101 +const U64 notHFile = C64(0x7f7f7f7f7f7f7f7f); // ~0x8080808080808080 + +U64 soutOne(U64 b) { return b >> 8; } +U64 nortOne(U64 b) { return b << 8; } +U64 eastOne(U64 b) { return (b & notHFile) << 1; } +U64 westOne(U64 b) { return (b & notAFile) >> 1; } +U64 soEaOne(U64 b) { return (b & notHFile) >> 7; } +U64 soWeOne(U64 b) { return (b & notAFile) >> 9; } +U64 noEaOne(U64 b) { return (b & notHFile) << 9; } +U64 noWeOne(U64 b) { return (b & notAFile) << 7; } + +// board rotation +U64 rotateLeft(U64 x, int s) { return (x << s) | (x >> (64 - s)); } +U64 rotateRight(U64 x, int s) { return (x >> s) | (x << (64 - s)); } + +const char *square_to_coordinates[]={ + "a1", "b1", "c1", "d1", "e1", "f1", "g1", "h1", + "a2", "b2", "c2", "d2", "e2", "f2", "g2", "h2", + "a3", "b3", "c3", "d3", "e3", "f3", "g3", "h3", + "a4", "b4", "c4", "d4", "e4", "f4", "g4", "h4", + "a5", "b5", "c5", "d5", "e5", "f5", "g5", "h5", + "a6", "b6", "c6", "d6", "e6", "f6", "g6", "h6", + "a7", "b7", "c7", "d7", "e7", "f7", "g7", "h7", + "a8", "b8", "c8", "d8", "e8", "f8", "g8", "h8", " " +};