Compare commits

..

15 Commits

Author SHA1 Message Date
4458fb6491 parseFen 2024-09-25 22:10:07 +02:00
924e7d5eed updated README 2024-09-25 21:34:38 +02:00
6ccd2a4efe partly implemented uci 2024-09-25 21:21:17 +02:00
6e61a14cdb change repetitionTable memory allocation place 2024-09-24 22:08:21 +02:00
2c3d1bc021 repetition table 2024-09-23 21:51:22 +02:00
6c5ef0586f zobristTableGet 2024-09-22 14:53:14 +02:00
db958dcac1 zobrist hashing 2024-09-22 11:45:30 +02:00
4d2a86c7b7 generate zobrist masks 2024-09-18 22:39:16 +02:00
5209dfec92 fixed promotion menu for black 2024-09-15 19:07:08 +02:00
a649db77ef fixed move generattion: perft now passing 2024-09-05 15:32:36 +02:00
1ab928cc6f fixed promotion menu
computer not working!
2024-09-05 14:47:47 +02:00
6178e7c963 fixed promotion for the white user 2024-09-04 08:24:54 +02:00
2d49f86517 README: changed sytling for targets 2024-03-26 00:04:42 +01:00
7900546964 README finished 2024-03-26 00:00:39 +01:00
f0f12be88d README: target information 2024-03-25 22:41:17 +01:00
23 changed files with 605 additions and 211 deletions

3
.gitignore vendored
View File

@ -1,6 +1,6 @@
/* /*
!/src !/src
/include/chess/generated/* /src/generated
!/CMakeLists.txt !/CMakeLists.txt
!/README.md !/README.md
!/LICENSE !/LICENSE
@ -8,5 +8,6 @@
!/lib !/lib
!.gitkeep !.gitkeep
!/include !/include
/include/chess/generated/*
!/.gitignore !/.gitignore
!/test !/test

View File

@ -1,29 +1,36 @@
cmake_minimum_required(VERSION 3.18) cmake_minimum_required(VERSION 3.18)
project(chess C) project(chess C)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_C_STANDARD 99)
include_directories(include) include_directories(include)
file(GLOB SOURCES "src/chess/*.c") file(GLOB SOURCES "src/chess/*.c")
file(GLOB LIB_SOURCES "src/common/*.c") file(GLOB CODE_GEN_LIB_SOURCES "src/common/preCodeGen/*.c")
file(GLOB LIB_SOURCES "src/common/*/*.c" "src/generated/*.c")
add_executable(genMoveConsts src/generateCode/moveConsts.c src/common/bitboard.c add_executable(genMoveConsts src/generateCode/moveConsts.c ${CODE_GEN_LIB_SOURCES})
src/common/print.c)
add_custom_command( add_custom_command(
OUTPUT moveConsts.h # Output file from code generation OUTPUT moveConsts.h # Output file from code generation
COMMAND genMoveConsts > include/chess/generated/moveConsts.h COMMAND genMoveConsts > include/chess/generated/moveConsts.h
DEPENDS genMoveConsts # Depends on the code generation executable DEPENDS genMoveConsts # Depends on the code generation executable
) )
add_custom_target(generateCode DEPENDS moveConsts.h)
add_executable(genZobrist src/generateCode/zobrist.c ${CODE_GEN_LIB_SOURCES})
add_custom_command(
OUTPUT zobristConsts.c # Output file from code generation
COMMAND genZobrist > src/generated/zobristConsts.c
DEPENDS genZobrist # Depends on the code generation executable
)
add_custom_target(generateCode DEPENDS moveConsts.h zobristConsts.c)
find_package(PkgConfig REQUIRED) find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK4 REQUIRED IMPORTED_TARGET gtk4) pkg_check_modules(GTK4 REQUIRED IMPORTED_TARGET gtk4)
pkg_check_modules(LIBRSVG REQUIRED IMPORTED_TARGET librsvg-2.0) pkg_check_modules(LIBRSVG REQUIRED IMPORTED_TARGET librsvg-2.0)
add_executable(findMagicNumber src/findMagicNumber.c ${LIB_SOURCES}) add_executable(findMagicNumber src/findMagicNumber.c ${LIB_SOURCES})
set_property(TARGET findMagicNumber PROPERTY C_STANDARD 99)
add_dependencies(findMagicNumber generateCode) add_dependencies(findMagicNumber generateCode)
add_executable(${PROJECT_NAME} ${SOURCES} ${LIB_SOURCES}) add_executable(${PROJECT_NAME} ${SOURCES} ${LIB_SOURCES})
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG) target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG)
add_dependencies(${PROJECT_NAME} generateCode) add_dependencies(${PROJECT_NAME} generateCode)
@ -39,11 +46,9 @@ if(CMAKE_BUILD_TYPE MATCHES "Release")
endif() endif()
add_executable(chessNoComputer ${SOURCES} ${LIB_SOURCES}) add_executable(chessNoComputer ${SOURCES} ${LIB_SOURCES})
set_property(TARGET chessNoComputer PROPERTY C_STANDARD 99)
target_link_libraries(chessNoComputer PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG) target_link_libraries(chessNoComputer PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG)
add_dependencies(chessNoComputer generateCode) add_dependencies(chessNoComputer generateCode)
target_compile_definitions(chessNoComputer PUBLIC NO_COMPUTER) target_compile_definitions(chessNoComputer PUBLIC NO_COMPUTER)
add_executable(moveGenTest test/moveGen.c src/chess/move.c ${LIB_SOURCES}) add_executable(moveGenTest test/moveGen.c src/chess/move.c ${LIB_SOURCES})
set_property(TARGET moveGenTest PROPERTY C_STANDARD 99)
add_test(NAME moveGen COMMAND moveGenTest) add_test(NAME moveGen COMMAND moveGenTest)

View File

@ -19,13 +19,62 @@ make
If you do not use make, replace it with ninja for example. The build script contains multiple If you do not use make, replace it with ninja for example. The build script contains multiple
targets: targets:
- chess: This is the main target. It has a GUI and the user plays as white. The engine responds with - `chess`: This is the main target. It has a GUI and the user plays as white. The engine responds with
a move for black. a move for black.
- chessNoComputer: The user can play both sides in a GUI. Mainly added for testing the move - `chessNoComputer`: The user can play both sides in a GUI. Mainly added for testing the move
generator. generator.
- findMagicNumber: finds Magic Numbers for the magic bitboard. - `findMagicNumber`: finds Magic Numbers for the magic bitboard.
- `moveGenTest`: tests the move generator,
- `genMoveConsts`: produces constants to generate moves faster.
# sources ## Testing
https://www.chessprogramming.org/Magic_Bitboards The target moveGenTest with its source in the test directory is a [perft](https://www.chessprogramming.org/Perft).
https://www.chessprogramming.org/Looking_for_Magics It works by counting the number of leaf nodes of the move generation with a
certain depth. The results where verified with [stockfish](https://stockfishchess.org/).
## Implementation
The engine is implemented using [bitboards](https://www.chessprogramming.org/Bitboards)
to store the position. A bitboard is an array of 64-Bit unsigned integers. The bitboard contains one
element per piece and square. Each Bit of the integer corresponds to one field
on the board, whether the piece stands there (1) or not (0). This allows use to
use bitwise operations to calculate information we need, really fast.
The move generation of pseudo-legal moves (moves without considering whether the
king is in check) is mostly straightforward. We pre-generate some data in
`src/generateCode/moveConsts.c`, so that we can calculate the moves faster.
For the king and the knight the moves are pre-generated because they can only
possibly move to fixed target squares for one given source square. For the other
pieces we pre-generated the distance, the piece can move in one given direction.
For testing, if the king is in check, so we get legal moves, [magic
Bitboards](https://www.chessprogramming.org/Magic_Bitboards) are used. This
allows us to calculate really fast whether the king is in check compared to
looping over all moves to see, if a piece can capture the king. Essentially we
get the bitboard of all pieces and null all bits but the file and rank, the rook
we want to the moves for, is on (or the relevant diagonals for the bishop). We
can look up the moves by using this mask as a key of a hashmap. A hashing
algorithm that multiplies with a so-called magic number and right shift, is used.
The hashing is fast, and we can find magic numbers, so there are no hash
collisions. To simplify the task of finding magic numbers, we use a different
magic number for each square ([fancy magic
bitboard](https://www.chessprogramming.org/Magic_Bitboards#Fancy)). The magic
numbers for this engine are simply found by trying randomly generated one. The
Implementation in `src/findMagicNumber.c` is based on ideas
[there](https://www.chessprogramming.org/Looking_for_Magics#Feeding_in_Randoms).
For seraching the moves, we use a variant of [Alpha-Beta](https://www.chessprogramming.org/Alpha-Beta).
Alpha-Beta is based on [minimax](https://en.wikipedia.org/wiki/Minimax) with an
added upper and lower bound for the position score with makes it much faster
than minimax.
The engine has a partly implemented UCI interface with its code in `src/uci.c`.
To detect repetitions we need a way to easily lookup, if the position already accrued. This done by
calculating a [Zobrist Hash](https://www.chessprogramming.org/Zobrist_Hashing) for the board
position. Zobrist Hashing is really nice because it allows incremental updating. With this hash the
engine has implemented its own hash tables, which allow for detecting repetitions. Later on this can
also be used for a [transposition table](https://www.chessprogramming.org/Transposition_Table).
The evaluation is currently simple. Here is currently the most room for optimizations.

View File

@ -2,9 +2,9 @@
#include <stdint.h> #include <stdint.h>
#include <chess/types.h> #include <chess/types.h>
bool bitsetGet(uint_least64_t bitset, uint_least8_t i); #define REPETETION_TABLE_LENGTH 1024
uint_least64_t bitsetClear(uint_least64_t bitset, uint_least8_t i); #define START_FEN "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
uint_least64_t bitsetSet(uint_least64_t bitset, uint_least8_t i);
bool bitboardGet(const uint_least64_t *board, struct piece_t piece, uint_least8_t i); bool bitboardGet(const uint_least64_t *board, struct piece_t piece, uint_least8_t i);
uint_least64_t bitboardGetMask(const uint_least64_t *board, struct piece_t piece); uint_least64_t bitboardGetMask(const uint_least64_t *board, struct piece_t piece);
void bitboardSetMask(uint_least64_t *board, struct piece_t piece, uint_least64_t value); void bitboardSetMask(uint_least64_t *board, struct piece_t piece, uint_least64_t value);
@ -13,4 +13,6 @@ uint_least64_t bitboardMaskAllPieces(const uint_least64_t *board);
void bitboardClear(uint_least64_t *board, struct piece_t piece, uint_least8_t i); void bitboardClear(uint_least64_t *board, struct piece_t piece, uint_least8_t i);
void bitboardSet(uint_least64_t *board, struct piece_t piece, uint_least8_t i); void bitboardSet(uint_least64_t *board, struct piece_t piece, uint_least8_t i);
struct piece_t pieceAtField(const uint_least64_t *board, uint_least8_t i); struct piece_t pieceAtField(const uint_least64_t *board, uint_least8_t i);
struct gameState_t newGameState(uint_least64_t *board, char *FEN); struct gameState_t newGameState(uint_least64_t *board,
struct zobristTableElement *repetitionTableStore, const char *FEN);
struct gameState_t parseFen(uint_least64_t *board, const char *FEN);

6
include/chess/bitset.h Normal file
View File

@ -0,0 +1,6 @@
#include <stdint.h>
#include <stdbool.h>
bool bitsetGet(uint_least64_t bitset, uint_least8_t i);
uint_least64_t bitsetClear(uint_least64_t bitset, uint_least8_t i);
uint_least64_t bitsetSet(uint_least64_t bitset, uint_least8_t i);

View File

@ -17,6 +17,7 @@ enum spezialMoves {
struct move_t { struct move_t {
uint_least8_t src, dst, spezialMove; uint_least8_t src, dst, spezialMove;
uint_least8_t srcPiece, dstPiece, capturedPiece; uint_least8_t srcPiece, dstPiece, capturedPiece;
uint_least64_t hash;
}; };
void genDirectionConsts(); void genDirectionConsts();
@ -24,9 +25,11 @@ uint_least8_t validMoves(struct gameState_t gameState, struct move_t *moves);
uint_least8_t pieceValidMoves(struct gameState_t gameState, struct piece_t piece, uint_least8_t src, uint_least8_t pieceValidMoves(struct gameState_t gameState, struct piece_t piece, uint_least8_t src,
struct move_t *moves, bool promotion); struct move_t *moves, bool promotion);
void undoMove(uint_least64_t *board, struct move_t move, bool color); void undoMove(struct gameState_t gameState, struct move_t move, bool color);
struct gameState_t makeMove(struct gameState_t gameState, struct move_t move); struct gameState_t makeMove(struct gameState_t gameState, const struct move_t move);
void initMagicTable(); void initMagicTable();
bool kingInCheck(const uint_least64_t *board, const bool color); bool kingInCheck(const uint_least64_t *board, const bool color);
uint_least8_t getBaseRankI(bool color);
uint_least8_t getBaseRank(bool color);
#endif #endif

View File

@ -2,26 +2,29 @@
#include <stdint.h> #include <stdint.h>
#include <chess/move.h> #include <chess/move.h>
#define fprintArray(file, printer, arr) \ #define fprintArrayCustomLength(file, printer, arr, length) \
do { \ do { \
fprintf(file, "{"); \ fprintf(file, "{"); \
for(size_t i = 0; i < LENGTH(arr); ++i) { \ for(size_t i = 0; i < length; ++i) { \
printer(file, arr[i]); \ printer(file, arr[i]); \
if (i < LENGTH(arr) - 1) { \ if (i < length - 1) { \
fprintf(file, ", "); \ fprintf(file, ", "); \
} \ } \
} \ } \
fprintf(file, " }"); \ fprintf(file, " }"); \
} while(0) } while(0)
#define fprintArray(file, printer, arr) fprintArrayCustomLength(file, printer, arr, LENGTH(arr))
#define fdefineArray(file, declaration, printer, arr) \ #define fdefineArrayCustomLength(file, declaration, printer, arr, length) \
do { \ do { \
fprintf(file, "%s[%zu] = ", declaration, LENGTH(arr)); \ fprintf(file, "%s[%zu] = ", declaration, length); \
fprintArray(file, printer, arr); \ fprintArrayCustomLength(file, printer, arr, length); \
fprintf(file, ";\n"); \ fprintf(file, ";\n"); \
} while(0) } while(0)
#define fdefineArray(file, declaration, printer, arr) fdefineArrayCustomLength(file, declaration, printer, arr, LENGTH(arr))
#define defineArray(declaration, printer, arr) fdefineArray(stdout, declaration, printer, arr) #define defineArrayCustomLength(declaration, printer, arr, length) fdefineArrayCustomLength(stdout, declaration, printer, arr, length)
#define defineArray(declaration, printer, arr) defineArrayCustomLength(declaration, printer, arr, LENGTH(arr))
void printerll(FILE *file, long long num); void printerll(FILE *file, long long num);
void printerull(FILE *file, unsigned long long num); void printerull(FILE *file, unsigned long long num);

View File

@ -3,6 +3,7 @@
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <stddef.h>
#define BOARD_SIZE 8 #define BOARD_SIZE 8
#define TOTAL_BOARD_SIZE (BOARD_SIZE * BOARD_SIZE) #define TOTAL_BOARD_SIZE (BOARD_SIZE * BOARD_SIZE)
@ -35,6 +36,16 @@ enum directions {
NORTH, NORTHWEST, WEST, SOUTHWEST, SOUTH, SOUTHEAST, EAST, NORTHEAST, DIRECTION_LENGTH NORTH, NORTHWEST, WEST, SOUTHWEST, SOUTH, SOUTHEAST, EAST, NORTHEAST, DIRECTION_LENGTH
}; };
struct zobristTableElement {
uint_least64_t hash;
int_least16_t value;
};
struct zobristTable {
struct zobristTableElement *arr;
size_t length;
};
struct moveDst_t { struct moveDst_t {
uint_least8_t length; uint_least8_t length;
uint_least8_t dst[8]; uint_least8_t dst[8];
@ -53,6 +64,8 @@ struct castle_t {
struct gameState_t { struct gameState_t {
uint_least64_t *board; uint_least64_t *board;
uint_least64_t zobrist;
struct zobristTable repetitionTable;
bool color; // color to move bool color; // color to move
struct castle_t canCastle[2]; struct castle_t canCastle[2];

18
include/chess/util.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef CHESS_UTIL_H
#define CHESS_UTIL_H
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <chess/types.h>
uint_least64_t rand_64();
uint_least64_t zobristPieceI(struct piece_t piece, uint_least8_t field);
uint_least8_t getFile(const uint_least8_t field);
uint_least8_t getRank(const uint_least8_t field);
struct zobristTable initZobirstTable(struct zobristTableElement *table, size_t length);
void zobristTableIter(const struct zobristTable table, const uint_least64_t key, void *result,
void (*callback)(struct zobristTableElement *element, const uint_least64_t key, void *result));
#endif

View File

@ -0,0 +1,16 @@
#ifndef CHESS_ZOBRIST_CONSTS_H
#define CHESS_ZOBRIST_CONSTS_H
#include "chess/types.h"
#include <stdint.h>
#define ZOBRIST_CASTLE_LENGTH 2
#define ZOBRIST_PIECE_LENGTH (TOTAL_BOARD_SIZE * 2 * PIECES_LENGTH)
extern const uint_least64_t ZOBRIST_PIECE[];
extern const uint_least64_t ZOBRIST_BLACK_MOVE;
extern const uint_least64_t ZOBRIST_SHORT_CASTLE[ZOBRIST_CASTLE_LENGTH];
extern const uint_least64_t ZOBRIST_LONG_CASTLE[ZOBRIST_CASTLE_LENGTH];
extern const uint_least64_t ZOBRIST_EN_PASSENT_FILE[BOARD_SIZE];
#endif

View File

@ -3,23 +3,22 @@
#include <stdint.h> #include <stdint.h>
#include <chess/bitboard.h> #include <chess/bitboard.h>
#include <chess/print.h> #include <chess/print.h>
#include <stdio.h>
// These min and max values will not overflow, when negated // These min and max values will not overflow, when negated
#define INT_16_SAFE_MIN (INT_LEAST16_MIN + 1) #define INT_16_SAFE_MIN (INT_LEAST16_MIN + 1)
#define INT_16_SAFE_MAX (INT_LEAST16_MAX - 1) #define INT_16_SAFE_MAX (INT_LEAST16_MAX - 1)
#ifdef __GNUC__ // Check if using GCC or compatible compiler #ifdef __GNUC__ // Check if using GCC or compatible compiler
static int_least16_t countOnes(uint_least64_t num) { static int_least16_t countOnes(const uint_least64_t num) {
return __builtin_popcountll(num); // Use GCC built-in function if available return __builtin_popcountll(num); // Use GCC built-in function if available
} }
#elif defined(_MSC_VER) // Check if using Microsoft Visual C++ #elif defined(_MSC_VER) // Check if using Microsoft Visual C++
#include <intrin.h> #include <intrin.h>
static int_least16_t countOnes(uint_least64_t num) { static int_least16_t countOnes(const uint_least64_t num) {
return __popcnt64(num); // Use MSVC intrinsic if available return __popcnt64(num); // Use MSVC intrinsic if available
} }
#else #else
static int_least16_t countOnes(uint_least64_t num) { static int_least16_t countOnes(const uint_least64_t num) {
int_least16_t count = 0; int_least16_t count = 0;
while(num) { while(num) {
count += num & 1; count += num & 1;
@ -30,6 +29,7 @@ static int_least16_t countOnes(uint_least64_t num) {
#endif #endif
static int_least16_t evaluate(const struct gameState_t gameState) { static int_least16_t evaluate(const struct gameState_t gameState) {
if(gameState.halfMoveCounter == 50) return 0;
int_least16_t value = 0; int_least16_t value = 0;
const uint_least64_t *board = gameState.board; const uint_least64_t *board = gameState.board;
int_least16_t pieceValues[PIECES_LENGTH]; int_least16_t pieceValues[PIECES_LENGTH];
@ -56,15 +56,15 @@ static int_least16_t alphaBeta(const struct gameState_t gameState, int_fast16_t
uint_least8_t movesLength = validMoves(gameState, moves); uint_least8_t movesLength = validMoves(gameState, moves);
if(movesLength == 0) { if(movesLength == 0) {
if(kingInCheck(gameState.board, gameState.color)) { if(kingInCheck(gameState.board, gameState.color)) {
return INT_16_SAFE_MIN + 1; return INT_16_SAFE_MIN + 1; // checkmate
} }
return 0; return 0; // stalemate
} }
for(uint_least8_t i = 0; i < movesLength; ++i) { for(uint_least8_t i = 0; i < movesLength; ++i) {
const struct move_t move = moves[i]; const struct move_t move = moves[i];
const struct gameState_t newGameState = makeMove(gameState, move); const struct gameState_t newGameState = makeMove(gameState, move);
const int_least16_t score = -alphaBeta(newGameState, -beta, -alpha, depth - 1); const int_least16_t score = -alphaBeta(newGameState, -beta, -alpha, depth - 1);
undoMove(gameState.board, move, gameState.color); undoMove(newGameState, move, gameState.color);
if(score >= beta) { if(score >= beta) {
return beta; // beta-cutoff return beta; // beta-cutoff
} }
@ -85,7 +85,7 @@ static struct move_t searchRoot(const struct gameState_t gameState, uint_least8_
const struct move_t move = moves[i]; const struct move_t move = moves[i];
const struct gameState_t newGameState = makeMove(gameState, move); const struct gameState_t newGameState = makeMove(gameState, move);
const int_least16_t score = -alphaBeta(newGameState, -beta, -alpha, depth - 1); const int_least16_t score = -alphaBeta(newGameState, -beta, -alpha, depth - 1);
undoMove(gameState.board, move, gameState.color); undoMove(newGameState, move, gameState.color);
if(score > alpha) { if(score > alpha) {
alpha = score; // alpha acts like max in MiniMax alpha = score; // alpha acts like max in MiniMax
selectedMove = move; selectedMove = move;

View File

@ -10,11 +10,7 @@
#include <chess/move.h> #include <chess/move.h>
#include <chess/bitboard.h> #include <chess/bitboard.h>
#include "evaluate.h" #include "evaluate.h"
#include <chess/util.h>
#define drawPiece(file, rank, piece) do { \
const RsvgRectangle rect = {xOffset + (file) * fieldSize, yOffset + (rank) * fieldSize, fieldSize, fieldSize};\
rsvg_handle_render_document(piecesSvg[pieceToSvgI(piece)], cr, &rect, NULL); \
} while(0)
struct drawData_t { struct drawData_t {
RsvgHandle **piecesSvg; RsvgHandle **piecesSvg;
@ -30,34 +26,47 @@ struct drawData_t {
}; };
const uint_least8_t PROMOTION_PIECES[] = {QUEEN, KNIGHT, BISHOP, ROOK}; const uint_least8_t PROMOTION_PIECES[] = {QUEEN, KNIGHT, BISHOP, ROOK};
static bool turn = WHITE;
static uint_least8_t pieceToSvgI(struct piece_t piece) { static uint_least8_t pieceToSvgI(struct piece_t piece) {
return piece.color == WHITE ? piece.type | 8 : piece.type; return piece.color == WHITE ? PIECES_LENGTH + piece.type : piece.type;
} }
static void selectCircle(cairo_t *cr, const struct drawData_t *drawData, uint_least8_t field, double radius) { static void drawPiece(struct drawData_t *ctx, cairo_t *cr, uint_least8_t field, struct piece_t piece) {
uint_least8_t file = field % BOARD_SIZE; const RsvgRectangle rect = {ctx->xOffset + getFile(field)* ctx->fieldSize,
uint_least8_t rank = field / BOARD_SIZE; ctx->yOffset + getRank(field) * ctx->fieldSize, ctx->fieldSize, ctx->fieldSize};
double xOffset = drawData->xOffset; rsvg_handle_render_document(ctx->piecesSvg[pieceToSvgI(piece)], cr, &rect, NULL);
double yOffset = drawData->yOffset; }
double fieldSize = drawData->fieldSize;
cairo_arc(cr, xOffset + (file + 0.5) * fieldSize, yOffset + (rank + 0.5) * fieldSize, radius, 0, 2 * M_PI);
static void selectCircle(cairo_t *cr, const struct drawData_t *drawData, uint_least8_t field, double radius) {
const double xOffset = drawData->xOffset;
const double yOffset = drawData->yOffset;
const double fieldSize = drawData->fieldSize;
cairo_arc(cr, xOffset + (getFile(field) + 0.5) * fieldSize, yOffset + (getRank(field) + 0.5) * fieldSize, radius, 0, 2 * M_PI);
} }
static struct gameState_t userMove(struct gameState_t gameState, struct move_t move) { static struct gameState_t userMove(struct gameState_t gameState, struct move_t move) {
gameState = makeMove(gameState, move); gameState = makeMove(gameState, move);
#ifdef NO_COMPUTER #ifndef NO_COMPUTER
turn = !turn;
#else
move = bestMove(gameState); move = bestMove(gameState);
gameState = makeMove(gameState, move); gameState = makeMove(gameState, move);
#endif #endif
return gameState; return gameState;
} }
static uint_least8_t promotionUiPos(uint_least8_t dst) { static uint_least8_t promotionUiPos(uint_least8_t dst, bool color) {
return dst >= 1 ? dst - 1 : 0; const uint_least8_t rankI = getBaseRankI(!color);
uint_least8_t uiPos = dst >= rankI + 1 ? dst - 1 : dst;
const uint_least8_t overflowI = rankI + BOARD_SIZE;
if(uiPos + LENGTH(PROMOTION_PIECES) > overflowI) uiPos = overflowI - LENGTH(PROMOTION_PIECES);
return uiPos;
}
static void drawRetangle(cairo_t *cr, struct drawData_t *drawData, uint_least8_t field, double width) {
const double fieldSize = drawData->fieldSize;
cairo_rectangle(cr, drawData->xOffset + getFile(field) * fieldSize,
drawData->yOffset + getRank(field) * fieldSize, width, fieldSize);
cairo_fill(cr);
} }
static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer data) { static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height, gpointer data) {
@ -65,7 +74,8 @@ static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height,
const double xOffset = (width - fieldSize * BOARD_SIZE) / 2; const double xOffset = (width - fieldSize * BOARD_SIZE) / 2;
const double yOffset = (height - fieldSize * BOARD_SIZE) / 2; const double yOffset = (height - fieldSize * BOARD_SIZE) / 2;
struct drawData_t *drawData = (struct drawData_t*)data; struct drawData_t *drawData = (struct drawData_t*)data;
const uint_least64_t *board = drawData->gameState->board; const struct gameState_t *gameState = drawData->gameState;
const uint_least64_t *board = gameState->board;
RsvgHandle **piecesSvg = drawData->piecesSvg; RsvgHandle **piecesSvg = drawData->piecesSvg;
drawData->xOffset = xOffset; drawData->xOffset = xOffset;
drawData->yOffset = yOffset; drawData->yOffset = yOffset;
@ -73,13 +83,12 @@ static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height,
for(uint_least8_t file = 0; file < BOARD_SIZE; ++file) { for(uint_least8_t file = 0; file < BOARD_SIZE; ++file) {
for(uint_least8_t rank = 0; rank < BOARD_SIZE; ++rank) { for(uint_least8_t rank = 0; rank < BOARD_SIZE; ++rank) {
const uint_least8_t field = rank * BOARD_SIZE + file; const uint_least8_t field = rank * BOARD_SIZE + file;
cairo_rectangle(cr, xOffset + file * fieldSize, yOffset + rank * fieldSize, fieldSize, fieldSize);
if((file + rank) & 1) cairo_set_source_rgb(cr, 0.46274, 0.5882, 0.3373); if((file + rank) & 1) cairo_set_source_rgb(cr, 0.46274, 0.5882, 0.3373);
else cairo_set_source_rgb(cr, 0.9333, 0.9333, 0.8235); else cairo_set_source_rgb(cr, 0.9333, 0.9333, 0.8235);
cairo_fill(cr); drawRetangle(cr, drawData, field, fieldSize);
if(bitboardGetAllPieces(board, field)) { if(bitboardGetAllPieces(board, field)) {
const struct piece_t piece = pieceAtField(board, rank * BOARD_SIZE + file); const struct piece_t piece = pieceAtField(board, rank * BOARD_SIZE + file);
drawPiece(file, rank, piece); drawPiece(drawData, cr, field, piece);
} }
} }
} }
@ -94,14 +103,12 @@ static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height,
} }
} }
if(drawData->selectDst != NOT_SELECTED) { if(drawData->selectDst != NOT_SELECTED) {
uint_least8_t uiPos = promotionUiPos(drawData->selectDst); uint_least8_t uiPos = promotionUiPos(drawData->selectDst, gameState->color);
if(uiPos + 3 >= BOARD_SIZE) uiPos = BOARD_SIZE - LENGTH(PROMOTION_PIECES);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5); cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_rectangle(cr, xOffset + uiPos * fieldSize, yOffset, 4 * fieldSize, fieldSize); drawRetangle(cr, drawData, uiPos, 4 * fieldSize);
cairo_fill(cr);
for(uint_least8_t i = 0; i < LENGTH(PROMOTION_PIECES); ++i) { for(uint_least8_t i = 0; i < LENGTH(PROMOTION_PIECES); ++i) {
const struct piece_t piece = {PROMOTION_PIECES[i], true}; const struct piece_t piece = {PROMOTION_PIECES[i], gameState->color};
drawPiece(uiPos + i, 0, piece); drawPiece(drawData, cr, uiPos + i, piece);
} }
} }
} }
@ -118,10 +125,11 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y,
const uint_least8_t file = adjustedX / fieldSize; const uint_least8_t file = adjustedX / fieldSize;
const uint_least8_t rank = adjustedY / fieldSize; const uint_least8_t rank = adjustedY / fieldSize;
const uint_least8_t field = file + rank * BOARD_SIZE; const uint_least8_t field = file + rank * BOARD_SIZE;
const bool onPromotionField = rank == getBaseRank(!gameState->color);
if(drawData->selectDst == NOT_SELECTED) { if(drawData->selectDst == NOT_SELECTED) {
const struct piece_t allPiece = {ALL_PIECES, turn}; const struct piece_t allPiece = {ALL_PIECES, gameState->color};
if(bitboardGet(board, allPiece, field)) { if(bitboardGet(board, allPiece, field)) {
// deaktivated piece by clicking on it again // deactivated piece by clicking on it again
if(field == drawData->clickedPiece) { if(field == drawData->clickedPiece) {
drawData->clickedPiece = NOT_SELECTED; drawData->clickedPiece = NOT_SELECTED;
drawData->movesLength = 0; drawData->movesLength = 0;
@ -141,8 +149,8 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y,
} }
} }
if(isValidDst) { if(isValidDst) {
const struct piece_t piece = pieceAtField(board, field); const struct piece_t piece = pieceAtField(board, drawData->clickedPiece);
if(piece.type == PAWN && field < BOARD_SIZE) { // promotion if(piece.type == PAWN && onPromotionField) { // promotion
drawData->selectDst = field; drawData->selectDst = field;
} }
else { else {
@ -162,12 +170,13 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y,
drawData->movesLength = 0; drawData->movesLength = 0;
} }
} }
else { else if(onPromotionField) {
if(rank != 0) return; const uint_least8_t uiPos = promotionUiPos(drawData->selectDst, gameState->color);
const uint_least8_t uiPos = promotionUiPos(drawData->selectDst); const uint_least8_t i = field - uiPos;
const uint_least8_t dstPiece = PROMOTION_PIECES[file - uiPos]; if(i >= LENGTH(PROMOTION_PIECES)) return;
const uint_least8_t dstPiece = PROMOTION_PIECES[i];
const uint_least8_t capturedPiece = pieceAtField(board, drawData->selectDst).type; const uint_least8_t capturedPiece = pieceAtField(board, drawData->selectDst).type;
const struct move_t move = {drawData->clickedPiece, drawData->selectDst, 0, PAWN, dstPiece}; const struct move_t move = {drawData->clickedPiece, drawData->selectDst, 0, PAWN, dstPiece, capturedPiece};
*gameState = userMove(*gameState, move); *gameState = userMove(*gameState, move);
drawData->selectDst = NOT_SELECTED; drawData->selectDst = NOT_SELECTED;
drawData->clickedPiece = NOT_SELECTED; drawData->clickedPiece = NOT_SELECTED;
@ -177,10 +186,11 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y,
static void app_activate(GApplication *app, gpointer data) { static void app_activate(GApplication *app, gpointer data) {
GtkWindow *window = GTK_WINDOW(gtk_window_new()); GtkWindow *window = GTK_WINDOW(gtk_window_new());
static RsvgHandle *piecesSvg[16]; static RsvgHandle *piecesSvg[2 * PIECES_LENGTH];
static uint_least64_t board[BITBOARD_LENGTH]; static uint_least64_t board[BITBOARD_LENGTH];
static struct zobristTableElement repetitionTableStore[REPETETION_TABLE_LENGTH];
static struct gameState_t gameState; static struct gameState_t gameState;
gameState = newGameState(board, "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"); gameState = newGameState(board, repetitionTableStore, START_FEN);
{ {
static struct move_t moves[TOTAL_BOARD_SIZE]; static struct move_t moves[TOTAL_BOARD_SIZE];
static struct drawData_t drawData = {piecesSvg, &gameState, NOT_SELECTED, moves, 0, NOT_SELECTED}; static struct drawData_t drawData = {piecesSvg, &gameState, NOT_SELECTED, moves, 0, NOT_SELECTED};

View File

@ -9,6 +9,10 @@
#include <stdio.h> #include <stdio.h>
#include "magicNumber.h" #include "magicNumber.h"
#include <chess/magic.h> #include <chess/magic.h>
#include <chess/util.h>
#include <chess/bitset.h>
#include <chess/zobristConsts.h>
#include <string.h>
#ifdef _MSC_VER #ifdef _MSC_VER
#include <intrin.h> #include <intrin.h>
@ -29,7 +33,7 @@ static uint_least8_t trailingBits(uint_least64_t num) {
unsigned long i; unsigned long i;
_BitScanForward(&i, num); _BitScanForward(&i, num);
return i; return i;
#elif #else
uint_least8_t count = 0; uint_least8_t count = 0;
while ((num & 1) == 0) { // Keep shifting num right until the least significant bit is 1 while ((num & 1) == 0) { // Keep shifting num right until the least significant bit is 1
num >>= 1; num >>= 1;
@ -133,24 +137,37 @@ bool kingInCheck(const uint_least64_t *board, const bool color) {
return inCheck(board, bitboardGetMask(board, king), color); return inCheck(board, bitboardGetMask(board, king), color);
} }
static uint_least8_t getCastleRankI(bool color) { uint_least8_t getBaseRank(bool color) {
if(color == WHITE) { if(color == WHITE) {
return (BOARD_SIZE - 1) * BOARD_SIZE; return BOARD_SIZE - 1;
} }
return 0; return 0;
} }
uint_least8_t getBaseRankI(bool color) {
return getBaseRank(color) * BOARD_SIZE;
}
static void castleMoveRook(uint_least64_t *board, uint_least8_t spezialMove, bool color, bool undo) { static void castleMoveRook(uint_least64_t *board, uint_least8_t spezialMove, bool color, bool undo) {
if(spezialMove == SHORT_CASTLE || spezialMove == LONG_CASTLE) { if(spezialMove == SHORT_CASTLE || spezialMove == LONG_CASTLE) {
const uint_least8_t rookSrc = getCastleRankI(color) + (spezialMove == SHORT_CASTLE ? BOARD_SIZE - 1 : 0); const uint_least8_t rookSrc = getBaseRankI(color) + (spezialMove == SHORT_CASTLE ? BOARD_SIZE - 1 : 0);
const uint_least8_t rookDst = getCastleRankI(color) + (spezialMove == SHORT_CASTLE ? 5 : 3); const uint_least8_t rookDst = getBaseRankI(color) + (spezialMove == SHORT_CASTLE ? 5 : 3);
const struct piece_t rook = {ROOK, color}; const struct piece_t rook = {ROOK, color};
bitboardSet(board, rook, undo ? rookSrc : rookDst); bitboardSet(board, rook, undo ? rookSrc : rookDst);
bitboardClear(board, rook, undo ? rookDst : rookSrc); bitboardClear(board, rook, undo ? rookDst : rookSrc);
} }
} }
void undoMove(uint_least64_t *board, struct move_t move, bool color) { static void zobristTableUndoMoveCallback(struct zobristTableElement *element, const uint_least64_t key,
void *resultVoid) {
if(element->hash == key) {
if(--element->value == 0) element->hash = 0;
}
fprintf(stderr, "Error: Position does not in repetitionTable in undoMove (move.c)");
}
void undoMove(const struct gameState_t gameState, struct move_t move, bool color) {
uint_least64_t *board = gameState.board;
const struct piece_t srcPiece = {move.srcPiece, color}; const struct piece_t srcPiece = {move.srcPiece, color};
const struct piece_t dstPiece = {move.dstPiece, color}; const struct piece_t dstPiece = {move.dstPiece, color};
bitboardSet(board, srcPiece, move.src); bitboardSet(board, srcPiece, move.src);
@ -160,38 +177,95 @@ void undoMove(uint_least64_t *board, struct move_t move, bool color) {
bitboardSet(board, piece, getCapturePos(move.src, move.dst, move.spezialMove)); bitboardSet(board, piece, getCapturePos(move.src, move.dst, move.spezialMove));
} }
castleMoveRook(board, move.spezialMove, color, true); castleMoveRook(board, move.spezialMove, color, true);
zobristTableIter(gameState.repetitionTable, gameState.zobrist, NULL, zobristTableUndoMoveCallback);
}
static void setCastleInfoRook(struct castle_t *canCastle, uint_least8_t posOnRank) {
switch(posOnRank) {
case 0:
canCastle->longCastle = false;
break;
case BOARD_SIZE - 1:
canCastle->shortCastle = false;
break;
}
}
static void setCastleInfo(struct castle_t *canCastle, const bool color, const struct move_t move) {
{
struct castle_t *canCastleCurrentPlayer = canCastle + color;
if(canCastleCurrentPlayer->longCastle || canCastleCurrentPlayer->shortCastle) {
if(move.dstPiece == KING) {
canCastleCurrentPlayer->longCastle = false;
canCastleCurrentPlayer->shortCastle = false;
}
else {
setCastleInfoRook(canCastleCurrentPlayer, move.src - getBaseRankI(color));
}
}
}
{
// if the rook is captured
struct castle_t *canCastleOther = canCastle + !color;
if(canCastleOther->longCastle || canCastleOther->shortCastle) {
setCastleInfoRook(canCastleOther, move.dst - getBaseRankI(!color));
}
}
} }
static void addMove(struct addMoveCtx_t ctx, struct piece_t movedPiece, uint_least8_t src, static void addMove(struct addMoveCtx_t ctx, struct piece_t movedPiece, uint_least8_t src,
uint_least8_t dst, uint_least8_t spezialMove, uint_least8_t promotionPiece) { uint_least8_t dst, uint_least8_t spezialMove, uint_least8_t promotionPiece) {
uint_least64_t *board = ctx.gameState.board; struct gameState_t gameState = ctx.gameState;
uint_least8_t *movesLength = ctx.movesLength; uint_least64_t *board = gameState.board;
uint_least8_t capturedPiece = NOT_SELECTED;
const uint_least8_t dstPiece = promotionPiece == NOT_SELECTED ? movedPiece.type : promotionPiece;
const struct piece_t pieceAll = {ALL_PIECES, movedPiece.color};
if(bitboardGet(board, pieceAll, dst)) return;
{ {
const struct piece_t pieceAllOtherColor = {ALL_PIECES, !movedPiece.color}; const struct piece_t pieceAll = {ALL_PIECES, movedPiece.color};
if(bitboardGet(board, pieceAll, dst)) return;
}
uint_least8_t *movesLength = ctx.movesLength;
uint_least8_t capturedPieceType = NOT_SELECTED;
const uint_least8_t dstPieceType = promotionPiece == NOT_SELECTED ? movedPiece.type : promotionPiece;
uint_least64_t hash = ZOBRIST_PIECE[zobristPieceI(movedPiece, src)] ^ ZOBRIST_BLACK_MOVE;
{
const struct piece_t dstPiece = {dstPieceType, movedPiece.color};
hash ^= ZOBRIST_PIECE[zobristPieceI(dstPiece, dst)];
}
{
const bool otherColor = !movedPiece.color;
const struct piece_t pieceAllOtherColor = {ALL_PIECES, otherColor};
const uint_least8_t capturePos = getCapturePos(src, dst, spezialMove); const uint_least8_t capturePos = getCapturePos(src, dst, spezialMove);
if(bitboardGet(board, pieceAllOtherColor, capturePos)) { if(bitboardGet(board, pieceAllOtherColor, capturePos)) {
for(uint_least8_t pieceType = QUEEN; pieceType < PIECES_LENGTH; ++pieceType) { for(uint_least8_t pieceType = QUEEN; pieceType < PIECES_LENGTH; ++pieceType) {
const struct piece_t piece = {pieceType, !movedPiece.color}; const struct piece_t piece = {pieceType, !movedPiece.color};
if(bitboardGet(board, piece, capturePos)) { if(bitboardGet(board, piece, capturePos)) {
capturedPiece = pieceType; const struct piece_t capturedPiece = {pieceType, otherColor};
hash ^= ZOBRIST_PIECE[zobristPieceI(capturedPiece, dst)];
capturedPieceType = pieceType;
break; break;
} }
} }
} }
} }
const struct move_t move = {src, dst, spezialMove, movedPiece.type, dstPiece, capturedPiece}; if(gameState.enPassantTo != NOT_SELECTED) hash ^= ZOBRIST_EN_PASSENT_FILE[getFile(gameState.enPassantTo)];
makeMove(ctx.gameState, move); if(spezialMove == FUTURE_EN_PASSANT) hash ^= ZOBRIST_EN_PASSENT_FILE[getFile(src)];
struct move_t move = {src, dst, spezialMove, movedPiece.type, dstPieceType, capturedPieceType, hash};
{ {
const bool color = ctx.gameState.color; struct castle_t canCastle[2];
memcpy(canCastle, gameState.canCastle, sizeof canCastle);
setCastleInfo(canCastle, gameState.color, move);
for(uint_least8_t color = BLACK; color <= WHITE; ++color) {
if(canCastle[color].shortCastle != gameState.canCastle[color].shortCastle) hash ^= ZOBRIST_SHORT_CASTLE[color];
if(canCastle[color].longCastle != gameState.canCastle[color].longCastle) hash ^= ZOBRIST_LONG_CASTLE[color];
}
move.hash = hash;
}
struct gameState_t newGameState = makeMove(gameState, move);
{
const bool color = gameState.color;
if(!kingInCheck(board, color)) { if(!kingInCheck(board, color)) {
ctx.moves[*movesLength] = move; ctx.moves[*movesLength] = move;
++*movesLength; ++*movesLength;
} }
undoMove(board, move, color); undoMove(newGameState, move, color);
} }
} }
@ -248,7 +322,7 @@ uint_least8_t pieceValidMoves(struct gameState_t gameState, struct piece_t piece
addPreGenMoves(addmoveCtx, KING_MOVES, src, piece); addPreGenMoves(addmoveCtx, KING_MOVES, src, piece);
const struct castle_t canCastle = gameState.canCastle[piece.color]; const struct castle_t canCastle = gameState.canCastle[piece.color];
const uint_least64_t allPiecesMask = bitboardMaskAllPieces(board); const uint_least64_t allPiecesMask = bitboardMaskAllPieces(board);
const uint_least8_t rankShift = piece.color == WHITE ? BOARD_SIZE * (BOARD_SIZE - 1) : 0; const uint_least8_t rankShift = getBaseRankI(piece.color);
const uint_least64_t shortCastleMask = (uint_least64_t)0b11 << (5 + rankShift); const uint_least64_t shortCastleMask = (uint_least64_t)0b11 << (5 + rankShift);
const uint_least64_t shortCastleCheckMask = (uint_least64_t)0b11 << (4 + rankShift); const uint_least64_t shortCastleCheckMask = (uint_least64_t)0b11 << (4 + rankShift);
const uint_least64_t longCastleMask = (uint_least64_t)0b111 << (1 + rankShift); const uint_least64_t longCastleMask = (uint_least64_t)0b111 << (1 + rankShift);
@ -299,7 +373,7 @@ uint_least8_t pieceValidMoves(struct gameState_t gameState, struct piece_t piece
uint_least8_t dst = src + DIRECTION_MODIFIER[pawnDirection]; uint_least8_t dst = src + DIRECTION_MODIFIER[pawnDirection];
if(!bitboardGetAllPieces(board, dst)) { if(!bitboardGetAllPieces(board, dst)) {
movePawn(addmoveCtx, piece.color, src, dst, promotion, 0); movePawn(addmoveCtx, piece.color, src, dst, promotion, 0);
if(src / BOARD_SIZE == (piece.color == WHITE ? BOARD_SIZE - 2 : 1)) { if(getRank(src) == (piece.color == WHITE ? BOARD_SIZE - 2 : 1)) {
dst += DIRECTION_MODIFIER[pawnDirection]; dst += DIRECTION_MODIFIER[pawnDirection];
if(!bitboardGetAllPieces(board, dst)) { if(!bitboardGetAllPieces(board, dst)) {
movePawn(addmoveCtx, piece.color, src, dst, promotion, FUTURE_EN_PASSANT); movePawn(addmoveCtx, piece.color, src, dst, promotion, FUTURE_EN_PASSANT);
@ -333,18 +407,17 @@ uint_least8_t validMoves(struct gameState_t gameState, struct move_t *moves) {
return movesLength; return movesLength;
} }
static void setCastleInfo(struct castle_t *canCastle, uint_least8_t posOnRank) { static void zobristTableMakeMoveCallback(struct zobristTableElement *element, const uint_least64_t key,
switch(posOnRank) { void *resultVoid) {
case 0: if(element->hash == key) {
canCastle->longCastle = false; ++element->value;
break; return;
case BOARD_SIZE - 1:
canCastle->shortCastle = false;
break;
} }
element->hash = key;
element->value = 1;
} }
struct gameState_t makeMove(struct gameState_t gameState, struct move_t move) { struct gameState_t makeMove(struct gameState_t gameState, const struct move_t move) {
uint_least64_t *board = gameState.board; uint_least64_t *board = gameState.board;
const struct piece_t srcPiece = {move.srcPiece, gameState.color}; const struct piece_t srcPiece = {move.srcPiece, gameState.color};
const struct piece_t dstPiece = {move.dstPiece, gameState.color}; const struct piece_t dstPiece = {move.dstPiece, gameState.color};
@ -355,25 +428,7 @@ struct gameState_t makeMove(struct gameState_t gameState, struct move_t move) {
bitboardClear(board, capturedPiece, getCapturePos(move.src, move.dst, move.spezialMove)); bitboardClear(board, capturedPiece, getCapturePos(move.src, move.dst, move.spezialMove));
} }
castleMoveRook(board, move.spezialMove, gameState.color, false); castleMoveRook(board, move.spezialMove, gameState.color, false);
{ setCastleInfo(gameState.canCastle, gameState.color, move);
struct castle_t *canCastleCurrentPlayer = gameState.canCastle + gameState.color;
if(canCastleCurrentPlayer->longCastle || canCastleCurrentPlayer->shortCastle) {
if(move.dstPiece == KING) {
canCastleCurrentPlayer->longCastle = false;
canCastleCurrentPlayer->shortCastle = false;
}
else {
setCastleInfo(canCastleCurrentPlayer, move.src - getCastleRankI(gameState.color));
}
}
}
{
// if the rook is captured
struct castle_t *canCastleOther = gameState.canCastle + !gameState.color;
if(canCastleOther->longCastle || canCastleOther->shortCastle) {
setCastleInfo(canCastleOther, move.dst - getCastleRankI(!gameState.color));
}
}
{ {
const int_least8_t dist = (int_least8_t)move.dst - (int_least8_t)move.src; const int_least8_t dist = (int_least8_t)move.dst - (int_least8_t)move.src;
gameState.enPassantTo = move.spezialMove == FUTURE_EN_PASSANT ? move.src + dist / 2 : NOT_SELECTED; gameState.enPassantTo = move.spezialMove == FUTURE_EN_PASSANT ? move.src + dist / 2 : NOT_SELECTED;
@ -381,5 +436,7 @@ struct gameState_t makeMove(struct gameState_t gameState, struct move_t move) {
if(move.capturedPiece != NOT_SELECTED || srcPiece.type == PAWN) gameState.halfMoveCounter = 0; if(move.capturedPiece != NOT_SELECTED || srcPiece.type == PAWN) gameState.halfMoveCounter = 0;
else ++gameState.halfMoveCounter; else ++gameState.halfMoveCounter;
gameState.color = !gameState.color; gameState.color = !gameState.color;
gameState.zobrist ^= move.hash;
zobristTableIter(gameState.repetitionTable, gameState.zobrist, NULL, zobristTableMakeMoveCallback);
return gameState; return gameState;
} }

View File

@ -1,20 +1,12 @@
#include "chess/zobristConsts.h"
#include <ctype.h> #include <ctype.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include <chess/types.h> #include <chess/types.h>
#include <stdlib.h> #include <stdlib.h>
#include <chess/util.h>
bool bitsetGet(uint_least64_t bitset, uint_least8_t i) { #include <chess/bitset.h>
return (bitset >> i) & 1u; #include <chess/bitboard.h>
}
uint_least64_t bitsetClear(uint_least64_t bitset, uint_least8_t i) {
return bitset & ~((uint_least64_t)1 << i);
}
uint_least64_t bitsetSet(uint_least64_t bitset, uint_least8_t i) {
return bitset | ((uint_least64_t)1 << i);
}
uint_least64_t bitboardGetMask(const uint_least64_t *board, struct piece_t piece) { uint_least64_t bitboardGetMask(const uint_least64_t *board, struct piece_t piece) {
return board[piece.color * PIECES_LENGTH + piece.type]; return board[piece.color * PIECES_LENGTH + piece.type];
@ -75,62 +67,80 @@ struct piece_t pieceAtField(const uint_least64_t *board, uint_least8_t i) {
} }
} }
struct gameState_t newGameState(uint_least64_t *board, char *FEN) { struct gameState_t parseFen(uint_least64_t *board, const char *FEN) {
struct gameState_t gameState = {board}; struct gameState_t gameState = {board, 0};
for(uint_least8_t i = 0; i < BITBOARD_LENGTH; ++i) { for(uint_least8_t i = 0; i < BITBOARD_LENGTH; ++i) {
board[i] = 0; board[i] = 0;
}
for(uint_least8_t field = 0; *FEN != ' '; ++FEN) {
struct piece_t piece;
switch(tolower(*FEN)) {
case 'k':
piece.type = KING;
break;
case 'q':
piece.type = QUEEN;
break;
case 'r':
piece.type = ROOK;
break;
case 'n':
piece.type = KNIGHT;
break;
case 'b':
piece.type = BISHOP;
break;
case 'p':
piece.type = PAWN;
break;
case '/':
continue;
default:
if(isdigit(*FEN)) {
field += *FEN - '0';
}
continue;
}
if(isalpha(*FEN)) piece.color = isupper(*FEN);
bitboardSet(board, piece, field);
++field;
}
gameState.color = *++FEN == 'w' ? WHITE : BLACK;
FEN += 2;
if(*FEN == '-') ++FEN;
else {
for(; *FEN != ' '; ++FEN) {
struct castle_t *playerCastle = gameState.canCastle + (isupper(*FEN) ? WHITE : BLACK);
if(tolower(*FEN) == 'k') playerCastle->shortCastle = true;
else playerCastle->longCastle = true;
}
}
if(*++FEN == '-'){
++FEN;
gameState.enPassantTo = NOT_SELECTED;
}
else {
gameState.enPassantTo = *FEN++ - 'a';
gameState.enPassantTo += (BOARD_SIZE - *FEN++) * BOARD_SIZE;
}
gameState.halfMoveCounter = atoi(++FEN);
return gameState;
} }
for(uint_least8_t field = 0; *FEN != ' '; ++FEN) {
struct piece_t piece;
switch(tolower(*FEN)) {
case 'k':
piece.type = KING;
break;
case 'q':
piece.type = QUEEN;
break;
case 'r':
piece.type = ROOK;
break;
case 'n':
piece.type = KNIGHT;
break;
case 'b':
piece.type = BISHOP;
break;
case 'p':
piece.type = PAWN;
break;
case '/':
continue;
default:
if(isdigit(*FEN)) {
field += *FEN - '0';
}
continue;
}
if(isalpha(*FEN)) piece.color = isupper(*FEN);
bitboardSet(board, piece, field);
gameState.zobrist ^= ZOBRIST_PIECE[zobristPieceI(piece, field)];
++field;
}
gameState.color = *++FEN == 'w' ? WHITE : BLACK;
if(gameState.color == BLACK) gameState.zobrist ^= ZOBRIST_BLACK_MOVE;
FEN += 2;
if(*FEN == '-') ++FEN;
else {
for(; *FEN != ' '; ++FEN) {
const bool color = isupper(*FEN) ? WHITE : BLACK;
struct castle_t *playerCastle = gameState.canCastle + color;
if(tolower(*FEN) == 'k') {
playerCastle->shortCastle = true;
gameState.zobrist ^= ZOBRIST_SHORT_CASTLE[color];
}
else {
playerCastle->longCastle = true;
gameState.zobrist ^= ZOBRIST_LONG_CASTLE[color];
}
}
}
if(*++FEN == '-'){
++FEN;
gameState.enPassantTo = NOT_SELECTED;
}
else {
const uint_least8_t enPassentFile = *FEN++ - 'a';
gameState.zobrist ^= ZOBRIST_EN_PASSENT_FILE[enPassentFile];
gameState.enPassantTo = enPassentFile;
gameState.enPassantTo += (BOARD_SIZE - *FEN++) * BOARD_SIZE;
}
gameState.halfMoveCounter = atoi(++FEN);
return gameState;
}
struct gameState_t newGameState(uint_least64_t *board,
struct zobristTableElement *repetitionTableStore, const char *FEN) {
struct gameState_t gameState = parseFen(board, FEN);
gameState.repetitionTable = initZobirstTable(repetitionTableStore, REPETETION_TABLE_LENGTH);
return gameState;
}

View File

@ -1,7 +1,7 @@
#include <stdint.h> #include <stdint.h>
#include <chess/types.h> #include <chess/types.h>
#include <chess/generated/moveConsts.h> #include <chess/generated/moveConsts.h>
#include <chess/bitboard.h> #include <chess/bitset.h>
uint_least64_t slidingMovementMask(const uint_least8_t *direction, uint_least8_t directionLength, uint_least64_t slidingMovementMask(const uint_least8_t *direction, uint_least8_t directionLength,
uint_least8_t field, uint_least64_t blockMask) { uint_least8_t field, uint_least64_t blockMask) {

View File

@ -0,0 +1,14 @@
#include <chess/bitset.h>
bool bitsetGet(uint_least64_t bitset, uint_least8_t i) {
return (bitset >> i) & 1u;
}
uint_least64_t bitsetClear(uint_least64_t bitset, uint_least8_t i) {
return bitset & ~((uint_least64_t)1 << i);
}
uint_least64_t bitsetSet(uint_least64_t bitset, uint_least8_t i) {
return bitset | ((uint_least64_t)1 << i);
}

View File

@ -2,7 +2,8 @@
#include "chess/types.h" #include "chess/types.h"
#include <stdio.h> #include <stdio.h>
#include <stdint.h> #include <stdint.h>
#include <chess/bitboard.h> #include <chess/bitset.h>
#include <chess/util.h>
void printerll(FILE *file, long long num) { void printerll(FILE *file, long long num) {
fprintf(file, "%lld", num); fprintf(file, "%lld", num);
@ -23,8 +24,8 @@ void printPieceMask(uint_least64_t mask) {
} }
void fieldToString(uint_least8_t field, char *output) { void fieldToString(uint_least8_t field, char *output) {
output[0] = 'a' + field % BOARD_SIZE; output[0] = 'a' + getFile(field);
output[1] = '0' + BOARD_SIZE - field / BOARD_SIZE; output[1] = '0' + BOARD_SIZE - getRank(field);
output[2] = '\0'; output[2] = '\0';
} }

View File

@ -0,0 +1,78 @@
#include "chess/types.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <chess/util.h>
uint_least64_t rand_64() {
uint_least64_t u1, u2, u3, u4;
u1 = (uint_least64_t)(rand()) & 0xFFFF; u2 = (uint_least64_t)(rand()) & 0xFFFF;
u3 = (uint_least64_t)(rand()) & 0xFFFF; u4 = (uint_least64_t)(rand()) & 0xFFFF;
return u1 | (u2 << 16) | (u3 << 32) | (u4 << 48);
}
uint_least64_t zobristPieceI(struct piece_t piece, uint_least8_t field) {
return field * 2 * PIECES_LENGTH + piece.color * PIECES_LENGTH + piece.type;
}
uint_least8_t getFile(const uint_least8_t field) {
return field % BOARD_SIZE;
}
uint_least8_t getRank(const uint_least8_t field) {
return field / BOARD_SIZE;
}
struct zobristTable initZobirstTable(struct zobristTableElement *table, size_t length) {
for(size_t i = 0; i < length; ++i) {
table[i].hash = 0;
}
return (struct zobristTable){table, length};
}
static size_t zobristTableInitialI(const struct zobristTable table, const uint_least64_t key) {
return key % table.length;
}
void zobristTableIter(const struct zobristTable table, const uint_least64_t key, void *result,
void (*callback)(struct zobristTableElement *element, const uint_least64_t key, void *result)) {
const size_t initI = zobristTableInitialI(table, key);
size_t i = initI;
do {
struct zobristTableElement *element = table.arr + i;
if(element->hash == 0 || element->hash == key) {
callback(element, key, result);
}
++i;
if(i >= table.length) i = 0;
} while(i != initI);
fprintf(stderr, "Error: zobrist table is full");
exit(1);
}
static void zobristTableItemPtrCallback(struct zobristTableElement *element, const uint_least64_t key,
void *resultVoid) {
struct zobristTableElement **result = resultVoid;
if(element->hash == key) {
*result = element;
return;
} // if(hash == 0)
*result = NULL;
}
static struct zobristTableElement* zobristTableItemPtr(const struct zobristTable table, const uint_least64_t key) {
struct zobristTableElement *result;
zobristTableIter(table, key, &result, zobristTableItemPtrCallback);
return result;
}
void zobristTableDelete(struct zobristTable table, const uint_least64_t key) {
struct zobristTableElement *element = zobristTableItemPtr(table, key);
if(element != NULL) element->hash = 0;
}
int_least16_t* zobristTableGetPtr(const struct zobristTable table, const uint_least64_t key) {
struct zobristTableElement *element = zobristTableItemPtr(table, key);
if(element == NULL) return NULL;
return &element->value;
}

View File

@ -9,18 +9,13 @@
#include <chess/magic.h> #include <chess/magic.h>
#include <chess/generated/moveConsts.h> #include <chess/generated/moveConsts.h>
#include <chess/bitboard.h> #include <chess/bitboard.h>
#include <chess/bitset.h>
#include <chess/util.h>
#define MAX_BITS 12 #define MAX_BITS 12
#define MAX_SIZE (1 << MAX_BITS) #define MAX_SIZE (1 << MAX_BITS)
#define ATTACK_TABLE_LENGTH (2 * MAX_SIZE * TOTAL_BOARD_SIZE) #define ATTACK_TABLE_LENGTH (2 * MAX_SIZE * TOTAL_BOARD_SIZE)
static uint_least64_t rand_64() {
uint_least64_t u1, u2, u3, u4;
u1 = (uint_least64_t)(rand()) & 0xFFFF; u2 = (uint_least64_t)(rand()) & 0xFFFF;
u3 = (uint_least64_t)(rand()) & 0xFFFF; u4 = (uint_least64_t)(rand()) & 0xFFFF;
return u1 | (u2 << 16) | (u3 << 32) | (u4 << 48);
}
static uint_least64_t randFewBits() { static uint_least64_t randFewBits() {
return rand_64() & rand_64() & rand_64(); return rand_64() & rand_64() & rand_64();
} }

View File

@ -2,8 +2,9 @@
#include <chess/types.h> #include <chess/types.h>
#include <stdint.h> #include <stdint.h>
#include <inttypes.h> #include <inttypes.h>
#include <chess/bitboard.h> #include <chess/bitset.h>
#include <chess/print.h> #include <chess/print.h>
#include <chess/util.h>
static void printerMoveDst(FILE *file, struct moveDst_t moveDst) { static void printerMoveDst(FILE *file, struct moveDst_t moveDst) {
fprintf(file, "{ %" PRIuLEAST8 ", ", moveDst.length); fprintf(file, "{ %" PRIuLEAST8 ", ", moveDst.length);
@ -36,8 +37,8 @@ int main() {
); );
uint_least8_t directionOffset[TOTAL_BOARD_SIZE * DIRECTION_LENGTH]; uint_least8_t directionOffset[TOTAL_BOARD_SIZE * DIRECTION_LENGTH];
for(uint_least16_t field = 0; field < TOTAL_BOARD_SIZE; ++field) { for(uint_least16_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
uint_least8_t file = field % BOARD_SIZE; uint_least8_t file = getFile(field);
uint_least8_t rank = field / BOARD_SIZE; uint_least8_t rank = getRank(field);
uint_least8_t *offsetField = directionOffset + field * DIRECTION_LENGTH; uint_least8_t *offsetField = directionOffset + field * DIRECTION_LENGTH;
offsetField[NORTH] = rank; offsetField[NORTH] = rank;
offsetField[WEST] = file; offsetField[WEST] = file;

View File

@ -0,0 +1,39 @@
#include "chess/types.h"
#include "chess/zobristConsts.h"
#include <stdint.h>
#include <stdio.h>
#include <chess/util.h>
#include <stdlib.h>
#include <chess/print.h>
#include <inttypes.h>
int main() {
srand(0xc0229b8e);
printf("#include <stdint.h>\n");
{
uint_least64_t zobristPiece[ZOBRIST_PIECE_LENGTH];
for(uint_least8_t color = BLACK; color <= WHITE; ++color) {
for(uint_least8_t pieceType = KING; pieceType < PIECES_LENGTH; ++pieceType) {
const struct piece_t piece = {pieceType, color};
for(uint_least8_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
zobristPiece[zobristPieceI(piece, field)] = rand_64();
}
}
}
defineArray("const uint_least64_t ZOBRIST_PIECE", printerull, zobristPiece);
}
printf("const uint_least64_t ZOBRIST_BLACK_MOVE = %" PRIuLEAST64 "u;\n", rand_64());
{
uint_least64_t zobristRest[4 + BOARD_SIZE]; // 4 for castling
for(uint_least8_t i = 0; i < LENGTH(zobristRest); ++i) {
zobristRest[i] = rand_64();
}
uint_least64_t *zobristRestPtr = zobristRest;
const size_t castleLength = 2;
defineArrayCustomLength("const uint_least64_t ZOBRIST_SHORT_CASTLE", printerull, zobristRestPtr, castleLength);
zobristRestPtr += castleLength;
defineArrayCustomLength("const uint_least64_t ZOBRIST_LONG_CASTLE", printerull, zobristRestPtr, castleLength);
zobristRestPtr += castleLength;
defineArrayCustomLength("const uint_least64_t ZOBRIST_EN_PASSENT_FILE", printerull, zobristRestPtr, (size_t)BOARD_SIZE);
}
}

72
src/uci.c Normal file
View File

@ -0,0 +1,72 @@
#include "chess/move.h"
#include "chess/types.h"
#include <stdint.h>
#include <stdio.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <string.h>
#include <stddef.h>
#include <chess/bitboard.h>
atomic_bool searchShouldStop;
#define customStrCmp(input, cmd) customStrCmpFunc(&(input), cmd, sizeof(cmd))
static bool customStrCmpFunc(char **input, const char *cmd, const size_t length) {
bool result = strncmp(*input, cmd, length) == 0;
if(result) *input += length + 1;
return result;
}
int main() {
char input[4096];
struct gameState_t gameState;
uint_least64_t board[TOTAL_BOARD_SIZE];
gameState.repetitionTable = (struct zobristTable){NULL};
initMagicTable();
while(true) {
char *inputPtr = input;
if(fgets(input, sizeof input, stdin) == NULL) {
perror("Error: reading stdin: ");
continue;
}
if(customStrCmp(inputPtr, "uci")) {
printf(
"id name NoNameChessEngine\n"
"id author MrGeorgen\n"
"uciok\n"
);
continue;
}
if(customStrCmp(inputPtr, "isready")) {
printf("readyok\n");
continue;
}
if(customStrCmp(inputPtr, "position")) {
if(gameState.repetitionTable.arr == NULL) {
fprintf(stderr, "Error: required uicnewgame before a position command\n");
continue;
}
if(customStrCmp(inputPtr, "startpos")) {
gameState = parseFen(board, START_FEN);
continue;
}
if(customStrCmp(inputPtr, "fen")) {
gameState = parseFen(board, inputPtr);
continue;
}
}
if(customStrCmp(inputPtr, "quit")) {
return 0;
}
if(customStrCmp(inputPtr, "go")) {
while(*inputPtr != '\0') {
//if(customStrCmp(inputPtr, "wtime"))
}
continue;
}
if(customStrCmp(inputPtr, "stop")) {
searchShouldStop = true;
continue;
}
}
}

View File

@ -21,14 +21,15 @@ static uint_least64_t perft(const struct gameState_t gameState, const uint_least
for(uint_least8_t i = 0; i < movesLength; ++i) { for(uint_least8_t i = 0; i < movesLength; ++i) {
const struct gameState_t newGameState = makeMove(gameState, moves[i]); const struct gameState_t newGameState = makeMove(gameState, moves[i]);
nodes += perft(newGameState, depth - 1); nodes += perft(newGameState, depth - 1);
undoMove(gameState.board, moves[i], gameState.color); undoMove(newGameState, moves[i], gameState.color);
} }
return nodes; return nodes;
} }
static void test(struct perf_t perf, const uint_least8_t i) { static void test(struct perf_t perf, const uint_least8_t i) {
uint_least64_t board[BITBOARD_LENGTH]; uint_least64_t board[BITBOARD_LENGTH];
const struct gameState_t gameState = newGameState(board, perf.FEN); struct zobristTableElement repetitionTableStore[REPETETION_TABLE_LENGTH];
const struct gameState_t gameState = newGameState(board, repetitionTableStore, perf.FEN);
const uint_least64_t nodes = perft(gameState, perf.depth); const uint_least64_t nodes = perft(gameState, perf.depth);
if(perf.nodes != nodes) { if(perf.nodes != nodes) {
printf("Test %" PRIuLEAST16 " failed: FEN: %s, depth: %" PRIuLEAST8 " calculated nodes: %" PRIuLEAST64 printf("Test %" PRIuLEAST16 " failed: FEN: %s, depth: %" PRIuLEAST8 " calculated nodes: %" PRIuLEAST64
@ -38,7 +39,7 @@ static void test(struct perf_t perf, const uint_least8_t i) {
for(uint_least8_t j = 0; j < movesLength; ++j) { for(uint_least8_t j = 0; j < movesLength; ++j) {
const struct gameState_t newGameState = makeMove(gameState, moves[j]); const struct gameState_t newGameState = makeMove(gameState, moves[j]);
const uint_least64_t nodes = perft(newGameState, perf.depth - 1); const uint_least64_t nodes = perft(newGameState, perf.depth - 1);
undoMove(gameState.board, moves[j], gameState.color); undoMove(newGameState, moves[j], gameState.color);
{ {
printMove(moves[j]); printMove(moves[j]);
printf(" %" PRIuLEAST64 "\n", nodes); printf(" %" PRIuLEAST64 "\n", nodes);