stellarUCI Chess engine written in C++20 |
git clone git://git.dimitrijedobrota.com/stellar.git |
Log | Files | Refs | README | LICENSE | |
commit | 8f954a0404ce9be9d8d5cb9bb59257b6cb5b83f3 |
parent | 227d847754d734b8d663443e463161e4857fac6d |
author | Dimitrije Dobrota <mail@dimitrijedobrota.com> |
date | Sat, 29 Jul 2023 19:13:58 +0200 |
Restructure project for better modularity
Diffstat:M | CMakeLists.txt | | | +-------------- |
D | include/attack.h | | | ----------------- |
D | include/board.h | | | -------------------------------------------------------------------- |
D | include/moves.h | | | ------------------------------------------------------ |
D | include/perft.h | | | --------- |
D | include/score.h | | | ---------- |
D | include/utils.h | | | --------------------------------------------------------------------------------- |
A | src/CMakeLists.txt | | | +++++++++ |
D | src/attack.c | | | --------------------------------------------------------------------------------- |
A | src/attack/CMakeLists.txt | | | +++++++ |
A | src/attack/attack.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/board.c | | | --------------------------------------------------------------------------------- |
A | src/board/CMakeLists.txt | | | +++++++ |
A | src/board/board.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/engine.c | | | --------------------------------------------------------------------------------- |
A | src/engine/CMakeLists.txt | | | +++++++++++++++++ |
A | src/engine/engine.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/include/attack.h | | | ++++++++++++++++++ |
A | src/include/board.h | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/include/moves.h | | | ++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | src/include/perft.h | | | +++++++++ |
A | src/include/score.h | | | ++++++++++ |
A | src/include/utils.h | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/moves.c | | | --------------------------------------------------------------------------------- |
A | src/moves/CMakeLists.txt | | | +++++++ |
A | src/moves/moves.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/perft.c | | | --------------------------------------------------------------------------------- |
A | src/perft/CMakeLists.txt | | | +++++++++++++++++ |
A | src/perft/perft.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/score.c | | | --------------------------------------------------------------------------------- |
A | src/score/CMakeLists.txt | | | +++++++ |
A | src/score/score.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
D | src/utils.c | | | --------------------------------------------------------------------------------- |
A | src/utils/CMakeLists.txt | | | +++++++ |
A | src/utils/utils.c | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
35 files changed, 2024 insertions(+), 1948 deletions(-)
diff --git a/CMakeLists.txt b/CMakeLists.txt
@@ -13,17 +13,4 @@ set(CMAKE_C_STANDARD 99)
set(CMAKE_C_STANDARD_REQUIRED ON)
set(CMAKE_C_EXTENSIONS OFF)
file(GLOB sources "src/*.c")
add_executable(engine ${sources})
target_link_libraries(engine "cul")
set_target_properties(engine PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
)
target_include_directories(engine
PRIVATE "${PROJECT_SOURCE_DIR}/include"
)
add_subdirectory(src)
diff --git a/include/attack.h b/include/attack.h
@@ -1,17 +0,0 @@
#ifndef ATTACK_H
#define ATTACK_H
#include "utils.h"
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/board.h b/include/board.h
@@ -1,68 +0,0 @@
#ifndef CBOARD_H
#define CBOARD_H
#include "utils.h"
enum enumCastle {
WK = 1,
WQ = 2,
BK = 4,
BQ = 8
};
typedef enum enumCastle eCastle;
typedef struct Piece *Piece;
char Piece_asci(Piece self);
char Piece_code(Piece self);
char *Piece_unicode(Piece self);
eColor Piece_color(Piece self);
ePiece Piece_piece(Piece self);
int Piece_index(Piece self);
Piece Piece_get(ePiece piece, eColor color);
Piece Piece_fromCode(char code);
Piece Piece_fromIndex(int index);
ePiece Piece_piece_fromCode(int index);
typedef struct Board *Board;
Board board_new(void);
void board_free(Board *p);
void board_copy(Board self, Board dest);
U64 board_colorBB(Board self, eColor color);
U64 board_occupancy(Board self);
U64 board_pieceBB(Board self, ePiece piece);
eCastle board_castle(Board self);
eColor board_side(Board self);
Square board_enpassant(Board self);
void board_enpassant_set(Board self, Square target);
U64 board_pieceSet(Board self, Piece piece);
U64 board_piece_attacks(Board self, Piece piece, Square src);
void board_piece_capture(Board self, Piece piece, Piece taken, Square source,
Square target);
void board_piece_move(Board self, Piece Piece, Square square, Square target);
void board_piece_pop(Board self, Piece Piece, Square square);
void board_piece_set(Board self, Piece Piece, Square square);
int board_piece_get(Board self, Square square);
U64 board_colorBB_get(Board self, eColor color, Square target);
void board_colorBB_pop(Board self, eColor color, Square target);
void board_colorBB_set(Board self, eColor color, Square target);
void board_castle_and(Board self, int exp);
void board_castle_pop(Board self, eCastle castle);
Piece board_square_piece(Board self, Square square, eColor side);
int board_square_isAttack(Board self, Square square, eColor side);
int board_square_isOccupied(Board self, Square square);
Board board_fromFEN(Board board, char *fen);
int board_isCheck(Board self);
void board_print(Board self);
void board_side_switch(Board self);
#endif
diff --git a/include/moves.h b/include/moves.h
@@ -1,54 +0,0 @@
#ifndef MOVES_H
#define MOVES_H
#include <stdint.h>
#include "board.h"
typedef struct Move Move;
struct Move {
unsigned source : 6;
unsigned target : 6;
unsigned piece : 5;
unsigned piece_capture : 5;
unsigned piece_promote : 5;
unsigned dbl : 1;
unsigned enpassant : 1;
unsigned castle : 1;
unsigned capture : 1;
unsigned promote : 1;
};
typedef struct MoveList *MoveList;
struct MoveList {
Move moves[256];
int count;
};
int move_cmp(Move a, Move b);
Move move_encode(Square src, Square tgt, Piece piece, Piece capture,
Piece promote, int dbl, int enpassant, int castle);
void move_print(Move move);
MoveList move_list_new(void);
void move_list_free(MoveList *p);
Move move_list_move(MoveList self, int index);
int move_list_size(MoveList self);
void move_list_reset(MoveList self);
void move_list_add(MoveList self, Move move);
void move_list_print(MoveList self);
MoveList move_list_generate(MoveList moves, Board board);
int move_make(Move move, Board board, int flag);
#define move_source(move) (move.source)
#define move_target(move) (move.target)
#define move_double(move) (move.dbl)
#define move_enpassant(move) (move.enpassant)
#define move_castle(move) (move.castle)
#define move_capture(move) (move.capture)
#define move_promote(move) (move.promote)
#define move_piece(move) (Piece_fromIndex(move.piece))
#define move_piece_capture(move) (Piece_fromIndex(move.piece_capture))
#define move_piece_promote(move) (Piece_fromIndex(move.piece_promote))
#endif
diff --git a/include/perft.h b/include/perft.h
@@ -1,9 +0,0 @@
#ifndef PERFT_H
#define PERFT_H
#include "board.h"
void perft_test_threaded(Board board, int depth);
void perft_test(Board board, int depth);
#endif
diff --git a/include/score.h b/include/score.h
@@ -1,10 +0,0 @@
#ifndef SCORE_H
#define SCORE_H
#include "board.h"
int Score_capture(ePiece src, ePiece tgt);
int Score_position(ePiece piece, eColor color, Square square);
int Score_value(ePiece piece);
#endif
diff --git a/include/utils.h b/include/utils.h
@@ -1,86 +0,0 @@
#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 functions
#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)))
int bit_count(U64 bitboard);
int bit_lsb_index(U64 bitboard);
#define bitboard_for_each_bit(var, bb) \
for (var = bit_lsb_index(bb); bb; bit_pop(bb, var), var = bit_lsb_index(bb))
void bitboard_print(U64 bitboard);
// 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[];
Square coordinates_to_square(char *cord);
// 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;
int get_time_ms(void);
typedef U64 (*attack_f)(Square square, U64 occupancy);
#endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
@@ -0,0 +1,9 @@
file(GLOB files "*")
foreach(file in ${files})
if(IS_DIRECTORY "${file}" AND EXISTS "${file}/CMakeLists.txt")
get_filename_component(mod ${file} NAME)
message(STATUS "Found target ${mod}")
add_subdirectory(${mod})
endif()
endforeach()
diff --git a/src/attack.c b/src/attack.c
@@ -1,366 +0,0 @@
#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);
}
// 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/attack/CMakeLists.txt b/src/attack/CMakeLists.txt
@@ -0,0 +1,7 @@
add_library(attack OBJECT
attack.c
)
target_include_directories(attack
PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
)
diff --git a/src/attack/attack.c b/src/attack/attack.c
@@ -0,0 +1,371 @@
#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);
}
// 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);
}
void init_attacks(void) {
init_leapers_attacks();
init_sliders_attacks();
}
diff --git a/src/board.c b/src/board.c
@@ -1,280 +0,0 @@
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <cul/assert.h>
#include <cul/mem.h>
#include "attack.h"
#include "board.h"
#include "utils.h"
U64 board_pieceBB_get(Board self, ePiece piece, Square target);
// PIECE
struct Piece {
ePiece piece;
eColor color;
char code;
char asci;
char *unicode;
attack_f attacks;
};
// clang-format off
struct Piece Pieces[2][6] = {
{
[PAWN] = {.color = WHITE, .code = 'P', .asci = 'P', .unicode = "♙ ", .piece = PAWN, .attacks = get_wpawn_attacks},
[KNIGHT] = {.color = WHITE, .code = 'N', .asci = 'N', .unicode = "♘ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = WHITE, .code = 'B', .asci = 'B', .unicode = "♗ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[ROOK] = {.color = WHITE, .code = 'R', .asci = 'R', .unicode = "♖ ", .piece = ROOK, .attacks = get_rook_attacks},
[QUEEN] = {.color = WHITE, .code = 'Q', .asci = 'Q', .unicode = "♕ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = WHITE, .code = 'K', .asci = 'K', .unicode = "♔ ", .piece = KING, .attacks = get_king_attacks},
},
{
[PAWN] = {.color = BLACK, .code = 'p', .asci = 'p', .unicode = "♟ ", .piece = PAWN, .attacks = get_bpawn_attacks},
[KNIGHT] = {.color = BLACK, .code = 'n', .asci = 'n', .unicode = "♞ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = BLACK, .code = 'b', .asci = 'b', .unicode = "♝ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[ROOK] = {.color = BLACK, .code = 'r', .asci = 'r', .unicode = "♜ ", .piece = ROOK, .attacks = get_rook_attacks},
[QUEEN] = {.color = BLACK, .code = 'q', .asci = 'q', .unicode = "♛ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = BLACK, .code = 'k', .asci = 'k', .unicode = "♚ ", .piece = KING, .attacks = get_king_attacks},
},
};
// clang-format on
attack_f Piece_attacks(Piece self) { return self->attacks; }
char Piece_asci(Piece self) { return self->asci; }
char Piece_code(Piece self) { return self->code; }
char *Piece_unicode(Piece self) { return self->unicode; }
eColor Piece_color(Piece self) { return self->color; }
ePiece Piece_piece(Piece self) { return self->piece; }
int Piece_index(Piece self) { return self->color * 8 + self->piece; }
Piece Piece_fromCode(char code) {
int color = (isupper(code)) ? WHITE : BLACK;
for (int i = 0; i < 6; i++)
if (Pieces[color][i].code == code) return &Pieces[color][i];
return NULL;
}
ePiece Piece_piece_fromCode(int index) {
return Pieces[WHITE][index % 8].piece;
}
Piece Piece_fromIndex(int index) { return &Pieces[index / 8][index % 8]; }
Piece Piece_get(ePiece piece, eColor color) { return &Pieces[color][piece]; }
// CBOARD
struct Board {
U64 colorBB[2];
U64 pieceBB[6];
eColor side;
Square enpassant;
eCastle castle;
};
Board board_new(void) {
Board p;
NEW0(p);
return p;
}
void board_free(Board *p) { FREE(*p); }
void board_copy(Board self, Board dest) { *dest = *self; }
Square board_enpassant(Board self) { return self->enpassant; }
eCastle board_castle(Board self) { return self->castle; }
eColor board_side(Board self) { return self->side; }
U64 board_colorBB(Board self, eColor color) { return self->colorBB[color]; }
U64 board_pieceBB(Board self, ePiece piece) { return self->pieceBB[piece]; }
U64 board_occupancy(Board self) {
return self->colorBB[WHITE] | self->colorBB[BLACK];
}
U64 board_pieceBB_get(Board self, ePiece piece, Square target) {
return bit_get(self->pieceBB[piece], target);
}
U64 board_pieceSet(Board self, Piece piece) {
return self->pieceBB[Piece_piece(piece)] &
self->colorBB[Piece_color(piece)];
}
void board_enpassant_set(Board self, Square target) {
self->enpassant = target;
}
void board_colorBB_pop(Board self, eColor color, Square target) {
bit_pop(self->colorBB[color], target);
}
void board_colorBB_set(Board self, eColor color, Square target) {
bit_set(self->colorBB[color], target);
}
U64 board_colorBB_get(Board self, eColor color, Square target) {
return bit_get(self->colorBB[color], target);
}
int board_piece_get(Board self, Square square) {
for (int i = 0; i < 6; i++)
if (bit_get(self->pieceBB[i], square)) return i;
return -1;
}
void board_piece_pop(Board self, Piece Piece, Square square) {
bit_pop(self->pieceBB[Piece->piece], square);
bit_pop(self->colorBB[Piece->color], square);
}
void board_piece_set(Board self, Piece Piece, Square square) {
bit_set(self->pieceBB[Piece->piece], square);
bit_set(self->colorBB[Piece->color], square);
}
void board_piece_move(Board self, Piece Piece, Square source, Square target) {
board_piece_pop(self, Piece, source);
board_piece_set(self, Piece, target);
}
U64 board_piece_attacks(Board self, Piece Piece, Square src) {
return Piece_attacks(Piece)(src, board_occupancy(self));
}
void board_piece_capture(Board self, Piece piece, Piece taken, Square source,
Square target) {
board_piece_pop(self, piece, source);
if (taken) board_piece_pop(self, taken, target);
board_piece_set(self, piece, target);
}
void board_castle_pop(Board self, eCastle castle) {
bit_pop(self->castle, bit_lsb_index(castle));
}
void board_castle_and(Board self, int exp) { self->castle &= exp; }
void board_side_switch(Board self) { self->side = !self->side; }
int board_isCheck(Board self) {
U64 king = self->pieceBB[KING] & self->colorBB[self->side];
return board_square_isAttack(self, bit_lsb_index(king), !self->side);
}
int board_square_isOccupied(Board self, Square square) {
return bit_get(board_occupancy(self), square);
}
int board_square_isAttack(Board 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;
}
Piece board_square_piece(Board self, Square square, eColor color) {
for (ePiece i = 0; i < 6; i++)
if (board_pieceBB_get(self, i, square)) return Piece_get(i, color);
return NULL;
}
void board_print(Board self) {
for (int rank = 0; rank < 8; rank++) {
for (int file = 0; file < 8; file++) {
Square square = (7 - rank) * 8 + file;
Piece piece = NULL;
int color = -1;
if (bit_get(self->colorBB[WHITE], square))
color = WHITE;
else if (bit_get(self->colorBB[BLACK], square))
color = BLACK;
if (color != -1) {
for (int piece_index = 0; piece_index < 6; piece_index++) {
if (bit_get(self->pieceBB[piece_index], square)) {
piece = &Pieces[color][piece_index];
break;
}
}
}
if (!file) printf(" %d ", 8 - rank);
printf("%s", (piece) ? Piece_unicode(piece) : ". ");
}
printf("\n");
}
printf(" A B C D E F G H\n");
printf(" Side: %s\n", (self->side == WHITE) ? "white" : "black");
printf("Enpassant: %s\n", square_to_coordinates[self->enpassant]);
printf(" Castling: %c%c%c%c\n", (self->castle & WK) ? 'K' : '-',
(self->castle & WQ) ? 'Q' : '-', (self->castle & BK) ? 'k' : '-',
(self->castle & BQ) ? 'q' : '-');
printf("\n");
}
Board board_fromFEN(Board board, char *fen) {
if (!board) NEW(board);
memset(board, 0, sizeof(*board));
board->side = -1;
board->enpassant = no_sq;
board->castle = 0;
int file = 0, rank = 7;
for (Piece piece; *fen != ' '; fen++) {
Square square = rank * 8 + file;
if (isalpha(*fen)) {
if (!(piece = Piece_fromCode(*fen))) assert(0);
bit_set(board->colorBB[piece->color], square);
bit_set(board->pieceBB[piece->piece], square);
file++;
} else if (isdigit(*fen)) {
file += *fen - '0';
} else if (*fen == '/') {
file = 0;
rank--;
} else
assert(0);
}
fen++;
if (*fen == 'w')
board->side = WHITE;
else if (*fen == 'b')
board->side = BLACK;
else
assert(0);
for (fen += 2; *fen != ' '; fen++) {
switch (*fen) {
case 'K':
board->castle |= WK;
break;
case 'Q':
board->castle |= WQ;
break;
case 'k':
board->castle |= BK;
break;
case 'q':
board->castle |= BQ;
break;
case '-':
break;
default:
assert(0);
}
}
fen++;
if (*fen != '-') {
board->enpassant = coordinates_to_square(fen);
}
return board;
}
diff --git a/src/board/CMakeLists.txt b/src/board/CMakeLists.txt
@@ -0,0 +1,7 @@
add_library(board OBJECT
board.c
)
target_include_directories(board
PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
)
diff --git a/src/board/board.c b/src/board/board.c
@@ -0,0 +1,280 @@
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <cul/assert.h>
#include <cul/mem.h>
#include "attack.h"
#include "board.h"
#include "utils.h"
U64 board_pieceBB_get(Board self, ePiece piece, Square target);
// PIECE
struct Piece {
ePiece piece;
eColor color;
char code;
char asci;
char *unicode;
attack_f attacks;
};
// clang-format off
struct Piece Pieces[2][6] = {
{
[PAWN] = {.color = WHITE, .code = 'P', .asci = 'P', .unicode = "♙ ", .piece = PAWN, .attacks = get_wpawn_attacks},
[KNIGHT] = {.color = WHITE, .code = 'N', .asci = 'N', .unicode = "♘ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = WHITE, .code = 'B', .asci = 'B', .unicode = "♗ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[ROOK] = {.color = WHITE, .code = 'R', .asci = 'R', .unicode = "♖ ", .piece = ROOK, .attacks = get_rook_attacks},
[QUEEN] = {.color = WHITE, .code = 'Q', .asci = 'Q', .unicode = "♕ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = WHITE, .code = 'K', .asci = 'K', .unicode = "♔ ", .piece = KING, .attacks = get_king_attacks},
},
{
[PAWN] = {.color = BLACK, .code = 'p', .asci = 'p', .unicode = "♟ ", .piece = PAWN, .attacks = get_bpawn_attacks},
[KNIGHT] = {.color = BLACK, .code = 'n', .asci = 'n', .unicode = "♞ ", .piece = KNIGHT, .attacks = get_knight_attacks},
[BISHOP] = {.color = BLACK, .code = 'b', .asci = 'b', .unicode = "♝ ", .piece = BISHOP, .attacks = get_bishop_attacks},
[ROOK] = {.color = BLACK, .code = 'r', .asci = 'r', .unicode = "♜ ", .piece = ROOK, .attacks = get_rook_attacks},
[QUEEN] = {.color = BLACK, .code = 'q', .asci = 'q', .unicode = "♛ ", .piece = QUEEN, .attacks = get_queen_attacks},
[KING] = {.color = BLACK, .code = 'k', .asci = 'k', .unicode = "♚ ", .piece = KING, .attacks = get_king_attacks},
},
};
// clang-format on
attack_f Piece_attacks(Piece self) { return self->attacks; }
char Piece_asci(Piece self) { return self->asci; }
char Piece_code(Piece self) { return self->code; }
char *Piece_unicode(Piece self) { return self->unicode; }
eColor Piece_color(Piece self) { return self->color; }
ePiece Piece_piece(Piece self) { return self->piece; }
int Piece_index(Piece self) { return self->color * 8 + self->piece; }
Piece Piece_fromCode(char code) {
int color = (isupper(code)) ? WHITE : BLACK;
for (int i = 0; i < 6; i++)
if (Pieces[color][i].code == code) return &Pieces[color][i];
return NULL;
}
ePiece Piece_piece_fromCode(int index) {
return Pieces[WHITE][index % 8].piece;
}
Piece Piece_fromIndex(int index) { return &Pieces[index / 8][index % 8]; }
Piece Piece_get(ePiece piece, eColor color) { return &Pieces[color][piece]; }
// CBOARD
struct Board {
U64 colorBB[2];
U64 pieceBB[6];
eColor side;
Square enpassant;
eCastle castle;
};
Board board_new(void) {
Board p;
NEW0(p);
return p;
}
void board_free(Board *p) { FREE(*p); }
void board_copy(Board self, Board dest) { *dest = *self; }
Square board_enpassant(Board self) { return self->enpassant; }
eCastle board_castle(Board self) { return self->castle; }
eColor board_side(Board self) { return self->side; }
U64 board_colorBB(Board self, eColor color) { return self->colorBB[color]; }
U64 board_pieceBB(Board self, ePiece piece) { return self->pieceBB[piece]; }
U64 board_occupancy(Board self) {
return self->colorBB[WHITE] | self->colorBB[BLACK];
}
U64 board_pieceBB_get(Board self, ePiece piece, Square target) {
return bit_get(self->pieceBB[piece], target);
}
U64 board_pieceSet(Board self, Piece piece) {
return self->pieceBB[Piece_piece(piece)] &
self->colorBB[Piece_color(piece)];
}
void board_enpassant_set(Board self, Square target) {
self->enpassant = target;
}
void board_colorBB_pop(Board self, eColor color, Square target) {
bit_pop(self->colorBB[color], target);
}
void board_colorBB_set(Board self, eColor color, Square target) {
bit_set(self->colorBB[color], target);
}
U64 board_colorBB_get(Board self, eColor color, Square target) {
return bit_get(self->colorBB[color], target);
}
int board_piece_get(Board self, Square square) {
for (int i = 0; i < 6; i++)
if (bit_get(self->pieceBB[i], square)) return i;
return -1;
}
void board_piece_pop(Board self, Piece Piece, Square square) {
bit_pop(self->pieceBB[Piece->piece], square);
bit_pop(self->colorBB[Piece->color], square);
}
void board_piece_set(Board self, Piece Piece, Square square) {
bit_set(self->pieceBB[Piece->piece], square);
bit_set(self->colorBB[Piece->color], square);
}
void board_piece_move(Board self, Piece Piece, Square source, Square target) {
board_piece_pop(self, Piece, source);
board_piece_set(self, Piece, target);
}
U64 board_piece_attacks(Board self, Piece Piece, Square src) {
return Piece_attacks(Piece)(src, board_occupancy(self));
}
void board_piece_capture(Board self, Piece piece, Piece taken, Square source,
Square target) {
board_piece_pop(self, piece, source);
if (taken) board_piece_pop(self, taken, target);
board_piece_set(self, piece, target);
}
void board_castle_pop(Board self, eCastle castle) {
bit_pop(self->castle, bit_lsb_index(castle));
}
void board_castle_and(Board self, int exp) { self->castle &= exp; }
void board_side_switch(Board self) { self->side = !self->side; }
int board_isCheck(Board self) {
U64 king = self->pieceBB[KING] & self->colorBB[self->side];
return board_square_isAttack(self, bit_lsb_index(king), !self->side);
}
int board_square_isOccupied(Board self, Square square) {
return bit_get(board_occupancy(self), square);
}
int board_square_isAttack(Board 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;
}
Piece board_square_piece(Board self, Square square, eColor color) {
for (ePiece i = 0; i < 6; i++)
if (board_pieceBB_get(self, i, square)) return Piece_get(i, color);
return NULL;
}
void board_print(Board self) {
for (int rank = 0; rank < 8; rank++) {
for (int file = 0; file < 8; file++) {
Square square = (7 - rank) * 8 + file;
Piece piece = NULL;
int color = -1;
if (bit_get(self->colorBB[WHITE], square))
color = WHITE;
else if (bit_get(self->colorBB[BLACK], square))
color = BLACK;
if (color != -1) {
for (int piece_index = 0; piece_index < 6; piece_index++) {
if (bit_get(self->pieceBB[piece_index], square)) {
piece = &Pieces[color][piece_index];
break;
}
}
}
if (!file) printf(" %d ", 8 - rank);
printf("%s", (piece) ? Piece_unicode(piece) : ". ");
}
printf("\n");
}
printf(" A B C D E F G H\n");
printf(" Side: %s\n", (self->side == WHITE) ? "white" : "black");
printf("Enpassant: %s\n", square_to_coordinates[self->enpassant]);
printf(" Castling: %c%c%c%c\n", (self->castle & WK) ? 'K' : '-',
(self->castle & WQ) ? 'Q' : '-', (self->castle & BK) ? 'k' : '-',
(self->castle & BQ) ? 'q' : '-');
printf("\n");
}
Board board_fromFEN(Board board, char *fen) {
if (!board) NEW(board);
memset(board, 0, sizeof(*board));
board->side = -1;
board->enpassant = no_sq;
board->castle = 0;
int file = 0, rank = 7;
for (Piece piece; *fen != ' '; fen++) {
Square square = rank * 8 + file;
if (isalpha(*fen)) {
if (!(piece = Piece_fromCode(*fen))) assert(0);
bit_set(board->colorBB[piece->color], square);
bit_set(board->pieceBB[piece->piece], square);
file++;
} else if (isdigit(*fen)) {
file += *fen - '0';
} else if (*fen == '/') {
file = 0;
rank--;
} else
assert(0);
}
fen++;
if (*fen == 'w')
board->side = WHITE;
else if (*fen == 'b')
board->side = BLACK;
else
assert(0);
for (fen += 2; *fen != ' '; fen++) {
switch (*fen) {
case 'K':
board->castle |= WK;
break;
case 'Q':
board->castle |= WQ;
break;
case 'k':
board->castle |= BK;
break;
case 'q':
board->castle |= BQ;
break;
case '-':
break;
default:
assert(0);
}
}
fen++;
if (*fen != '-') {
board->enpassant = coordinates_to_square(fen);
}
return board;
}
diff --git a/src/engine.c b/src/engine.c
@@ -1,442 +0,0 @@
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "board.h"
#include "attack.h"
#include "moves.h"
#include "perft.h"
#include "score.h"
#include "utils.h"
#include <cul/assert.h>
#include <cul/mem.h>
/* DEBUGGING */
// FEN debug positions
#define empty_board "8/8/8/8/8/8/8/8 w - - "
#define start_position \
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 "
#define tricky_position \
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 "
#define killer_position \
"rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1"
#define cmk_position \
"r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 "
#define MAX_PLY 64
typedef struct Stats_T *Stats_T;
struct Stats_T {
long nodes;
int ply;
int pv_length[MAX_PLY];
Move pv_table[MAX_PLY][MAX_PLY];
Move killer_moves[2][MAX_PLY];
U32 history_moves[16][64];
};
Stats_T Stats_new(void) {
Stats_T p;
NEW0(p);
return p;
}
void Stats_free(Stats_T *p) { FREE(*p); }
int move_score(Stats_T stats, Move move) {
if (move_capture(move)) {
return Score_capture(Piece_piece(move_piece(move)),
Piece_piece(move_piece_capture(move)));
} else {
if (!move_cmp(stats->killer_moves[0][stats->ply], move))
return 9000;
else if (!move_cmp(stats->killer_moves[1][stats->ply], move))
return 8000;
else
return stats->history_moves[Piece_index(move_piece(move))]
[move_target(move)];
}
return 0;
}
void move_list_sort(Stats_T stats, MoveList list) {
int score[list->count];
for (int i = 0; i < list->count; i++)
score[i] = move_score(stats, list->moves[i]);
for (int i = 0; i < list->count; i++)
for (int j = i + 1; j < list->count; j++)
if (score[i] < score[j]) {
Move t = list->moves[i];
list->moves[i] = list->moves[j];
list->moves[j] = t;
int s = score[i];
score[i] = score[j];
score[j] = s;
}
}
/* SEARCHING */
int evaluate(Board board) {
Square square;
eColor side = board_side(board);
U64 occupancy = board_colorBB(board, side);
int score = 0;
for (int i = 0; i < 6; i++) {
U64 bitboard = board_pieceBB(board, i);
bitboard_for_each_bit(square, bitboard) {
if (bit_get(occupancy, square)) {
score += Score_value(i);
score += Score_position(i, side, square);
} else {
score -= Score_value(i);
score -= Score_position(i, !side, square);
}
}
}
return score;
}
int quiescence(Stats_T stats, Board board, int alpha, int beta) {
MoveList moves;
Board copy;
int eval = evaluate(board);
stats->nodes++;
if (eval >= beta) {
return beta;
}
if (eval > alpha) {
alpha = eval;
}
copy = board_new();
moves = move_list_generate(NULL, board);
move_list_sort(stats, moves);
for (int i = 0; i < move_list_size(moves); i++) {
board_copy(board, copy);
if (move_make(move_list_move(moves, i), copy, 1) == 0) continue;
stats->ply++;
int score = -quiescence(stats, copy, -beta, -alpha);
stats->ply--;
if (score >= beta) {
move_list_free(&moves);
board_free(©);
return beta;
}
if (score > alpha) {
alpha = score;
}
}
move_list_free(&moves);
board_free(©);
return alpha;
}
int negamax(Stats_T stats, Board board, int alpha, int beta, int depth) {
MoveList list;
Board copy;
int isCheck = 0;
int ply = stats->ply;
stats->pv_length[ply] = ply;
if (depth == 0) return quiescence(stats, board, alpha, beta);
if (ply > MAX_PLY - 1) return evaluate(board);
stats->nodes++;
copy = board_new();
list = move_list_generate(NULL, board);
isCheck = board_isCheck(board);
if (isCheck) depth++;
int legal_moves = 0;
move_list_sort(stats, list);
for (int i = 0; i < move_list_size(list); i++) {
Move move = move_list_move(list, i);
board_copy(board, copy);
if (move_make(move, copy, 0) == 0) {
continue;
}
stats->ply++;
int score = -negamax(stats, copy, -beta, -alpha, depth - 1);
stats->ply--;
legal_moves++;
if (score >= beta) {
if (!move_capture(move)) {
stats->killer_moves[1][ply] = stats->killer_moves[0][ply];
stats->killer_moves[0][ply] = move;
}
move_list_free(&list);
board_free(©);
return beta;
}
if (score > alpha) {
if (!move_capture(move))
stats->history_moves[Piece_index(move_piece(move))]
[move_target(move)] += depth;
alpha = score;
stats->pv_table[ply][ply] = move;
for (int i = stats->ply + 1; i < stats->pv_length[ply + 1]; i++)
stats->pv_table[ply][i] = stats->pv_table[ply + 1][i];
stats->pv_length[ply] = stats->pv_length[ply + 1];
}
}
if (legal_moves == 0) {
if (isCheck)
return -49000 + stats->ply;
else
return 0;
}
move_list_free(&list);
board_free(©);
return alpha;
}
void move_print_UCI(Move move) {
printf("%s%s", square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)]);
if (move_promote(move)) printf(" %c", Piece_asci(move_piece_promote(move)));
}
void search_position(Board board, int depth) {
Stats_T stats = Stats_new();
for (int crnt = 1; crnt <= depth; crnt++) {
int score = negamax(stats, board, -50000, 50000, crnt);
printf("info score cp %d depth %d nodes %ld pv ", score, crnt,
stats->nodes);
for (int i = 0; i < stats->pv_length[0]; i++) {
move_print_UCI(stats->pv_table[0][i]);
printf(" ");
}
printf("\n");
}
printf("bestmove ");
move_print_UCI(stats->pv_table[0][0]);
printf("\n");
Stats_free(&stats);
}
void print_info(void) {
printf("id name Stellar\n");
printf("id author Dimitrije Dobrota\n");
printf("uciok\n");
}
typedef struct Instruction_T *Instruction_T;
struct Instruction_T {
char *command;
char *token;
char *crnt;
};
char *Instruction_token_next(Instruction_T self);
Instruction_T Instruction_new(char *command) {
Instruction_T p;
NEW0(p);
p->command = ALLOC(strlen(command) + 1);
p->token = ALLOC(strlen(command) + 1);
strcpy(p->command, command);
p->crnt = command;
Instruction_token_next(p);
return p;
}
void Instruction_free(Instruction_T *p) {
FREE((*p)->command);
FREE((*p)->token);
FREE(*p);
}
char *Instruction_token(Instruction_T self) { return self->token; }
char *Instruction_token_n(Instruction_T self, int n) {
while (isspace(*self->crnt) && *self->crnt != '\0')
self->crnt++;
if (*self->crnt == '\0') {
*self->token = '\0';
return NULL;
}
char *p = self->token;
while (n--) {
while (!isspace(*self->crnt) && *self->crnt != '\0' &&
*self->crnt != ';')
*p++ = *self->crnt++;
if (*self->crnt == '\0') {
p++;
break;
}
self->crnt++;
*p++ = ' ';
}
*--p = '\0';
return self->token;
}
char *Instruction_token_next(Instruction_T self) {
return Instruction_token_n(self, 1);
}
Move parse_move(Board board, char *move_string) {
Move result = {0};
MoveList moves;
Square source, target;
source = coordinates_to_square(move_string);
target = coordinates_to_square(move_string + 2);
moves = move_list_generate(NULL, board);
for (int i = 0; i < moves->count; i++) {
Move move = moves->moves[i];
if (move_source(move) == source && move_target(move) == target) {
if (move_string[4]) {
if (tolower(Piece_code(move_piece_promote(move))) !=
move_string[4])
continue;
}
result = move;
break;
}
}
move_list_free(&moves);
return result;
}
Board Instruction_parse(Instruction_T self, Board board) {
char *token = Instruction_token(self);
if (!board) board = board_new();
do {
if (strcmp(token, "ucinewgame") == 0) {
board = board_fromFEN(board, start_position);
continue;
}
if (strcmp(token, "quit") == 0) return NULL;
if (strcmp(token, "position") == 0) {
token = Instruction_token_next(self);
if (token && strcmp(token, "startpos") == 0) {
board = board_fromFEN(board, start_position);
} else if (token && strcmp(token, "fen") == 0) {
token = Instruction_token_n(self, 6);
board = board_fromFEN(board, token);
} else {
printf("Unknown argument after position\n");
}
// board_print(board);
continue;
}
if (strcmp(token, "moves") == 0) {
while ((token = Instruction_token_next(self))) {
Move move = parse_move(board, token);
if (!move_cmp(move, (Move){0})) {
move_make(move, board, 0);
} else {
printf("Invalid move %s!\n", token);
}
}
// board_print(board);
return board;
}
if (strcmp(token, "go") == 0) {
token = Instruction_token_next(self);
int depth = 5;
if (token && strcmp(token, "depth") == 0) {
token = Instruction_token_next(self);
depth = atoi(token);
} else {
printf("Unknown argument after go\n");
}
search_position(board, depth);
continue;
}
if (strcmp(token, "isready") == 0) {
printf("readyok\n");
continue;
}
if (strcmp(token, "uci") == 0) {
print_info();
continue;
}
} while ((token = Instruction_token_next(self)));
return board;
}
void uci_loop(void) {
Board board = NULL;
Instruction_T instruction;
char input[200000];
setbuf(stdin, NULL);
setbuf(stdout, NULL);
print_info();
while (1) {
memset(input, 0, sizeof(input));
fflush(stdout);
if (!fgets(input, sizeof(input), stdin)) continue;
instruction = Instruction_new(input);
if (!(board = Instruction_parse(instruction, board))) break;
Instruction_free(&instruction);
}
Instruction_free(&instruction);
board_free(&board);
}
/* MAIN */
void init_all() {
init_leapers_attacks();
init_sliders_attacks();
}
int main(void) {
init_all();
uci_loop();
return 0;
}
diff --git a/src/engine/CMakeLists.txt b/src/engine/CMakeLists.txt
@@ -0,0 +1,17 @@
add_executable(engine engine.c)
target_link_libraries(engine
PRIVATE attack
PRIVATE board
PRIVATE moves
PRIVATE score
PRIVATE utils
)
target_link_libraries(engine PRIVATE "cul")
set_target_properties(engine PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
diff --git a/src/engine/engine.c b/src/engine/engine.c
@@ -0,0 +1,425 @@
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "attack.h"
#include "board.h"
#include "moves.h"
#include "perft.h"
#include "score.h"
#include "utils.h"
#include <cul/assert.h>
#include <cul/mem.h>
#define MAX_PLY 64
typedef struct Stats_T *Stats_T;
struct Stats_T {
long nodes;
int ply;
int pv_length[MAX_PLY];
Move pv_table[MAX_PLY][MAX_PLY];
Move killer_moves[2][MAX_PLY];
U32 history_moves[16][64];
};
Stats_T Stats_new(void) {
Stats_T p;
NEW0(p);
return p;
}
void Stats_free(Stats_T *p) { FREE(*p); }
int move_score(Stats_T stats, Move move) {
if (move_capture(move)) {
return Score_capture(Piece_piece(move_piece(move)),
Piece_piece(move_piece_capture(move)));
} else {
if (!move_cmp(stats->killer_moves[0][stats->ply], move))
return 9000;
else if (!move_cmp(stats->killer_moves[1][stats->ply], move))
return 8000;
else
return stats->history_moves[Piece_index(move_piece(move))]
[move_target(move)];
}
return 0;
}
void move_list_sort(Stats_T stats, MoveList list) {
int score[list->count];
for (int i = 0; i < list->count; i++)
score[i] = move_score(stats, list->moves[i]);
for (int i = 0; i < list->count; i++)
for (int j = i + 1; j < list->count; j++)
if (score[i] < score[j]) {
Move t = list->moves[i];
list->moves[i] = list->moves[j];
list->moves[j] = t;
int s = score[i];
score[i] = score[j];
score[j] = s;
}
}
/* SEARCHING */
int evaluate(Board board) {
Square square;
eColor side = board_side(board);
U64 occupancy = board_colorBB(board, side);
int score = 0;
for (int i = 0; i < 6; i++) {
U64 bitboard = board_pieceBB(board, i);
bitboard_for_each_bit(square, bitboard) {
if (bit_get(occupancy, square)) {
score += Score_value(i);
score += Score_position(i, side, square);
} else {
score -= Score_value(i);
score -= Score_position(i, !side, square);
}
}
}
return score;
}
int quiescence(Stats_T stats, Board board, int alpha, int beta) {
MoveList moves;
Board copy;
int eval = evaluate(board);
stats->nodes++;
if (eval >= beta) {
return beta;
}
if (eval > alpha) {
alpha = eval;
}
copy = board_new();
moves = move_list_generate(NULL, board);
move_list_sort(stats, moves);
for (int i = 0; i < move_list_size(moves); i++) {
board_copy(board, copy);
if (move_make(move_list_move(moves, i), copy, 1) == 0) continue;
stats->ply++;
int score = -quiescence(stats, copy, -beta, -alpha);
stats->ply--;
if (score >= beta) {
move_list_free(&moves);
board_free(©);
return beta;
}
if (score > alpha) {
alpha = score;
}
}
move_list_free(&moves);
board_free(©);
return alpha;
}
int negamax(Stats_T stats, Board board, int alpha, int beta, int depth) {
MoveList list;
Board copy;
int isCheck = 0;
int ply = stats->ply;
stats->pv_length[ply] = ply;
if (depth == 0) return quiescence(stats, board, alpha, beta);
if (ply > MAX_PLY - 1) return evaluate(board);
stats->nodes++;
copy = board_new();
list = move_list_generate(NULL, board);
isCheck = board_isCheck(board);
if (isCheck) depth++;
int legal_moves = 0;
move_list_sort(stats, list);
for (int i = 0; i < move_list_size(list); i++) {
Move move = move_list_move(list, i);
board_copy(board, copy);
if (move_make(move, copy, 0) == 0) {
continue;
}
stats->ply++;
int score = -negamax(stats, copy, -beta, -alpha, depth - 1);
stats->ply--;
legal_moves++;
if (score >= beta) {
if (!move_capture(move)) {
stats->killer_moves[1][ply] = stats->killer_moves[0][ply];
stats->killer_moves[0][ply] = move;
}
move_list_free(&list);
board_free(©);
return beta;
}
if (score > alpha) {
if (!move_capture(move))
stats->history_moves[Piece_index(move_piece(move))]
[move_target(move)] += depth;
alpha = score;
stats->pv_table[ply][ply] = move;
for (int i = stats->ply + 1; i < stats->pv_length[ply + 1]; i++)
stats->pv_table[ply][i] = stats->pv_table[ply + 1][i];
stats->pv_length[ply] = stats->pv_length[ply + 1];
}
}
if (legal_moves == 0) {
if (isCheck)
return -49000 + stats->ply;
else
return 0;
}
move_list_free(&list);
board_free(©);
return alpha;
}
void move_print_UCI(Move move) {
printf("%s%s", square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)]);
if (move_promote(move)) printf(" %c", Piece_asci(move_piece_promote(move)));
}
void search_position(Board board, int depth) {
Stats_T stats = Stats_new();
for (int crnt = 1; crnt <= depth; crnt++) {
int score = negamax(stats, board, -50000, 50000, crnt);
printf("info score cp %d depth %d nodes %ld pv ", score, crnt,
stats->nodes);
for (int i = 0; i < stats->pv_length[0]; i++) {
move_print_UCI(stats->pv_table[0][i]);
printf(" ");
}
printf("\n");
}
printf("bestmove ");
move_print_UCI(stats->pv_table[0][0]);
printf("\n");
Stats_free(&stats);
}
void print_info(void) {
printf("id name Stellar\n");
printf("id author Dimitrije Dobrota\n");
printf("uciok\n");
}
typedef struct Instruction_T *Instruction_T;
struct Instruction_T {
char *command;
char *token;
char *crnt;
};
char *Instruction_token_next(Instruction_T self);
Instruction_T Instruction_new(char *command) {
Instruction_T p;
NEW0(p);
p->command = ALLOC(strlen(command) + 1);
p->token = ALLOC(strlen(command) + 1);
strcpy(p->command, command);
p->crnt = command;
Instruction_token_next(p);
return p;
}
void Instruction_free(Instruction_T *p) {
FREE((*p)->command);
FREE((*p)->token);
FREE(*p);
}
char *Instruction_token(Instruction_T self) { return self->token; }
char *Instruction_token_n(Instruction_T self, int n) {
while (isspace(*self->crnt) && *self->crnt != '\0')
self->crnt++;
if (*self->crnt == '\0') {
*self->token = '\0';
return NULL;
}
char *p = self->token;
while (n--) {
while (!isspace(*self->crnt) && *self->crnt != '\0' &&
*self->crnt != ';')
*p++ = *self->crnt++;
if (*self->crnt == '\0') {
p++;
break;
}
self->crnt++;
*p++ = ' ';
}
*--p = '\0';
return self->token;
}
char *Instruction_token_next(Instruction_T self) {
return Instruction_token_n(self, 1);
}
Move parse_move(Board board, char *move_string) {
Move result = {0};
MoveList moves;
Square source, target;
source = coordinates_to_square(move_string);
target = coordinates_to_square(move_string + 2);
moves = move_list_generate(NULL, board);
for (int i = 0; i < moves->count; i++) {
Move move = moves->moves[i];
if (move_source(move) == source && move_target(move) == target) {
if (move_string[4]) {
if (tolower(Piece_code(move_piece_promote(move))) !=
move_string[4])
continue;
}
result = move;
break;
}
}
move_list_free(&moves);
return result;
}
Board Instruction_parse(Instruction_T self, Board board) {
char *token = Instruction_token(self);
if (!board) board = board_new();
do {
if (strcmp(token, "ucinewgame") == 0) {
board = board_fromFEN(board, start_position);
continue;
}
if (strcmp(token, "quit") == 0) return NULL;
if (strcmp(token, "position") == 0) {
token = Instruction_token_next(self);
if (token && strcmp(token, "startpos") == 0) {
board = board_fromFEN(board, start_position);
} else if (token && strcmp(token, "fen") == 0) {
token = Instruction_token_n(self, 6);
board = board_fromFEN(board, token);
} else {
printf("Unknown argument after position\n");
}
// board_print(board);
continue;
}
if (strcmp(token, "moves") == 0) {
while ((token = Instruction_token_next(self))) {
Move move = parse_move(board, token);
if (!move_cmp(move, (Move){0})) {
move_make(move, board, 0);
} else {
printf("Invalid move %s!\n", token);
}
}
// board_print(board);
return board;
}
if (strcmp(token, "go") == 0) {
token = Instruction_token_next(self);
int depth = 5;
if (token && strcmp(token, "depth") == 0) {
token = Instruction_token_next(self);
depth = atoi(token);
} else {
printf("Unknown argument after go\n");
}
search_position(board, depth);
continue;
}
if (strcmp(token, "isready") == 0) {
printf("readyok\n");
continue;
}
if (strcmp(token, "uci") == 0) {
print_info();
continue;
}
} while ((token = Instruction_token_next(self)));
return board;
}
void uci_loop(void) {
Board board = NULL;
Instruction_T instruction;
char input[200000];
setbuf(stdin, NULL);
setbuf(stdout, NULL);
print_info();
while (1) {
memset(input, 0, sizeof(input));
fflush(stdout);
if (!fgets(input, sizeof(input), stdin)) continue;
instruction = Instruction_new(input);
if (!(board = Instruction_parse(instruction, board))) break;
Instruction_free(&instruction);
}
Instruction_free(&instruction);
board_free(&board);
}
/* MAIN */
int main(void) {
init_attacks();
uci_loop();
return 0;
}
diff --git a/src/include/attack.h b/src/include/attack.h
@@ -0,0 +1,18 @@
#ifndef ATTACK_H
#define ATTACK_H
#include "utils.h"
void init_attacks(void);
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/src/include/board.h b/src/include/board.h
@@ -0,0 +1,68 @@
#ifndef CBOARD_H
#define CBOARD_H
#include "utils.h"
enum enumCastle {
WK = 1,
WQ = 2,
BK = 4,
BQ = 8
};
typedef enum enumCastle eCastle;
typedef struct Piece *Piece;
char Piece_asci(Piece self);
char Piece_code(Piece self);
char *Piece_unicode(Piece self);
eColor Piece_color(Piece self);
ePiece Piece_piece(Piece self);
int Piece_index(Piece self);
Piece Piece_get(ePiece piece, eColor color);
Piece Piece_fromCode(char code);
Piece Piece_fromIndex(int index);
ePiece Piece_piece_fromCode(int index);
typedef struct Board *Board;
Board board_new(void);
void board_free(Board *p);
void board_copy(Board self, Board dest);
U64 board_colorBB(Board self, eColor color);
U64 board_occupancy(Board self);
U64 board_pieceBB(Board self, ePiece piece);
eCastle board_castle(Board self);
eColor board_side(Board self);
Square board_enpassant(Board self);
void board_enpassant_set(Board self, Square target);
U64 board_pieceSet(Board self, Piece piece);
U64 board_piece_attacks(Board self, Piece piece, Square src);
void board_piece_capture(Board self, Piece piece, Piece taken, Square source,
Square target);
void board_piece_move(Board self, Piece Piece, Square square, Square target);
void board_piece_pop(Board self, Piece Piece, Square square);
void board_piece_set(Board self, Piece Piece, Square square);
int board_piece_get(Board self, Square square);
U64 board_colorBB_get(Board self, eColor color, Square target);
void board_colorBB_pop(Board self, eColor color, Square target);
void board_colorBB_set(Board self, eColor color, Square target);
void board_castle_and(Board self, int exp);
void board_castle_pop(Board self, eCastle castle);
Piece board_square_piece(Board self, Square square, eColor side);
int board_square_isAttack(Board self, Square square, eColor side);
int board_square_isOccupied(Board self, Square square);
Board board_fromFEN(Board board, char *fen);
int board_isCheck(Board self);
void board_print(Board self);
void board_side_switch(Board self);
#endif
diff --git a/src/include/moves.h b/src/include/moves.h
@@ -0,0 +1,54 @@
#ifndef MOVES_H
#define MOVES_H
#include <stdint.h>
#include "board.h"
typedef struct Move Move;
struct Move {
unsigned source : 6;
unsigned target : 6;
unsigned piece : 5;
unsigned piece_capture : 5;
unsigned piece_promote : 5;
unsigned dbl : 1;
unsigned enpassant : 1;
unsigned castle : 1;
unsigned capture : 1;
unsigned promote : 1;
};
typedef struct MoveList *MoveList;
struct MoveList {
Move moves[256];
int count;
};
int move_cmp(Move a, Move b);
Move move_encode(Square src, Square tgt, Piece piece, Piece capture,
Piece promote, int dbl, int enpassant, int castle);
void move_print(Move move);
MoveList move_list_new(void);
void move_list_free(MoveList *p);
Move move_list_move(MoveList self, int index);
int move_list_size(MoveList self);
void move_list_reset(MoveList self);
void move_list_add(MoveList self, Move move);
void move_list_print(MoveList self);
MoveList move_list_generate(MoveList moves, Board board);
int move_make(Move move, Board board, int flag);
#define move_source(move) (move.source)
#define move_target(move) (move.target)
#define move_double(move) (move.dbl)
#define move_enpassant(move) (move.enpassant)
#define move_castle(move) (move.castle)
#define move_capture(move) (move.capture)
#define move_promote(move) (move.promote)
#define move_piece(move) (Piece_fromIndex(move.piece))
#define move_piece_capture(move) (Piece_fromIndex(move.piece_capture))
#define move_piece_promote(move) (Piece_fromIndex(move.piece_promote))
#endif
diff --git a/src/include/perft.h b/src/include/perft.h
@@ -0,0 +1,9 @@
#ifndef PERFT_H
#define PERFT_H
#include "board.h"
void perft_test_threaded(Board board, int depth);
void perft_test(Board board, int depth);
#endif
diff --git a/src/include/score.h b/src/include/score.h
@@ -0,0 +1,10 @@
#ifndef SCORE_H
#define SCORE_H
#include "board.h"
int Score_capture(ePiece src, ePiece tgt);
int Score_position(ePiece piece, eColor color, Square square);
int Score_value(ePiece piece);
#endif
diff --git a/src/include/utils.h b/src/include/utils.h
@@ -0,0 +1,90 @@
#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 functions
#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)))
int bit_count(U64 bitboard);
int bit_lsb_index(U64 bitboard);
#define bitboard_for_each_bit(var, bb) \
for (var = bit_lsb_index(bb); bb; bit_pop(bb, var), var = bit_lsb_index(bb))
void bitboard_print(U64 bitboard);
// 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[];
Square coordinates_to_square(char *cord);
// 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;
int get_time_ms(void);
typedef U64 (*attack_f)(Square square, U64 occupancy);
#define empty_board "8/8/8/8/8/8/8/8 w - - "
#define start_position \
"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1 "
#endif
diff --git a/src/moves.c b/src/moves.c
@@ -1,273 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <cul/mem.h>
#include "moves.h"
int move_cmp(Move a, Move b) { return *(uint32_t *)&a == *(uint32_t *)&b; }
Move move_encode(Square src, Square tgt, Piece piece, Piece capture,
Piece promote, int dbl, int enpassant, int castle) {
return (Move){
.source = src,
.target = tgt,
.dbl = dbl,
.enpassant = enpassant,
.castle = castle,
.capture = capture != NULL,
.promote = promote != NULL,
.piece = Piece_index(piece),
.piece_capture = capture ? Piece_index(capture) : 0,
.piece_promote = promote ? Piece_index(promote) : 0,
};
}
void move_print(Move move) {
printf("%5s %5s %2s %2s %2s %4d %4d %4d %4d %4d\n",
square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)],
Piece_unicode(move_piece(move)),
move_capture(move) ? Piece_unicode(move_piece_capture(move)) : "X ",
move_promote(move) ? Piece_unicode(move_piece_promote(move)) : "X ",
move_double(move) ? 1 : 0, move_enpassant(move) ? 1 : 0,
move_castle(move) ? 1 : 0, move_capture(move) ? 1 : 0,
move_promote(move) ? 1 : 0);
}
MoveList move_list_new(void) {
MoveList p;
NEW0(p);
return p;
}
void move_list_free(MoveList *p) { FREE(*p); }
Move move_list_move(MoveList self, int index) { return self->moves[index]; }
int move_list_size(MoveList self) { return self->count; }
void move_list_reset(MoveList self) { self->count = 0; }
void move_list_add(MoveList self, Move move) {
self->moves[self->count++] = move;
}
void move_list_print(MoveList self) {
printf(" From To Pi Cap Prmt Dbl Enp Cst C P\n");
for (int i = 0; i < self->count; i++)
move_print(self->moves[i]);
printf("Total: %d\n", self->count);
}
#define pawn_canPromote(color, source) \
((color == WHITE && source >= a7 && source <= h7) || \
(color == BLACK && source >= a2 && source <= h2))
#define pawn_onStart(color, source) \
((color == BLACK && source >= a7 && source <= h7) || \
(color == WHITE && source >= a2 && source <= h2))
#define pawn_promote(source, target, Piece, Capture) \
for (int i = 1; i < 5; i++) { \
move = move_encode(source, target, Piece, Capture, \
Piece_get(i, color), 0, 0, 0); \
move_list_add(moves, move); \
}
MoveList move_list_generate(MoveList moves, Board board) {
Move move;
Square src, tgt;
eColor color = board_side(board);
if (!moves) moves = move_list_new();
{ // pawn moves
Piece Piece = Piece_get(PAWN, color);
U64 bitboard = board_pieceSet(board, Piece);
bitboard_for_each_bit(src, bitboard) {
{ // quiet
int add = (color == WHITE) ? +8 : -8;
tgt = src + add;
if (tgt > a1 && tgt < h8 &&
!board_square_isOccupied(board, tgt)) {
if (pawn_canPromote(color, src)) {
pawn_promote(src, tgt, Piece, 0);
} else {
move_list_add(
moves, move_encode(src, tgt, Piece, 0, 0, 0, 0, 0));
// two ahead
if (pawn_onStart(color, src) &&
!board_square_isOccupied(board, tgt += add))
move_list_add(moves, move_encode(src, tgt, Piece, 0,
0, 1, 0, 0));
}
}
}
{ // capture
U64 attack = board_piece_attacks(board, Piece, src) &
board_colorBB(board, !color);
bitboard_for_each_bit(tgt, attack) {
if (pawn_canPromote(color, src)) {
pawn_promote(src, tgt, Piece,
board_square_piece(board, tgt, !color));
} else {
move_list_add(
moves,
move_encode(src, tgt, Piece,
board_square_piece(board, tgt, !color),
0, 0, 0, 0));
}
}
}
{ // en passant
if (board_enpassant(board) != no_sq &&
board_piece_attacks(board, Piece, src) &
(C64(1) << board_enpassant(board)))
move_list_add(
moves,
move_encode(src, board_enpassant(board), Piece,
board_square_piece(board, tgt, !color), 0,
0, 1, 0));
}
}
}
// All piece move
for (int piece = 1; piece < 6; piece++) {
Piece Piece = Piece_get(piece, color);
U64 bitboard = board_pieceSet(board, Piece);
bitboard_for_each_bit(src, bitboard) {
U64 attack = board_piece_attacks(board, Piece, src) &
~board_colorBB(board, color);
bitboard_for_each_bit(tgt, attack) {
/* int take = bit_get(board_colorBB(board, !color), tgt); */
move_list_add(
moves, move_encode(src, tgt, Piece,
board_square_piece(board, tgt, !color),
0, 0, 0, 0));
}
}
}
// Castling
{
if (color == WHITE) {
Piece Piece = Piece_get(KING, WHITE);
if (board_castle(board) & WK) {
if (!board_square_isOccupied(board, f1) &&
!board_square_isOccupied(board, g1) &&
!board_square_isAttack(board, e1, BLACK) &&
!board_square_isAttack(board, f1, BLACK))
move_list_add(moves,
move_encode(e1, g1, Piece, 0, 0, 0, 0, 1));
}
if (board_castle(board) & WQ) {
if (!board_square_isOccupied(board, d1) &&
!board_square_isOccupied(board, c1) &&
!board_square_isOccupied(board, b1) &&
!board_square_isAttack(board, e1, BLACK) &&
!board_square_isAttack(board, d1, BLACK))
move_list_add(moves,
move_encode(e1, c1, Piece, 0, 0, 0, 0, 1));
}
} else {
Piece Piece = Piece_get(KING, BLACK);
if (board_castle(board) & BK) {
if (!board_square_isOccupied(board, f8) &&
!board_square_isOccupied(board, g8) &&
!board_square_isAttack(board, e8, WHITE) &&
!board_square_isAttack(board, f8, WHITE))
move_list_add(moves,
move_encode(e8, g8, Piece, 0, 0, 0, 0, 1));
}
if (board_castle(board) & BQ) {
if (!board_square_isOccupied(board, d8) &&
!board_square_isOccupied(board, c8) &&
!board_square_isOccupied(board, b8) &&
!board_square_isAttack(board, e8, WHITE) &&
!board_square_isAttack(board, d8, WHITE))
move_list_add(moves,
move_encode(e8, c8, Piece, 0, 0, 0, 0, 1));
}
}
}
return moves;
}
// clang-format off
const int castling_rights[64] = {
13, 15, 15, 15, 12, 15, 15, 14,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
7, 15, 15, 15, 3, 15, 15, 11,
};
// clang-format on
int move_make(Move move, Board board, int flag) {
if (flag == 0) {
Square source = move_source(move);
Square target = move_target(move);
Piece piece = move_piece(move);
eColor color = board_side(board);
if (!move_capture(move))
board_piece_move(board, piece, source, target);
else
board_piece_capture(board, piece, move_piece_capture(move), source,
target);
if (move_promote(move)) {
board_piece_pop(board, piece, target);
board_piece_set(board, move_piece_promote(move), target);
}
{
int ntarget = target + (color == WHITE ? -8 : +8);
if (move_enpassant(move))
board_piece_pop(board, Piece_get(PAWN, !color), ntarget);
board_enpassant_set(board, move_double(move) ? ntarget : no_sq);
}
if (move_castle(move)) {
Piece Rook = Piece_get(ROOK, board_side(board));
switch (target) {
case g1:
board_piece_move(board, Rook, h1, f1);
break;
case c1:
board_piece_move(board, Rook, a1, d1);
break;
case g8:
board_piece_move(board, Rook, h8, f8);
break;
case c8:
board_piece_move(board, Rook, a8, d8);
break;
default:
break;
}
}
board_castle_and(board, castling_rights[source]);
board_castle_and(board, castling_rights[target]);
if (!board_isCheck(board)) {
board_side_switch(board);
return 1;
} else
return 0;
} else {
if (move_capture(move))
return move_make(move, board, 0);
else
return 0;
}
}
diff --git a/src/moves/CMakeLists.txt b/src/moves/CMakeLists.txt
@@ -0,0 +1,7 @@
add_library(moves OBJECT
moves.c
)
target_include_directories(moves
PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
)
diff --git a/src/moves/moves.c b/src/moves/moves.c
@@ -0,0 +1,273 @@
#include <stdio.h>
#include <stdlib.h>
#include <cul/mem.h>
#include "moves.h"
int move_cmp(Move a, Move b) { return *(uint32_t *)&a == *(uint32_t *)&b; }
Move move_encode(Square src, Square tgt, Piece piece, Piece capture,
Piece promote, int dbl, int enpassant, int castle) {
return (Move){
.source = src,
.target = tgt,
.dbl = dbl,
.enpassant = enpassant,
.castle = castle,
.capture = capture != NULL,
.promote = promote != NULL,
.piece = Piece_index(piece),
.piece_capture = capture ? Piece_index(capture) : 0,
.piece_promote = promote ? Piece_index(promote) : 0,
};
}
void move_print(Move move) {
printf("%5s %5s %2s %2s %2s %4d %4d %4d %4d %4d\n",
square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)],
Piece_unicode(move_piece(move)),
move_capture(move) ? Piece_unicode(move_piece_capture(move)) : "X ",
move_promote(move) ? Piece_unicode(move_piece_promote(move)) : "X ",
move_double(move) ? 1 : 0, move_enpassant(move) ? 1 : 0,
move_castle(move) ? 1 : 0, move_capture(move) ? 1 : 0,
move_promote(move) ? 1 : 0);
}
MoveList move_list_new(void) {
MoveList p;
NEW0(p);
return p;
}
void move_list_free(MoveList *p) { FREE(*p); }
Move move_list_move(MoveList self, int index) { return self->moves[index]; }
int move_list_size(MoveList self) { return self->count; }
void move_list_reset(MoveList self) { self->count = 0; }
void move_list_add(MoveList self, Move move) {
self->moves[self->count++] = move;
}
void move_list_print(MoveList self) {
printf(" From To Pi Cap Prmt Dbl Enp Cst C P\n");
for (int i = 0; i < self->count; i++)
move_print(self->moves[i]);
printf("Total: %d\n", self->count);
}
#define pawn_canPromote(color, source) \
((color == WHITE && source >= a7 && source <= h7) || \
(color == BLACK && source >= a2 && source <= h2))
#define pawn_onStart(color, source) \
((color == BLACK && source >= a7 && source <= h7) || \
(color == WHITE && source >= a2 && source <= h2))
#define pawn_promote(source, target, Piece, Capture) \
for (int i = 1; i < 5; i++) { \
move = move_encode(source, target, Piece, Capture, \
Piece_get(i, color), 0, 0, 0); \
move_list_add(moves, move); \
}
MoveList move_list_generate(MoveList moves, Board board) {
Move move;
Square src, tgt;
eColor color = board_side(board);
if (!moves) moves = move_list_new();
{ // pawn moves
Piece Piece = Piece_get(PAWN, color);
U64 bitboard = board_pieceSet(board, Piece);
bitboard_for_each_bit(src, bitboard) {
{ // quiet
int add = (color == WHITE) ? +8 : -8;
tgt = src + add;
if (tgt > a1 && tgt < h8 &&
!board_square_isOccupied(board, tgt)) {
if (pawn_canPromote(color, src)) {
pawn_promote(src, tgt, Piece, 0);
} else {
move_list_add(
moves, move_encode(src, tgt, Piece, 0, 0, 0, 0, 0));
// two ahead
if (pawn_onStart(color, src) &&
!board_square_isOccupied(board, tgt += add))
move_list_add(moves, move_encode(src, tgt, Piece, 0,
0, 1, 0, 0));
}
}
}
{ // capture
U64 attack = board_piece_attacks(board, Piece, src) &
board_colorBB(board, !color);
bitboard_for_each_bit(tgt, attack) {
if (pawn_canPromote(color, src)) {
pawn_promote(src, tgt, Piece,
board_square_piece(board, tgt, !color));
} else {
move_list_add(
moves,
move_encode(src, tgt, Piece,
board_square_piece(board, tgt, !color),
0, 0, 0, 0));
}
}
}
{ // en passant
if (board_enpassant(board) != no_sq &&
board_piece_attacks(board, Piece, src) &
(C64(1) << board_enpassant(board)))
move_list_add(
moves,
move_encode(src, board_enpassant(board), Piece,
board_square_piece(board, tgt, !color), 0,
0, 1, 0));
}
}
}
// All piece move
for (int piece = 1; piece < 6; piece++) {
Piece Piece = Piece_get(piece, color);
U64 bitboard = board_pieceSet(board, Piece);
bitboard_for_each_bit(src, bitboard) {
U64 attack = board_piece_attacks(board, Piece, src) &
~board_colorBB(board, color);
bitboard_for_each_bit(tgt, attack) {
/* int take = bit_get(board_colorBB(board, !color), tgt); */
move_list_add(
moves, move_encode(src, tgt, Piece,
board_square_piece(board, tgt, !color),
0, 0, 0, 0));
}
}
}
// Castling
{
if (color == WHITE) {
Piece Piece = Piece_get(KING, WHITE);
if (board_castle(board) & WK) {
if (!board_square_isOccupied(board, f1) &&
!board_square_isOccupied(board, g1) &&
!board_square_isAttack(board, e1, BLACK) &&
!board_square_isAttack(board, f1, BLACK))
move_list_add(moves,
move_encode(e1, g1, Piece, 0, 0, 0, 0, 1));
}
if (board_castle(board) & WQ) {
if (!board_square_isOccupied(board, d1) &&
!board_square_isOccupied(board, c1) &&
!board_square_isOccupied(board, b1) &&
!board_square_isAttack(board, e1, BLACK) &&
!board_square_isAttack(board, d1, BLACK))
move_list_add(moves,
move_encode(e1, c1, Piece, 0, 0, 0, 0, 1));
}
} else {
Piece Piece = Piece_get(KING, BLACK);
if (board_castle(board) & BK) {
if (!board_square_isOccupied(board, f8) &&
!board_square_isOccupied(board, g8) &&
!board_square_isAttack(board, e8, WHITE) &&
!board_square_isAttack(board, f8, WHITE))
move_list_add(moves,
move_encode(e8, g8, Piece, 0, 0, 0, 0, 1));
}
if (board_castle(board) & BQ) {
if (!board_square_isOccupied(board, d8) &&
!board_square_isOccupied(board, c8) &&
!board_square_isOccupied(board, b8) &&
!board_square_isAttack(board, e8, WHITE) &&
!board_square_isAttack(board, d8, WHITE))
move_list_add(moves,
move_encode(e8, c8, Piece, 0, 0, 0, 0, 1));
}
}
}
return moves;
}
// clang-format off
const int castling_rights[64] = {
13, 15, 15, 15, 12, 15, 15, 14,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15,
7, 15, 15, 15, 3, 15, 15, 11,
};
// clang-format on
int move_make(Move move, Board board, int flag) {
if (flag == 0) {
Square source = move_source(move);
Square target = move_target(move);
Piece piece = move_piece(move);
eColor color = board_side(board);
if (!move_capture(move))
board_piece_move(board, piece, source, target);
else
board_piece_capture(board, piece, move_piece_capture(move), source,
target);
if (move_promote(move)) {
board_piece_pop(board, piece, target);
board_piece_set(board, move_piece_promote(move), target);
}
{
int ntarget = target + (color == WHITE ? -8 : +8);
if (move_enpassant(move))
board_piece_pop(board, Piece_get(PAWN, !color), ntarget);
board_enpassant_set(board, move_double(move) ? ntarget : no_sq);
}
if (move_castle(move)) {
Piece Rook = Piece_get(ROOK, board_side(board));
switch (target) {
case g1:
board_piece_move(board, Rook, h1, f1);
break;
case c1:
board_piece_move(board, Rook, a1, d1);
break;
case g8:
board_piece_move(board, Rook, h8, f8);
break;
case c8:
board_piece_move(board, Rook, a8, d8);
break;
default:
break;
}
}
board_castle_and(board, castling_rights[source]);
board_castle_and(board, castling_rights[target]);
if (!board_isCheck(board)) {
board_side_switch(board);
return 1;
} else
return 0;
} else {
if (move_capture(move))
return move_make(move, board, 0);
else
return 0;
}
}
diff --git a/src/perft.c b/src/perft.c
@@ -1,127 +0,0 @@
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include "moves.h"
#include "perft.h"
struct MoveList moveList[10];
long nodes;
void perft_driver(Board board, struct MoveList *moveList, int depth,
unsigned long long *nodes) {
if (depth == 0) {
(*nodes)++;
return;
}
MoveList list = move_list_generate(&moveList[depth], board);
Board copy = board_new();
for (int i = 0; i < move_list_size(list); i++) {
board_copy(board, copy);
if (!move_make(move_list_move(list, i), copy, 0)) continue;
perft_driver(copy, moveList, depth - 1, nodes);
}
move_list_reset(list);
board_free(©);
}
void perft_test(Board board, int depth) {
MoveList list = move_list_generate(&moveList[depth], board);
Board copy = board_new();
long start = get_time_ms();
printf("\n Performance test\n\n");
nodes = 0;
for (int i = 0; i < move_list_size(list); i++) {
board_copy(board, copy);
Move move = move_list_move(list, i);
if (!move_make(move_list_move(list, i), copy, 0)) continue;
unsigned long long node = 0;
perft_driver(copy, moveList, depth - 1, &node);
printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)], node);
nodes += node;
}
move_list_reset(list);
board_free(©);
printf("\nNodes searched: %ld\n\n", nodes);
printf("\n Depth: %d\n", depth);
printf(" Nodes: %ld\n", nodes);
printf(" Time: %ld\n\n", get_time_ms() - start);
}
typedef struct perf_shared perf_shared;
struct perf_shared {
Board board;
MoveList list;
int depth;
sem_t *mutex;
int *index;
sem_t *finish;
unsigned long long *total;
};
void *perft_thread(void *arg) {
perf_shared *shared = (perf_shared *)arg;
Board board = board_new();
unsigned long long node_count = 0;
struct MoveList moveList[10];
while (1) {
sem_wait(shared->mutex);
*shared->total += node_count;
if (*shared->index >= move_list_size(shared->list)) {
sem_post(shared->mutex);
break;
}
Move move = move_list_move(shared->list, (*shared->index)++);
sem_post(shared->mutex);
board_copy(shared->board, board);
if (!move_make(move, board, 0)) continue;
node_count = 0;
perft_driver(board, moveList, shared->depth, &node_count);
printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)], node_count);
}
board_free(&board);
sem_post(shared->finish);
return NULL;
}
void perft_test_threaded(Board board, int depth) {
MoveList list = move_list_generate(NULL, board);
int size = 8;
unsigned long long total = 0;
perf_shared shared[size];
pthread_t threads[size];
sem_t mutex, finish;
int index = 0;
sem_init(&mutex, 0, 1);
sem_init(&finish, 0, 0);
for (int i = 0; i < size; i++) {
shared[i].board = board;
shared[i].list = list;
shared[i].depth = depth - 1;
shared[i].mutex = &mutex;
shared[i].index = &index;
shared[i].finish = &finish;
shared[i].total = &total;
pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i));
}
for (int i = 0; i < size; i++)
sem_wait(&finish);
move_list_free(&list);
printf("Nodes processed: %llu\n", total);
}
diff --git a/src/perft/CMakeLists.txt b/src/perft/CMakeLists.txt
@@ -0,0 +1,17 @@
add_executable(perft perft.c)
target_link_libraries(perft
PRIVATE attack
PRIVATE board
PRIVATE moves
PRIVATE score
PRIVATE utils
)
target_link_libraries(perft PRIVATE "cul")
set_target_properties(perft PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin"
)
diff --git a/src/perft/perft.c b/src/perft/perft.c
@@ -0,0 +1,145 @@
#include <pthread.h>
#include <semaphore.h>
#include <stdio.h>
#include "attack.h"
#include "board.h"
#include "moves.h"
#include "perft.h"
#include "utils.h"
struct MoveList moveList[10];
long nodes;
void perft_driver(Board board, struct MoveList *moveList, int depth,
unsigned long long *nodes) {
if (depth == 0) {
(*nodes)++;
return;
}
MoveList list = move_list_generate(&moveList[depth], board);
Board copy = board_new();
for (int i = 0; i < move_list_size(list); i++) {
board_copy(board, copy);
if (!move_make(move_list_move(list, i), copy, 0)) continue;
perft_driver(copy, moveList, depth - 1, nodes);
}
move_list_reset(list);
board_free(©);
}
void perft_test(Board board, int depth) {
MoveList list = move_list_generate(&moveList[depth], board);
Board copy = board_new();
long start = get_time_ms();
printf("\n Performance test\n\n");
nodes = 0;
for (int i = 0; i < move_list_size(list); i++) {
board_copy(board, copy);
Move move = move_list_move(list, i);
if (!move_make(move_list_move(list, i), copy, 0)) continue;
unsigned long long node = 0;
perft_driver(copy, moveList, depth - 1, &node);
printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)], node);
nodes += node;
}
move_list_reset(list);
board_free(©);
printf("\nNodes searched: %ld\n\n", nodes);
printf("\n Depth: %d\n", depth);
printf(" Nodes: %ld\n", nodes);
printf(" Time: %ld\n\n", get_time_ms() - start);
}
typedef struct perf_shared perf_shared;
struct perf_shared {
Board board;
MoveList list;
int depth;
sem_t *mutex;
int *index;
sem_t *finish;
unsigned long long *total;
};
void *perft_thread(void *arg) {
perf_shared *shared = (perf_shared *)arg;
Board board = board_new();
unsigned long long node_count = 0;
struct MoveList moveList[10];
while (1) {
sem_wait(shared->mutex);
*shared->total += node_count;
if (*shared->index >= move_list_size(shared->list)) {
sem_post(shared->mutex);
break;
}
Move move = move_list_move(shared->list, (*shared->index)++);
sem_post(shared->mutex);
board_copy(shared->board, board);
if (!move_make(move, board, 0)) continue;
node_count = 0;
perft_driver(board, moveList, shared->depth, &node_count);
printf("%s%s: %llu\n", square_to_coordinates[move_source(move)],
square_to_coordinates[move_target(move)], node_count);
}
board_free(&board);
sem_post(shared->finish);
return NULL;
}
void perft_test_threaded(Board board, int depth) {
MoveList list = move_list_generate(NULL, board);
int size = 8;
unsigned long long total = 0;
perf_shared shared[size];
pthread_t threads[size];
sem_t mutex, finish;
int index = 0;
sem_init(&mutex, 0, 1);
sem_init(&finish, 0, 0);
for (int i = 0; i < size; i++) {
shared[i].board = board;
shared[i].list = list;
shared[i].depth = depth - 1;
shared[i].mutex = &mutex;
shared[i].index = &index;
shared[i].finish = &finish;
shared[i].total = &total;
pthread_create(threads + i, NULL, perft_thread, (void *)(shared + i));
}
for (int i = 0; i < size; i++)
sem_wait(&finish);
move_list_free(&list);
printf("Nodes processed: %llu\n", total);
}
// FEN debug positions
#define tricky_position \
"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq - 0 1 "
#define killer_position \
"rnbqkb1r/pp1p1pPp/8/2p1pP2/1P1P4/3P3P/P1P1P3/RNBQKBNR w KQkq e6 0 1"
#define cmk_position \
"r2q1rk1/ppp2ppp/2n1bn2/2b1p3/3pP3/3P1NPP/PPP1NPB1/R1BQ1RK1 b - - 0 9 "
int main(void) {
init_attacks();
Board board = board_new();
board_fromFEN(board, tricky_position);
perft_test_threaded(board, 5);
}
diff --git a/src/score.c b/src/score.c
@@ -1,117 +0,0 @@
#include "score.h"
// clang-format off
struct Score_T {
int value;
int position[64];
int capture[6];
};
struct Score_T Scores[] = {
[PAWN] = {
.value = 100,
.position = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, -10, -10, 0, 0, 0,
0, 0, 0, 5, 5, 0, 0, 0,
5, 5, 10, 20, 20, 5, 5, 5,
10, 10, 10, 20, 20, 10, 10, 10,
20, 20, 20, 30, 30, 30, 20, 20,
30, 30, 30, 40, 40, 30, 30, 30,
90, 90, 90, 90, 90, 90, 90, 90, },
.capture = { [PAWN] = 105, [KNIGHT] = 205,
[BISHOP] = 305, [ROOK] = 405,
[QUEEN] = 505, [KING] = 605} },
[KNIGHT] = {
.value = 300,
.position = {
-5, -10 , 0, 0, 0, 0, -10, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 5, 20, 10, 10, 20, 5, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 5, 20, 20, 20, 20, 5, -5,
-5, 0, 0, 10, 10, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5, },
.capture = { [PAWN] = 104, [KNIGHT] = 204,
[BISHOP] = 304, [ROOK] = 404,
[QUEEN] = 504, [KING] = 604} },
[BISHOP] = {
.value = 350,
.position = {
0, 0, -10, 0, 0, -10, 0, 0,
0, 30, 0, 0, 0, 0, 30, 0,
0, 10, 0, 0, 0, 0, 10, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 0, 10, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 103, [KNIGHT] = 203,
[BISHOP] = 303, [ROOK] = 403,
[QUEEN] = 503, [KING] = 603} },
[ROOK] = {
.value = 500,
.position = {
0, 0, 0, 20, 20, 0, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, },
.capture = { [PAWN] = 102, [KNIGHT] = 202,
[BISHOP] = 302, [ROOK] = 402,
[QUEEN] = 502, [KING] = 602} },
[QUEEN] = {
.value = 1000,
.position = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 101, [KNIGHT] = 201,
[BISHOP] = 301, [ROOK] = 401,
[QUEEN] = 501, [KING] = 601} },
[KING] = {
.value = 10000,
.position = {
0, 0, 5, 0, -15, 0, 10, 0,
0, 5, 5, -5, -5, 0, 5, 0,
0, 0, 5, 10, 10, 5, 0, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 5, 10, 10, 5, 5, 0,
0, 0, 5, 5, 5, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 100, [KNIGHT] = 200,
[BISHOP] = 300, [ROOK] = 400,
[QUEEN] = 500, [KING] = 600} },
};
const int mirror_score[128] =
{
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1, no_sq,
};
// clang-format on
int Score_value(ePiece piece) { return Scores[piece].value; }
int Score_position(ePiece piece, eColor color, Square square) {
if (color == BLACK) square = mirror_score[square];
return Scores[piece].position[square];
}
int Score_capture(ePiece src, ePiece tgt) { return Scores[src].capture[tgt]; }
diff --git a/src/score/CMakeLists.txt b/src/score/CMakeLists.txt
@@ -0,0 +1,7 @@
add_library(score OBJECT
score.c
)
target_include_directories(score
PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
)
diff --git a/src/score/score.c b/src/score/score.c
@@ -0,0 +1,117 @@
#include "score.h"
// clang-format off
struct Score_T {
int value;
int position[64];
int capture[6];
};
struct Score_T Scores[] = {
[PAWN] = {
.value = 100,
.position = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, -10, -10, 0, 0, 0,
0, 0, 0, 5, 5, 0, 0, 0,
5, 5, 10, 20, 20, 5, 5, 5,
10, 10, 10, 20, 20, 10, 10, 10,
20, 20, 20, 30, 30, 30, 20, 20,
30, 30, 30, 40, 40, 30, 30, 30,
90, 90, 90, 90, 90, 90, 90, 90, },
.capture = { [PAWN] = 105, [KNIGHT] = 205,
[BISHOP] = 305, [ROOK] = 405,
[QUEEN] = 505, [KING] = 605} },
[KNIGHT] = {
.value = 300,
.position = {
-5, -10 , 0, 0, 0, 0, -10, -5,
-5, 0, 0, 0, 0, 0, 0, -5,
-5, 5, 20, 10, 10, 20, 5, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 10, 20, 30, 30, 20, 10, -5,
-5, 5, 20, 20, 20, 20, 5, -5,
-5, 0, 0, 10, 10, 0, 0, -5,
-5, 0, 0, 0, 0, 0, 0, -5, },
.capture = { [PAWN] = 104, [KNIGHT] = 204,
[BISHOP] = 304, [ROOK] = 404,
[QUEEN] = 504, [KING] = 604} },
[BISHOP] = {
.value = 350,
.position = {
0, 0, -10, 0, 0, -10, 0, 0,
0, 30, 0, 0, 0, 0, 30, 0,
0, 10, 0, 0, 0, 0, 10, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 0, 10, 10, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 103, [KNIGHT] = 203,
[BISHOP] = 303, [ROOK] = 403,
[QUEEN] = 503, [KING] = 603} },
[ROOK] = {
.value = 500,
.position = {
0, 0, 0, 20, 20, 0, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
0, 0, 10, 20, 20, 10, 0, 0,
50, 50, 50, 50, 50, 50, 50, 50,
50, 50, 50, 50, 50, 50, 50, 50, },
.capture = { [PAWN] = 102, [KNIGHT] = 202,
[BISHOP] = 302, [ROOK] = 402,
[QUEEN] = 502, [KING] = 602} },
[QUEEN] = {
.value = 1000,
.position = {
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 101, [KNIGHT] = 201,
[BISHOP] = 301, [ROOK] = 401,
[QUEEN] = 501, [KING] = 601} },
[KING] = {
.value = 10000,
.position = {
0, 0, 5, 0, -15, 0, 10, 0,
0, 5, 5, -5, -5, 0, 5, 0,
0, 0, 5, 10, 10, 5, 0, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 10, 20, 20, 10, 5, 0,
0, 5, 5, 10, 10, 5, 5, 0,
0, 0, 5, 5, 5, 5, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, },
.capture = { [PAWN] = 100, [KNIGHT] = 200,
[BISHOP] = 300, [ROOK] = 400,
[QUEEN] = 500, [KING] = 600} },
};
const int mirror_score[128] =
{
a8, b8, c8, d8, e8, f8, g8, h8,
a7, b7, c7, d7, e7, f7, g7, h7,
a6, b6, c6, d6, e6, f6, g6, h6,
a5, b5, c5, d5, e5, f5, g5, h5,
a4, b4, c4, d4, e4, f4, g4, h4,
a3, b3, c3, d3, e3, f3, g3, h3,
a2, b2, c2, d2, e2, f2, g2, h2,
a1, b1, c1, d1, e1, f1, g1, h1, no_sq,
};
// clang-format on
int Score_value(ePiece piece) { return Scores[piece].value; }
int Score_position(ePiece piece, eColor color, Square square) {
if (color == BLACK) square = mirror_score[square];
return Scores[piece].position[square];
}
int Score_capture(ePiece src, ePiece tgt) { return Scores[src].capture[tgt]; }
diff --git a/src/utils.c b/src/utils.c
@@ -1,85 +0,0 @@
#include <stdio.h>
#ifdef WIN64
#include <widnows.h>
#else
#include <sys/time.h>
#endif
#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)); }
// clang-format off
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
//
Square coordinates_to_square(char *cord) {
return (cord[1] - '1') * 8 + (cord[0] - 'a');
}
int bit_count(U64 bitboard) {
int count = 0;
while (bitboard > 0) {
count++;
bitboard &= bitboard - 1;
}
return count;
}
int bit_lsb_index(U64 bitboard) {
if (!bitboard) return -1;
return bit_count((bitboard & -bitboard) - 1);
}
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 (!file) printf(" %d ", 8 - rank);
printf("%d ", bit_get(bitboard, square) ? 1 : 0);
}
printf("\n");
}
printf("\n A B C D E F G H\n\n");
printf(" Bitboard: %llud\n\n", bitboard);
}
int get_time_ms(void) {
#ifdef WIN64
return GetTickCount();
#else
struct timeval time;
gettimeofday(&time, NULL);
return time.tv_sec * 1000 + time.tv_usec / 1000;
#endif
}
diff --git a/src/utils/CMakeLists.txt b/src/utils/CMakeLists.txt
@@ -0,0 +1,7 @@
add_library(utils OBJECT
utils.c
)
target_include_directories(utils
PUBLIC "${PROJECT_SOURCE_DIR}/src/include"
)
diff --git a/src/utils/utils.c b/src/utils/utils.c
@@ -0,0 +1,85 @@
#include <stdio.h>
#ifdef WIN64
#include <widnows.h>
#else
#include <sys/time.h>
#endif
#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)); }
// clang-format off
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
//
Square coordinates_to_square(char *cord) {
return (cord[1] - '1') * 8 + (cord[0] - 'a');
}
int bit_count(U64 bitboard) {
int count = 0;
while (bitboard > 0) {
count++;
bitboard &= bitboard - 1;
}
return count;
}
int bit_lsb_index(U64 bitboard) {
if (!bitboard) return -1;
return bit_count((bitboard & -bitboard) - 1);
}
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 (!file) printf(" %d ", 8 - rank);
printf("%d ", bit_get(bitboard, square) ? 1 : 0);
}
printf("\n");
}
printf("\n A B C D E F G H\n\n");
printf(" Bitboard: %llud\n\n", bitboard);
}
int get_time_ms(void) {
#ifdef WIN64
return GetTickCount();
#else
struct timeval time;
gettimeofday(&time, NULL);
return time.tv_sec * 1000 + time.tv_usec / 1000;
#endif
}