Compare commits
18 Commits
69790345c5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4458fb6491 | |||
| 924e7d5eed | |||
| 6ccd2a4efe | |||
| 6e61a14cdb | |||
| 2c3d1bc021 | |||
| 6c5ef0586f | |||
| db958dcac1 | |||
| 4d2a86c7b7 | |||
| 5209dfec92 | |||
| a649db77ef | |||
| 1ab928cc6f | |||
| 6178e7c963 | |||
| 2d49f86517 | |||
| 7900546964 | |||
| f0f12be88d | |||
| 1d5f0b7d75 | |||
| 21de797d66 | |||
| 84fad9abb8 |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,6 +1,6 @@
|
||||
/*
|
||||
!/src
|
||||
/include/chess/generated/*
|
||||
/src/generated
|
||||
!/CMakeLists.txt
|
||||
!/README.md
|
||||
!/LICENSE
|
||||
@ -8,5 +8,6 @@
|
||||
!/lib
|
||||
!.gitkeep
|
||||
!/include
|
||||
/include/chess/generated/*
|
||||
!/.gitignore
|
||||
!/test
|
||||
|
||||
@ -1,29 +1,36 @@
|
||||
cmake_minimum_required(VERSION 3.18)
|
||||
project(chess C)
|
||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||
set(CMAKE_C_STANDARD 99)
|
||||
include_directories(include)
|
||||
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
|
||||
src/common/print.c)
|
||||
add_executable(genMoveConsts src/generateCode/moveConsts.c ${CODE_GEN_LIB_SOURCES})
|
||||
add_custom_command(
|
||||
OUTPUT moveConsts.h # Output file from code generation
|
||||
COMMAND genMoveConsts > include/chess/generated/moveConsts.h
|
||||
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)
|
||||
pkg_check_modules(GTK4 REQUIRED IMPORTED_TARGET gtk4)
|
||||
pkg_check_modules(LIBRSVG REQUIRED IMPORTED_TARGET librsvg-2.0)
|
||||
|
||||
add_executable(findMagicNumber src/findMagicNumber.c ${LIB_SOURCES})
|
||||
set_property(TARGET findMagicNumber PROPERTY C_STANDARD 99)
|
||||
add_dependencies(findMagicNumber generateCode)
|
||||
|
||||
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)
|
||||
add_dependencies(${PROJECT_NAME} generateCode)
|
||||
|
||||
@ -39,11 +46,9 @@ if(CMAKE_BUILD_TYPE MATCHES "Release")
|
||||
endif()
|
||||
|
||||
add_executable(chessNoComputer ${SOURCES} ${LIB_SOURCES})
|
||||
set_property(TARGET chessNoComputer PROPERTY C_STANDARD 99)
|
||||
target_link_libraries(chessNoComputer PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG)
|
||||
add_dependencies(chessNoComputer generateCode)
|
||||
target_compile_definitions(chessNoComputer PUBLIC NO_COMPUTER)
|
||||
|
||||
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)
|
||||
|
||||
82
README.md
82
README.md
@ -1,8 +1,80 @@
|
||||
# chess
|
||||
<!-- LTeX: language=en-US -->
|
||||
# Chess
|
||||
|
||||
A chess engine
|
||||
A chess engine with focus on fast move generation.
|
||||
|
||||
# sources
|
||||
## Building
|
||||
|
||||
https://www.chessprogramming.org/Magic_Bitboards
|
||||
https://www.chessprogramming.org/Looking_for_Magics
|
||||
The following dependencies are used for the GUI:
|
||||
- gtk 4
|
||||
- librsvg (for loading the piece images)
|
||||
|
||||
The engine can be built with cmake.
|
||||
|
||||
```
|
||||
cmake .
|
||||
make
|
||||
```
|
||||
|
||||
If you do not use make, replace it with ninja for example. The build script contains multiple
|
||||
targets:
|
||||
|
||||
- `chess`: This is the main target. It has a GUI and the user plays as white. The engine responds with
|
||||
a move for black.
|
||||
- `chessNoComputer`: The user can play both sides in a GUI. Mainly added for testing the move
|
||||
generator.
|
||||
- `findMagicNumber`: finds Magic Numbers for the magic bitboard.
|
||||
- `moveGenTest`: tests the move generator,
|
||||
- `genMoveConsts`: produces constants to generate moves faster.
|
||||
|
||||
## Testing
|
||||
|
||||
The target moveGenTest with its source in the test directory is a [perft](https://www.chessprogramming.org/Perft).
|
||||
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.
|
||||
|
||||
@ -2,9 +2,9 @@
|
||||
#include <stdint.h>
|
||||
#include <chess/types.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);
|
||||
#define REPETETION_TABLE_LENGTH 1024
|
||||
#define START_FEN "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"
|
||||
|
||||
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);
|
||||
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 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 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
6
include/chess/bitset.h
Normal 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);
|
||||
@ -17,6 +17,7 @@ enum spezialMoves {
|
||||
struct move_t {
|
||||
uint_least8_t src, dst, spezialMove;
|
||||
uint_least8_t srcPiece, dstPiece, capturedPiece;
|
||||
uint_least64_t hash;
|
||||
};
|
||||
|
||||
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,
|
||||
struct move_t *moves, bool promotion);
|
||||
|
||||
void undoMove(uint_least64_t *board, struct move_t move, bool color);
|
||||
struct gameState_t makeMove(struct gameState_t gameState, struct move_t move);
|
||||
struct gameState_t computerMove(struct gameState_t gameState);
|
||||
void undoMove(struct gameState_t gameState, struct move_t move, bool color);
|
||||
struct gameState_t makeMove(struct gameState_t gameState, const struct move_t move);
|
||||
void initMagicTable();
|
||||
bool kingInCheck(const uint_least64_t *board, const bool color);
|
||||
uint_least8_t getBaseRankI(bool color);
|
||||
uint_least8_t getBaseRank(bool color);
|
||||
|
||||
#endif
|
||||
|
||||
@ -1,27 +1,34 @@
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <chess/move.h>
|
||||
|
||||
#define fprintArray(file, printer, arr) \
|
||||
#define fprintArrayCustomLength(file, printer, arr, length) \
|
||||
do { \
|
||||
fprintf(file, "{"); \
|
||||
for(size_t i = 0; i < LENGTH(arr); ++i) { \
|
||||
for(size_t i = 0; i < length; ++i) { \
|
||||
printer(file, arr[i]); \
|
||||
if (i < LENGTH(arr) - 1) { \
|
||||
if (i < length - 1) { \
|
||||
fprintf(file, ", "); \
|
||||
} \
|
||||
} \
|
||||
fprintf(file, " }"); \
|
||||
} 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 { \
|
||||
fprintf(file, "%s[%zu] = ", declaration, LENGTH(arr)); \
|
||||
fprintArray(file, printer, arr); \
|
||||
fprintf(file, "%s[%zu] = ", declaration, length); \
|
||||
fprintArrayCustomLength(file, printer, arr, length); \
|
||||
fprintf(file, ";\n"); \
|
||||
} 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 printerull(FILE *file, unsigned long long num);
|
||||
void printPieceMask(uint_least64_t mask);
|
||||
void fieldToString(uint_least8_t field, char *output);
|
||||
void printMove(const struct move_t move);
|
||||
void printMoveln(const struct move_t move);
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define BOARD_SIZE 8
|
||||
#define TOTAL_BOARD_SIZE (BOARD_SIZE * BOARD_SIZE)
|
||||
@ -35,6 +36,16 @@ enum directions {
|
||||
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 {
|
||||
uint_least8_t length;
|
||||
uint_least8_t dst[8];
|
||||
@ -53,6 +64,8 @@ struct castle_t {
|
||||
|
||||
struct gameState_t {
|
||||
uint_least64_t *board;
|
||||
uint_least64_t zobrist;
|
||||
struct zobristTable repetitionTable;
|
||||
bool color; // color to move
|
||||
struct castle_t canCastle[2];
|
||||
|
||||
|
||||
18
include/chess/util.h
Normal file
18
include/chess/util.h
Normal 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
|
||||
16
include/chess/zobristConsts.h
Normal file
16
include/chess/zobristConsts.h
Normal 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
|
||||
100
src/chess/evaluate.c
Normal file
100
src/chess/evaluate.c
Normal file
@ -0,0 +1,100 @@
|
||||
#include "chess/move.h"
|
||||
#include "chess/types.h"
|
||||
#include <stdint.h>
|
||||
#include <chess/bitboard.h>
|
||||
#include <chess/print.h>
|
||||
|
||||
// These min and max values will not overflow, when negated
|
||||
#define INT_16_SAFE_MIN (INT_LEAST16_MIN + 1)
|
||||
#define INT_16_SAFE_MAX (INT_LEAST16_MAX - 1)
|
||||
|
||||
#ifdef __GNUC__ // Check if using GCC or compatible compiler
|
||||
static int_least16_t countOnes(const uint_least64_t num) {
|
||||
return __builtin_popcountll(num); // Use GCC built-in function if available
|
||||
}
|
||||
#elif defined(_MSC_VER) // Check if using Microsoft Visual C++
|
||||
#include <intrin.h>
|
||||
static int_least16_t countOnes(const uint_least64_t num) {
|
||||
return __popcnt64(num); // Use MSVC intrinsic if available
|
||||
}
|
||||
#else
|
||||
static int_least16_t countOnes(const uint_least64_t num) {
|
||||
int_least16_t count = 0;
|
||||
while(num) {
|
||||
count += num & 1;
|
||||
num >>= 1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int_least16_t evaluate(const struct gameState_t gameState) {
|
||||
if(gameState.halfMoveCounter == 50) return 0;
|
||||
int_least16_t value = 0;
|
||||
const uint_least64_t *board = gameState.board;
|
||||
int_least16_t pieceValues[PIECES_LENGTH];
|
||||
pieceValues[PAWN] = 100;
|
||||
pieceValues[KNIGHT] = 300;
|
||||
pieceValues[BISHOP] = 310;
|
||||
pieceValues[QUEEN] = 900;
|
||||
pieceValues[ROOK] = 500;
|
||||
|
||||
for(uint_least8_t pieceType = QUEEN; pieceType < PIECES_LENGTH; ++pieceType) {
|
||||
for(uint_least8_t color = BLACK; color <= WHITE; ++color) {
|
||||
const struct piece_t piece = {pieceType, color};
|
||||
const int_least16_t sign = color == gameState.color ? 1 : -1;
|
||||
value += sign * countOnes(bitboardGetMask(board, piece)) * pieceValues[pieceType];
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static int_least16_t alphaBeta(const struct gameState_t gameState, int_fast16_t alpha,
|
||||
int_least16_t beta, uint_least8_t depth) {
|
||||
if(depth == 0) return evaluate(gameState);
|
||||
struct move_t moves[MAX_VALID_MOVES];
|
||||
uint_least8_t movesLength = validMoves(gameState, moves);
|
||||
if(movesLength == 0) {
|
||||
if(kingInCheck(gameState.board, gameState.color)) {
|
||||
return INT_16_SAFE_MIN + 1; // checkmate
|
||||
}
|
||||
return 0; // stalemate
|
||||
}
|
||||
for(uint_least8_t i = 0; i < movesLength; ++i) {
|
||||
const struct move_t move = moves[i];
|
||||
const struct gameState_t newGameState = makeMove(gameState, move);
|
||||
const int_least16_t score = -alphaBeta(newGameState, -beta, -alpha, depth - 1);
|
||||
undoMove(newGameState, move, gameState.color);
|
||||
if(score >= beta) {
|
||||
return beta; // beta-cutoff
|
||||
}
|
||||
if(score > alpha) {
|
||||
alpha = score; // alpha acts like max in MiniMax
|
||||
}
|
||||
}
|
||||
return alpha;
|
||||
}
|
||||
|
||||
static struct move_t searchRoot(const struct gameState_t gameState, uint_least8_t depth) {
|
||||
struct move_t moves[MAX_VALID_MOVES];
|
||||
uint_least8_t movesLength = validMoves(gameState, moves);
|
||||
int_least16_t alpha = INT_16_SAFE_MIN;
|
||||
int_least16_t beta = INT_16_SAFE_MAX;
|
||||
struct move_t selectedMove;
|
||||
for(uint_least8_t i = 0; i < movesLength; ++i) {
|
||||
const struct move_t move = moves[i];
|
||||
const struct gameState_t newGameState = makeMove(gameState, move);
|
||||
const int_least16_t score = -alphaBeta(newGameState, -beta, -alpha, depth - 1);
|
||||
undoMove(newGameState, move, gameState.color);
|
||||
if(score > alpha) {
|
||||
alpha = score; // alpha acts like max in MiniMax
|
||||
selectedMove = move;
|
||||
}
|
||||
}
|
||||
return selectedMove;
|
||||
}
|
||||
|
||||
struct move_t bestMove(const struct gameState_t gameState) {
|
||||
const uint_least8_t depth = 6;
|
||||
return searchRoot(gameState, depth);
|
||||
}
|
||||
8
src/chess/evaluate.h
Normal file
8
src/chess/evaluate.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef CHESS_EVALUATE_H
|
||||
#define CHESS_EVALUATE_H
|
||||
|
||||
#include <chess/types.h>
|
||||
|
||||
struct move_t bestMove(const struct gameState_t gameState);
|
||||
|
||||
#endif
|
||||
@ -1,3 +1,3 @@
|
||||
#include <chess/types.h>
|
||||
static struct magic_t rookMagic[64] = {{ 0, 126u, 585504314899303506u, 58}, { 0, 144680345676153468u, 9241404028087189504u, 53}, { 0, 289360691352306810u, 612524733837086720u, 53}, { 0, 578721382704613494u, 72092812853645312u, 53}, { 0, 1157442765409226862u, 1188953050539099136u, 53}, { 0, 2314885530818453598u, 72058693583372296u, 53}, { 0, 4629771061636907070u, 1188970094051459328u, 53}, { 0, 126u, 585504314899303506u, 58}, { 0, 32256u, 4573969545971713u, 58}, { 0, 144680345676184578u, 9223442680478961664u, 54}, { 0, 289360691352336900u, 2392606057169024u, 54}, { 0, 578721382704641544u, 140806208356480u, 54}, { 0, 1157442765409250832u, 1153062791918127104u, 54}, { 0, 2314885530818469408u, 140754676613632u, 54}, { 0, 4629771061636906560u, 14411660094863311104u, 54}, { 0, 32256u, 4573969545971713u, 58}, { 0, 8257536u, 4035243133187982848u, 58}, { 0, 144680345684148738u, 9223442680478961664u, 54}, { 0, 289360691360039940u, 71468792942848u, 54}, { 0, 578721382711822344u, 143486535927808u, 54}, { 0, 1157442765415387152u, 1153062791918127104u, 54}, { 0, 2314885530822516768u, 141287311278592u, 54}, { 0, 4629771061636776000u, 4398063813136u, 54}, { 0, 8257536u, 4035243133187982848u, 58}, { 0, 2113929216u, 9799832806609265699u, 58}, { 0, 144680347722973698u, 35186523766912u, 54}, { 0, 289360693332018180u, 2324376172109952u, 54}, { 0, 578721384550107144u, 8798241554560u, 54}, { 0, 1157442766986285072u, 8798248895488u, 54}, { 0, 2314885531858640928u, 4400202383872u, 54}, { 0, 4629771061603352640u, 9223513925395025944u, 54}, { 0, 2113929216u, 9799832806609265699u, 58}, { 0, 541165879296u, 8623489186u, 58}, { 0, 144680869662163458u, 211174972989440u, 54}, { 0, 289361198158447620u, 422349979521024u, 54}, { 0, 578721855151015944u, 8798248898560u, 54}, { 0, 1157443169136152592u, 8798248895488u, 54}, { 0, 2314885797106425888u, 176196872635408u, 54}, { 0, 4629771053046972480u, 864693334524825672u, 54}, { 0, 541165879296u, 8623489186u, 58}, { 0, 138538465099776u, 1152923704242602112u, 58}, { 0, 144814486094742018u, 87961467109376u, 54}, { 0, 289490433724384260u, 35184640557184u, 54}, { 0, 578842328983668744u, 17592320295040u, 54}, { 0, 1157546119502237712u, 1152930300767010944u, 54}, { 0, 2314953700539375648u, 2199090397312u, 54}, { 0, 4629768862613651520u, 71477114437768u, 54}, { 0, 138538465099776u, 1152923704242602112u, 58}, { 0, 35465847065542656u, 145139880169537u, 58}, { 0, 179020292834853378u, 35186523766912u, 54}, { 0, 322574738604164100u, 71468792942848u, 54}, { 0, 609683630142785544u, 2305880667756036608u, 54}, { 0, 1183901413220028432u, 4400194519168u, 54}, { 0, 2332336979374514208u, 3458905259899093120u, 54}, { 0, 4629208111683485760u, 123149731628032u, 54}, { 0, 35465847065542656u, 145139880169537u, 58}, { 0, 9079256848778919936u, 145139880169537u, 58}, { 0, 8935706818303361538u, 22548582572802u, 53}, { 0, 8792156787827803140u, 9223443505379549185u, 53}, { 0, 8505056726876686344u, 281492694499337u, 53}, { 0, 7930856604974452752u, 3298862563377u, 53}, { 0, 6782456361169985568u, 576478370259304706u, 52}, { 0, 4485655873561051200u, 17715716818948u, 53}, { 0, 9079256848778919936u, 145139880169537u, 58} };
|
||||
static struct magic_t bishopMagic[64] = {{ 0, 18049651735527936u, 450387485566996608u, 58}, { 0, 70506452091904u, 292736183996786692u, 59}, { 0, 275415828992u, 15764799564551168u, 59}, { 0, 1075975168u, 633872748382213u, 59}, { 0, 38021120u, 565217696169988u, 59}, { 0, 8657588224u, 571754770663745u, 59}, { 0, 2216338399232u, 564393097077764u, 59}, { 0, 567382630219776u, 4647745618985488384u, 58}, { 0, 4620710844295151618u, 151878701417476u, 59}, { 0, 18049651735527428u, 9605338177241346u, 59}, { 0, 70506452221962u, 9223495332494639169u, 59}, { 0, 275449643028u, 9223446873539363073u, 59}, { 0, 9733406760u, 572125077307650u, 59}, { 0, 2216342585424u, 2305915585839759872u, 59}, { 0, 567382630203424u, 576608090351732752u, 59}, { 0, 145249953336262720u, 154635866112u, 59}, { 0, 2310355422147510788u, 18084841411575936u, 59}, { 0, 4620710844295021576u, 4516828160236544u, 59}, { 0, 18049651768822288u, 9225629402954698769u, 57}, { 0, 70515108615202u, 2251975928381472u, 57}, { 0, 2491752130628u, 4611967513004083360u, 57}, { 0, 567383701868552u, 281550407600400u, 57}, { 0, 145249953332076560u, 6920908969368159235u, 59}, { 0, 290499906664153120u, 703829253328896u, 59}, { 0, 1155177711057110024u, 1143497474336784u, 59}, { 0, 2310355422114220048u, 317758928062976u, 59}, { 0, 4620710852818505760u, 1155175507755663872u, 57}, { 0, 18051867805491776u, 5764897794523414784u, 55}, { 0, 637888545440770u, 4785350622855232u, 55}, { 0, 145250227678349316u, 570096847160448u, 57}, { 0, 290499905592496136u, 9223519374650978432u, 59}, { 0, 580999811184992272u, 1153062792136247296u, 59}, { 0, 577588851267340304u, 4521260600263744u, 59}, { 0, 1155177702534680608u, 3124159228940288u, 59}, { 0, 2310357604126171200u, 74775381671976u, 57}, { 0, 4621278158205894656u, 4647717016617615488u, 55}, { 0, 163299467632837120u, 9277415507529629952u, 55}, { 0, 290570138238321666u, 4503917455229184u, 57}, { 0, 580999536840804356u, 36600586291970176u, 59}, { 0, 1161999073681608712u, 2261146400784640u, 59}, { 0, 288793334762704928u, 1127136992038912u, 59}, { 0, 577586669525409856u, 1695455559819264u, 59}, { 0, 1155736297594175488u, 13880314514376704u, 57}, { 0, 2455587783297728512u, 144397496285665280u, 57}, { 0, 4911175566587199488u, 4612011478231285824u, 57}, { 0, 598979094172140032u, 9042384708960320u, 57}, { 0, 1161928841569502210u, 18876832271040768u, 59}, { 0, 2323857683139004420u, 1127275689020928u, 59}, { 0, 144117404414255168u, 72576638388224u, 59}, { 0, 288234808828510208u, 720646618495844352u, 59}, { 0, 720587004756099072u, 146371669538381824u, 59}, { 0, 1441174018093744128u, 11538239838684184576u, 59}, { 0, 2882348034073559040u, 9223975260183924736u, 59}, { 0, 5764695518391435264u, 4899987047395164192u, 59}, { 0, 2305878262439739904u, 3461051790072611360u, 59}, { 0, 4611756524879479810u, 72624959419465760u, 59}, { 0, 567382630219776u, 4647745618985488384u, 58}, { 0, 1134765260406784u, 2312598961229660688u, 59}, { 0, 2832480465846272u, 10376295759300608386u, 59}, { 0, 5667157807464448u, 1152930369692107270u, 59}, { 0, 11333774449049600u, 1152930369692107270u, 59}, { 0, 22526811443298304u, 36421940281888u, 59}, { 0, 9024825867763712u, 151878701417476u, 59}, { 0, 18049651735527936u, 450387485566996608u, 58} };
|
||||
static struct magic_t rookMagic[64] = {{ 0, 282578800148862u, 2341871894348709888u, 52}, { 0, 565157600297596u, 9961962444465111104u, 53}, { 0, 1130315200595066u, 144133330555126272u, 53}, { 0, 2260630401190006u, 9259418428207792132u, 53}, { 0, 4521260802379886u, 4647717031650001025u, 53}, { 0, 9042521604759646u, 216191473946330112u, 53}, { 0, 18085043209519166u, 2341942724749427200u, 53}, { 0, 36170086419038334u, 4719772959250661632u, 52}, { 0, 282578800180736u, 4775082277841412224u, 53}, { 0, 565157600328704u, 76631837289484288u, 54}, { 0, 1130315200625152u, 307089306986487809u, 54}, { 0, 2260630401218048u, 9288816033529864u, 54}, { 0, 4521260802403840u, 141287378387968u, 54}, { 0, 9042521604775424u, 9570183869903124u, 54}, { 0, 18085043209518592u, 7036891614413312u, 54}, { 0, 36170086419037696u, 1153484593081418770u, 53}, { 0, 282578808340736u, 324259448052785152u, 53}, { 0, 565157608292864u, 5908793355000619010u, 54}, { 0, 1130315208328192u, 76632662458041361u, 54}, { 0, 2260630408398848u, 630514944024117264u, 54}, { 0, 4521260808540160u, 141287378387968u, 54}, { 0, 9042521608822784u, 18718635740693504u, 54}, { 0, 18085043209388032u, 5066825540829440u, 54}, { 0, 36170086418907136u, 2199095673988u, 53}, { 0, 282580897300736u, 2326742528381978032u, 53}, { 0, 565159647117824u, 288318340303167488u, 54}, { 0, 1130317180306432u, 436857960485093408u, 54}, { 0, 2260632246683648u, 4613937854749343874u, 54}, { 0, 4521262379438080u, 1173196501178909696u, 54}, { 0, 9042522644946944u, 4632519566898102400u, 54}, { 0, 18085043175964672u, 8813273026561u, 54}, { 0, 36170086385483776u, 4683747469346218057u, 53}, { 0, 283115671060736u, 324259448052785152u, 53}, { 0, 565681586307584u, 11712879804977004544u, 54}, { 0, 1130822006735872u, 4611826893363089413u, 54}, { 0, 2261102847592448u, 4611826790283874304u, 54}, { 0, 4521664529305600u, 1173196501178909696u, 54}, { 0, 9042787892731904u, 595038117954585216u, 54}, { 0, 18085034619584512u, 18016597549580292u, 54}, { 0, 36170077829103616u, 2306687435234357382u, 53}, { 0, 420017753620736u, 2902889407696764937u, 53}, { 0, 699298018886144u, 45038538897113088u, 54}, { 0, 1260057572672512u, 40532551399383040u, 54}, { 0, 2381576680245248u, 5773898465009008672u, 54}, { 0, 4624614895390720u, 1126176932759552u, 54}, { 0, 9110691325681664u, 4612248977037885568u, 54}, { 0, 18082844186263552u, 5119360519897218u, 54}, { 0, 36167887395782656u, 18295875671621634u, 53}, { 0, 35466950888980736u, 1477468785259610624u, 53}, { 0, 34905104758997504u, 76631633278533760u, 54}, { 0, 34344362452452352u, 436857960485093408u, 54}, { 0, 33222877839362048u, 166635419663892608u, 54}, { 0, 30979908613181440u, 1128648753545344u, 54}, { 0, 26493970160820224u, 595038117954585216u, 54}, { 0, 17522093256097792u, 2252969152611328u, 54}, { 0, 35607136465616896u, 5764607800395235840u, 53}, { 0, 9079539427579068672u, 2378464662405522454u, 52}, { 0, 8935706818303361536u, 2378464662405522454u, 53}, { 0, 8792156787827803136u, 167073095052427521u, 53}, { 0, 8505056726876686336u, 38843564238571554u, 53}, { 0, 7930856604974452736u, 324540682508174387u, 53}, { 0, 6782456361169985536u, 9288708625006593u, 53}, { 0, 4485655873561051136u, 1153484456724793346u, 53}, { 0, 9115426935197958144u, 1153484593081418770u, 52} };
|
||||
static struct magic_t bishopMagic[64] = {{ 0, 18049651735527936u, 19141544795963648u, 58}, { 0, 70506452091904u, 4505253206687752u, 59}, { 0, 275415828992u, 90354593216594048u, 59}, { 0, 1075975168u, 4613973694639800321u, 59}, { 0, 38021120u, 614189463018562176u, 59}, { 0, 8657588224u, 153694142109712388u, 59}, { 0, 2216338399232u, 4611976573233664521u, 59}, { 0, 567382630219776u, 374082743755080256u, 58}, { 0, 9024825867763712u, 18032677916247168u, 59}, { 0, 18049651735527424u, 4611721207162144849u, 59}, { 0, 70506452221952u, 4776012087296u, 59}, { 0, 275449643008u, 9295451711487353344u, 59}, { 0, 9733406720u, 303551645491200u, 59}, { 0, 2216342585344u, 148619346592208512u, 59}, { 0, 567382630203392u, 10953321663308055553u, 59}, { 0, 1134765260406784u, 2306142356220944904u, 59}, { 0, 4512412933816832u, 594475168263569698u, 59}, { 0, 9024825867633664u, 157696373151891552u, 59}, { 0, 18049651768822272u, 1182204866511054880u, 57}, { 0, 70515108615168u, 2256207020834816u, 57}, { 0, 2491752130560u, 704254387965952u, 57}, { 0, 567383701868544u, 562989686202432u, 57}, { 0, 1134765256220672u, 13907256408426942720u, 59}, { 0, 2269530512441344u, 19298778481721888u, 59}, { 0, 2256206450263040u, 4592935417696336u, 59}, { 0, 4512412900526080u, 4919074312566210768u, 59}, { 0, 9024834391117824u, 11529294245282317316u, 57}, { 0, 18051867805491712u, 40536794697302024u, 55}, { 0, 637888545440768u, 2378184277318730784u, 55}, { 0, 1135039602493440u, 2252075798954116u, 57}, { 0, 2269529440784384u, 77128610930233602u, 59}, { 0, 4539058881568768u, 1126449664787468u, 59}, { 0, 1128098963916800u, 4508204168188160u, 59}, { 0, 2256197927833600u, 4632286708805830664u, 59}, { 0, 4514594912477184u, 1297072495799042080u, 57}, { 0, 9592139778506752u, 79199197071873u, 55}, { 0, 19184279556981248u, 1441257709826736384u, 55}, { 0, 2339762086609920u, 6494191214571618368u, 57}, { 0, 4538784537380864u, 3820179484655618048u, 59}, { 0, 9077569074761728u, 155374497589920282u, 59}, { 0, 562958610993152u, 216318261380595712u, 59}, { 0, 1125917221986304u, 9227947141782112256u, 59}, { 0, 2814792987328512u, 9224080139556487425u, 57}, { 0, 5629586008178688u, 1312954725173508u, 57}, { 0, 11259172008099840u, 14195348636979822848u, 57}, { 0, 22518341868716544u, 1243277310774345984u, 57}, { 0, 9007336962655232u, 1345948328394816u, 59}, { 0, 18014673925310464u, 1243277310774345984u, 59}, { 0, 2216338399232u, 4611976573233664521u, 59}, { 0, 4432676798464u, 2397502563287040u, 59}, { 0, 11064376819712u, 36645624251285512u, 59}, { 0, 22137335185408u, 11822019940893327488u, 59}, { 0, 44272556441600u, 1805943519328735744u, 59}, { 0, 87995357200384u, 153694142109712388u, 59}, { 0, 35253226045952u, 4632096321797488644u, 59}, { 0, 70506452091904u, 4505253206687752u, 59}, { 0, 567382630219776u, 374082743755080256u, 58}, { 0, 1134765260406784u, 2306142356220944904u, 59}, { 0, 2832480465846272u, 10953321663308055553u, 59}, { 0, 5667157807464448u, 16141482087675924608u, 59}, { 0, 11333774449049600u, 5260489826817409556u, 59}, { 0, 22526811443298304u, 576531138303525376u, 59}, { 0, 9024825867763712u, 18032677916247168u, 59}, { 0, 18049651735527936u, 19141544795963648u, 58} };
|
||||
|
||||
@ -9,11 +9,8 @@
|
||||
#include <chess/types.h>
|
||||
#include <chess/move.h>
|
||||
#include <chess/bitboard.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)
|
||||
#include "evaluate.h"
|
||||
#include <chess/util.h>
|
||||
|
||||
struct drawData_t {
|
||||
RsvgHandle **piecesSvg;
|
||||
@ -29,33 +26,47 @@ struct drawData_t {
|
||||
};
|
||||
|
||||
const uint_least8_t PROMOTION_PIECES[] = {QUEEN, KNIGHT, BISHOP, ROOK};
|
||||
static bool turn = WHITE;
|
||||
|
||||
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) {
|
||||
uint_least8_t file = field % BOARD_SIZE;
|
||||
uint_least8_t rank = field / BOARD_SIZE;
|
||||
double xOffset = drawData->xOffset;
|
||||
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 drawPiece(struct drawData_t *ctx, cairo_t *cr, uint_least8_t field, struct piece_t piece) {
|
||||
const RsvgRectangle rect = {ctx->xOffset + getFile(field)* ctx->fieldSize,
|
||||
ctx->yOffset + getRank(field) * ctx->fieldSize, ctx->fieldSize, ctx->fieldSize};
|
||||
rsvg_handle_render_document(ctx->piecesSvg[pieceToSvgI(piece)], cr, &rect, NULL);
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
gameState = makeMove(gameState, move);
|
||||
#ifdef NO_COMPUTER
|
||||
turn = !turn;
|
||||
#else
|
||||
gameState = computerMove(gameState);
|
||||
#ifndef NO_COMPUTER
|
||||
move = bestMove(gameState);
|
||||
gameState = makeMove(gameState, move);
|
||||
#endif
|
||||
return gameState;
|
||||
}
|
||||
|
||||
static uint_least8_t promotionUiPos(uint_least8_t dst) {
|
||||
return dst >= 1 ? dst - 1 : 0;
|
||||
static uint_least8_t promotionUiPos(uint_least8_t dst, bool color) {
|
||||
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) {
|
||||
@ -63,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 yOffset = (height - fieldSize * BOARD_SIZE) / 2;
|
||||
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;
|
||||
drawData->xOffset = xOffset;
|
||||
drawData->yOffset = yOffset;
|
||||
@ -71,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 rank = 0; rank < BOARD_SIZE; ++rank) {
|
||||
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);
|
||||
else cairo_set_source_rgb(cr, 0.9333, 0.9333, 0.8235);
|
||||
cairo_fill(cr);
|
||||
drawRetangle(cr, drawData, field, fieldSize);
|
||||
if(bitboardGetAllPieces(board, field)) {
|
||||
const struct piece_t piece = pieceAtField(board, rank * BOARD_SIZE + file);
|
||||
drawPiece(file, rank, piece);
|
||||
drawPiece(drawData, cr, field, piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -92,14 +103,12 @@ static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height,
|
||||
}
|
||||
}
|
||||
if(drawData->selectDst != NOT_SELECTED) {
|
||||
uint_least8_t uiPos = promotionUiPos(drawData->selectDst);
|
||||
if(uiPos + 3 >= BOARD_SIZE) uiPos = BOARD_SIZE - LENGTH(PROMOTION_PIECES);
|
||||
uint_least8_t uiPos = promotionUiPos(drawData->selectDst, gameState->color);
|
||||
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
|
||||
cairo_rectangle(cr, xOffset + uiPos * fieldSize, yOffset, 4 * fieldSize, fieldSize);
|
||||
cairo_fill(cr);
|
||||
drawRetangle(cr, drawData, uiPos, 4 * fieldSize);
|
||||
for(uint_least8_t i = 0; i < LENGTH(PROMOTION_PIECES); ++i) {
|
||||
const struct piece_t piece = {PROMOTION_PIECES[i], true};
|
||||
drawPiece(uiPos + i, 0, piece);
|
||||
const struct piece_t piece = {PROMOTION_PIECES[i], gameState->color};
|
||||
drawPiece(drawData, cr, uiPos + i, piece);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -116,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 rank = adjustedY / fieldSize;
|
||||
const uint_least8_t field = file + rank * BOARD_SIZE;
|
||||
const bool onPromotionField = rank == getBaseRank(!gameState->color);
|
||||
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)) {
|
||||
// deaktivated piece by clicking on it again
|
||||
// deactivated piece by clicking on it again
|
||||
if(field == drawData->clickedPiece) {
|
||||
drawData->clickedPiece = NOT_SELECTED;
|
||||
drawData->movesLength = 0;
|
||||
@ -139,8 +149,8 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y,
|
||||
}
|
||||
}
|
||||
if(isValidDst) {
|
||||
const struct piece_t piece = pieceAtField(board, field);
|
||||
if(piece.type == PAWN && field < BOARD_SIZE) { // promotion
|
||||
const struct piece_t piece = pieceAtField(board, drawData->clickedPiece);
|
||||
if(piece.type == PAWN && onPromotionField) { // promotion
|
||||
drawData->selectDst = field;
|
||||
}
|
||||
else {
|
||||
@ -160,12 +170,13 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y,
|
||||
drawData->movesLength = 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(rank != 0) return;
|
||||
const uint_least8_t uiPos = promotionUiPos(drawData->selectDst);
|
||||
const uint_least8_t dstPiece = PROMOTION_PIECES[file - uiPos];
|
||||
else if(onPromotionField) {
|
||||
const uint_least8_t uiPos = promotionUiPos(drawData->selectDst, gameState->color);
|
||||
const uint_least8_t i = field - 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 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);
|
||||
drawData->selectDst = NOT_SELECTED;
|
||||
drawData->clickedPiece = NOT_SELECTED;
|
||||
@ -175,10 +186,11 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y,
|
||||
|
||||
static void app_activate(GApplication *app, gpointer data) {
|
||||
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 struct zobristTableElement repetitionTableStore[REPETETION_TABLE_LENGTH];
|
||||
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 drawData_t drawData = {piecesSvg, &gameState, NOT_SELECTED, moves, 0, NOT_SELECTED};
|
||||
|
||||
216
src/chess/move.c
216
src/chess/move.c
@ -9,8 +9,14 @@
|
||||
#include <stdio.h>
|
||||
#include "magicNumber.h"
|
||||
#include <chess/magic.h>
|
||||
#include <chess/util.h>
|
||||
#include <chess/bitset.h>
|
||||
#include <chess/zobristConsts.h>
|
||||
#include <string.h>
|
||||
|
||||
#define trailingBits(num) __builtin_ctzll(num)
|
||||
#ifdef _MSC_VER
|
||||
#include <intrin.h>
|
||||
#endif
|
||||
|
||||
struct addMoveCtx_t {
|
||||
struct gameState_t gameState;
|
||||
@ -20,6 +26,23 @@ struct addMoveCtx_t {
|
||||
|
||||
const static uint_least8_t MAIN_DIRECTION[] = {NORTH, WEST, EAST, SOUTH};
|
||||
|
||||
static uint_least8_t trailingBits(uint_least64_t num) {
|
||||
#ifdef __GNUC__
|
||||
return __builtin_ctzll(num);
|
||||
#elif defined(_MSC_VER)
|
||||
unsigned long i;
|
||||
_BitScanForward(&i, num);
|
||||
return i;
|
||||
#else
|
||||
uint_least8_t count = 0;
|
||||
while ((num & 1) == 0) { // Keep shifting num right until the least significant bit is 1
|
||||
num >>= 1;
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
#endif
|
||||
}
|
||||
|
||||
static uint_least8_t getDirectionOffset(uint_least8_t field, uint_least8_t direction) {
|
||||
defineDirectionOffset;
|
||||
return DIRECTION_OFFSET[field * DIRECTION_LENGTH + direction];
|
||||
@ -37,8 +60,6 @@ static size_t getTotalMagicSize(const struct magic_t *magic) {
|
||||
return size;
|
||||
}
|
||||
|
||||
uint_least64_t *overflowPtr;
|
||||
|
||||
static void initMagicHelper(uint_least64_t *attackTable, struct magic_t *magic,
|
||||
const uint_least8_t *direction, const uint_least8_t directionLength) {
|
||||
for(uint_least8_t field = 0; field < TOTAL_BOARD_SIZE; ++field, ++magic) {
|
||||
@ -48,9 +69,6 @@ static void initMagicHelper(uint_least64_t *attackTable, struct magic_t *magic,
|
||||
do { // https://stackoverflow.com/questions/7277554/what-is-a-good-way-to-iterate-a-number-through-all-the-possible-values-of-a-mask
|
||||
const uint_least64_t attackMask = slidingMovementMask(direction, directionLength, field, pieceArangement);
|
||||
uint_least64_t *ptr = getMagicAttackPtr(pieceArangement, *magic);
|
||||
if(ptr >= overflowPtr) {
|
||||
printf("overflow\n");
|
||||
}
|
||||
*ptr = attackMask;
|
||||
pieceArangement = (pieceArangement - magic->mask) & magic->mask;
|
||||
} while(pieceArangement != 0);
|
||||
@ -61,7 +79,6 @@ void initMagicTable() {
|
||||
const size_t rookMagicSize = getTotalMagicSize(rookMagic);
|
||||
const size_t size = rookMagicSize + getTotalMagicSize(bishopMagic);
|
||||
uint_least64_t *attackTable = malloc(size * sizeof *attackTable);
|
||||
overflowPtr = attackTable + size;
|
||||
if(attackTable == NULL) {
|
||||
perror("failed to allocate memory: ");
|
||||
exit(1);
|
||||
@ -115,65 +132,140 @@ static bool inCheck(const uint_least64_t *board, const uint_least64_t kingMask,
|
||||
checkMagic(board, queen, rookMagic, kingMask) || checkMagic(board, queen, bishopMagic, kingMask);
|
||||
}
|
||||
|
||||
static uint_least8_t getCastleRankI(bool color) {
|
||||
bool kingInCheck(const uint_least64_t *board, const bool color) {
|
||||
const struct piece_t king = {KING, color};
|
||||
return inCheck(board, bitboardGetMask(board, king), color);
|
||||
}
|
||||
|
||||
uint_least8_t getBaseRank(bool color) {
|
||||
if(color == WHITE) {
|
||||
return (BOARD_SIZE - 1) * BOARD_SIZE;
|
||||
return BOARD_SIZE - 1;
|
||||
}
|
||||
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) {
|
||||
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 rookDst = getCastleRankI(color) + (spezialMove == SHORT_CASTLE ? 5 : 3);
|
||||
const uint_least8_t rookSrc = getBaseRankI(color) + (spezialMove == SHORT_CASTLE ? BOARD_SIZE - 1 : 0);
|
||||
const uint_least8_t rookDst = getBaseRankI(color) + (spezialMove == SHORT_CASTLE ? 5 : 3);
|
||||
const struct piece_t rook = {ROOK, color};
|
||||
bitboardSet(board, rook, undo ? rookSrc : rookDst);
|
||||
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 dstPiece = {move.dstPiece, color};
|
||||
bitboardSet(board, srcPiece, move.src);
|
||||
bitboardClear(board, dstPiece, move.dst);
|
||||
if(move.capturedPiece != NOT_SELECTED) {
|
||||
const struct piece_t piece = {move.capturedPiece, !color};
|
||||
bitboardSet(board, piece, move.dst);
|
||||
bitboardSet(board, piece, getCapturePos(move.src, move.dst, move.spezialMove));
|
||||
}
|
||||
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,
|
||||
uint_least8_t dst, uint_least8_t spezialMove) {
|
||||
uint_least64_t *board = ctx.gameState.board;
|
||||
uint_least8_t *movesLength = ctx.movesLength;
|
||||
uint_least8_t capturedPiece = NOT_SELECTED;
|
||||
const struct piece_t pieceAll = {ALL_PIECES, movedPiece.color};
|
||||
if(bitboardGet(board, pieceAll, dst)) return;
|
||||
uint_least8_t dst, uint_least8_t spezialMove, uint_least8_t promotionPiece) {
|
||||
struct gameState_t gameState = ctx.gameState;
|
||||
uint_least64_t *board = gameState.board;
|
||||
{
|
||||
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);
|
||||
if(bitboardGet(board, pieceAllOtherColor, capturePos)) {
|
||||
for(uint_least8_t pieceType = QUEEN; pieceType < PIECES_LENGTH; ++pieceType) {
|
||||
const struct piece_t piece = {pieceType, !movedPiece.color};
|
||||
if(bitboardGet(board, piece, capturePos)) {
|
||||
capturedPiece = pieceType;
|
||||
const struct piece_t capturedPiece = {pieceType, otherColor};
|
||||
hash ^= ZOBRIST_PIECE[zobristPieceI(capturedPiece, dst)];
|
||||
capturedPieceType = pieceType;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
const struct move_t move = {src, dst, spezialMove, movedPiece.type, movedPiece.type, capturedPiece};
|
||||
makeMove(ctx.gameState, move);
|
||||
if(gameState.enPassantTo != NOT_SELECTED) hash ^= ZOBRIST_EN_PASSENT_FILE[getFile(gameState.enPassantTo)];
|
||||
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;
|
||||
const struct piece_t king = {KING, color};
|
||||
if(!inCheck(board, bitboardGetMask(board, king), 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)) {
|
||||
ctx.moves[*movesLength] = move;
|
||||
++*movesLength;
|
||||
}
|
||||
undoMove(board, move, color);
|
||||
undoMove(newGameState, move, color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -181,7 +273,7 @@ static void addPreGenMoves(struct addMoveCtx_t ctx, const struct moveDst_t *move
|
||||
uint_least8_t src, struct piece_t piece) {
|
||||
const struct moveDst_t moveDstField = moveDst[src];
|
||||
for(uint_least8_t i = 0; i < moveDstField.length; ++i) {
|
||||
addMove(ctx, piece, src, moveDstField.dst[i], 0);
|
||||
addMove(ctx, piece, src, moveDstField.dst[i], 0, NOT_SELECTED);
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,7 +285,7 @@ static void moveSliding(const uint_least8_t *direction, uint_least8_t directionL
|
||||
const int_least8_t modifier = DIRECTION_MODIFIER[direction[j]];
|
||||
for(uint_least8_t currentField = field + modifier, i = 0;
|
||||
i < getDirectionOffset(field, direction[j]); ++i, currentField += modifier) {
|
||||
addMove(ctx, movedPiece, field, currentField, 0);
|
||||
addMove(ctx, movedPiece, field, currentField, 0, NOT_SELECTED);
|
||||
if(bitboardGetAllPieces(board, currentField)) { // ziehe die info wo das nächste piece steht
|
||||
// über bit ops?
|
||||
break;
|
||||
@ -210,12 +302,11 @@ static void movePawn(struct addMoveCtx_t ctx, bool color, uint_least8_t src, uin
|
||||
const struct piece_t piece = {PAWN, color};
|
||||
if(promotion && (color == WHITE && dst < BOARD_SIZE || color == BLACK && dst >= BOARD_SIZE * (BOARD_SIZE - 1))) {
|
||||
for(uint_least8_t promotionPiece = QUEEN; promotionPiece < PAWN; ++promotionPiece) {
|
||||
addMove(ctx, piece, src, dst, spezialMove); // always adds new move
|
||||
moves[*movesLength].dstPiece = promotionPiece;
|
||||
addMove(ctx, piece, src, dst, spezialMove, promotionPiece);
|
||||
}
|
||||
}
|
||||
else {
|
||||
addMove(ctx, piece, src, dst, spezialMove);
|
||||
addMove(ctx, piece, src, dst, spezialMove, NOT_SELECTED);
|
||||
}
|
||||
}
|
||||
|
||||
@ -231,14 +322,16 @@ uint_least8_t pieceValidMoves(struct gameState_t gameState, struct piece_t piece
|
||||
addPreGenMoves(addmoveCtx, KING_MOVES, src, piece);
|
||||
const struct castle_t canCastle = gameState.canCastle[piece.color];
|
||||
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 shortCastleCheckMask = (uint_least64_t)0b11 << (4 + rankShift);
|
||||
const uint_least64_t longCastleMask = (uint_least64_t)0b111 << (1 + rankShift);
|
||||
if(canCastle.shortCastle && !(allPiecesMask & shortCastleMask) && !inCheck(board, shortCastleMask, piece.color)) {
|
||||
addMove(addmoveCtx, piece, src, src + 2 * DIRECTION_MODIFIER[EAST], SHORT_CASTLE);
|
||||
const uint_least64_t longCastleCheckMask = (uint_least64_t)0b11 << (3 + rankShift);
|
||||
if(canCastle.shortCastle && !(allPiecesMask & shortCastleMask) && !inCheck(board, shortCastleCheckMask, piece.color)) {
|
||||
addMove(addmoveCtx, piece, src, src + 2 * DIRECTION_MODIFIER[EAST], SHORT_CASTLE, NOT_SELECTED);
|
||||
}
|
||||
if(canCastle.longCastle && !(allPiecesMask & longCastleMask) && !inCheck(board, longCastleMask, piece.color)) {
|
||||
addMove(addmoveCtx, piece, src, src + 2 * DIRECTION_MODIFIER[WEST], LONG_CASTLE);
|
||||
if(canCastle.longCastle && !(allPiecesMask & longCastleMask) && !inCheck(board, longCastleCheckMask, piece.color)) {
|
||||
addMove(addmoveCtx, piece, src, src + 2 * DIRECTION_MODIFIER[WEST], LONG_CASTLE, NOT_SELECTED);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -280,7 +373,7 @@ uint_least8_t pieceValidMoves(struct gameState_t gameState, struct piece_t piece
|
||||
uint_least8_t dst = src + DIRECTION_MODIFIER[pawnDirection];
|
||||
if(!bitboardGetAllPieces(board, dst)) {
|
||||
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];
|
||||
if(!bitboardGetAllPieces(board, dst)) {
|
||||
movePawn(addmoveCtx, piece.color, src, dst, promotion, FUTURE_EN_PASSANT);
|
||||
@ -314,18 +407,17 @@ uint_least8_t validMoves(struct gameState_t gameState, struct move_t *moves) {
|
||||
return movesLength;
|
||||
}
|
||||
|
||||
static void setCastleInfo(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 zobristTableMakeMoveCallback(struct zobristTableElement *element, const uint_least64_t key,
|
||||
void *resultVoid) {
|
||||
if(element->hash == key) {
|
||||
++element->value;
|
||||
return;
|
||||
}
|
||||
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;
|
||||
const struct piece_t srcPiece = {move.srcPiece, gameState.color};
|
||||
const struct piece_t dstPiece = {move.dstPiece, gameState.color};
|
||||
@ -336,37 +428,15 @@ struct gameState_t makeMove(struct gameState_t gameState, struct move_t move) {
|
||||
bitboardClear(board, capturedPiece, getCapturePos(move.src, move.dst, move.spezialMove));
|
||||
}
|
||||
castleMoveRook(board, move.spezialMove, gameState.color, false);
|
||||
{
|
||||
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));
|
||||
}
|
||||
}
|
||||
setCastleInfo(gameState.canCastle, gameState.color, move);
|
||||
{
|
||||
const int_least8_t dist = (int_least8_t)move.dst - (int_least8_t)move.src;
|
||||
gameState.enPassantTo = FUTURE_EN_PASSANT ? move.src + dist / 2 : NOT_SELECTED;
|
||||
gameState.enPassantTo = move.spezialMove == FUTURE_EN_PASSANT ? move.src + dist / 2 : NOT_SELECTED;
|
||||
}
|
||||
if(move.capturedPiece != NOT_SELECTED || srcPiece.type == PAWN) gameState.halfMoveCounter = 0;
|
||||
else ++gameState.halfMoveCounter;
|
||||
gameState.color = !gameState.color;
|
||||
gameState.zobrist ^= move.hash;
|
||||
zobristTableIter(gameState.repetitionTable, gameState.zobrist, NULL, zobristTableMakeMoveCallback);
|
||||
return gameState;
|
||||
}
|
||||
|
||||
struct gameState_t computerMove(struct gameState_t gameState) {
|
||||
struct move_t moves[MAX_VALID_MOVES];
|
||||
validMoves(gameState, moves);
|
||||
return makeMove(gameState, moves[0]);
|
||||
}
|
||||
|
||||
@ -1,20 +1,12 @@
|
||||
#include "chess/zobristConsts.h"
|
||||
#include <ctype.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <chess/types.h>
|
||||
#include <stdlib.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);
|
||||
}
|
||||
#include <chess/util.h>
|
||||
#include <chess/bitset.h>
|
||||
#include <chess/bitboard.h>
|
||||
|
||||
uint_least64_t bitboardGetMask(const uint_least64_t *board, struct piece_t piece) {
|
||||
return board[piece.color * PIECES_LENGTH + piece.type];
|
||||
@ -75,59 +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 gameState = {board};
|
||||
for(uint_least8_t i = 0; i < BITBOARD_LENGTH; ++i) {
|
||||
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;
|
||||
else {
|
||||
gameState.enPassantTo = *FEN++ - 'a';
|
||||
gameState.enPassantTo += (BOARD_SIZE - *FEN++) * BOARD_SIZE;
|
||||
}
|
||||
gameState.halfMoveCounter = atoi(++FEN);
|
||||
return gameState;
|
||||
struct gameState_t parseFen(uint_least64_t *board, const char *FEN) {
|
||||
struct gameState_t gameState = {board, 0};
|
||||
for(uint_least8_t i = 0; i < BITBOARD_LENGTH; ++i) {
|
||||
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);
|
||||
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;
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
#include <stdint.h>
|
||||
#include <chess/types.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_least8_t field, uint_least64_t blockMask) {
|
||||
14
src/common/preCodeGen/bitset.c
Normal file
14
src/common/preCodeGen/bitset.c
Normal 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);
|
||||
}
|
||||
|
||||
61
src/common/preCodeGen/print.c
Normal file
61
src/common/preCodeGen/print.c
Normal file
@ -0,0 +1,61 @@
|
||||
#include "chess/move.h"
|
||||
#include "chess/types.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <chess/bitset.h>
|
||||
#include <chess/util.h>
|
||||
|
||||
void printerll(FILE *file, long long num) {
|
||||
fprintf(file, "%lld", num);
|
||||
}
|
||||
|
||||
void printerull(FILE *file, unsigned long long num) {
|
||||
fprintf(file, "%lluu", num);
|
||||
}
|
||||
|
||||
// for debugging
|
||||
void printPieceMask(uint_least64_t mask) {
|
||||
for(uint_least8_t i = 0; i < BOARD_SIZE; ++i, mask >>= BOARD_SIZE) {
|
||||
for(uint_least8_t j = 0; j < BOARD_SIZE; ++j) {
|
||||
printf("%d ", bitsetGet(mask, j));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
void fieldToString(uint_least8_t field, char *output) {
|
||||
output[0] = 'a' + getFile(field);
|
||||
output[1] = '0' + BOARD_SIZE - getRank(field);
|
||||
output[2] = '\0';
|
||||
}
|
||||
|
||||
void printMove(const struct move_t move) {
|
||||
char src[3];
|
||||
char dst[3];
|
||||
fieldToString(move.src, src);
|
||||
fieldToString(move.dst, dst);
|
||||
printf("%s%s", src, dst);
|
||||
if(move.srcPiece != move.dstPiece) {
|
||||
char piece;
|
||||
switch(move.dstPiece) {
|
||||
case ROOK:
|
||||
piece = 'R';
|
||||
break;
|
||||
case BISHOP:
|
||||
piece = 'B';
|
||||
break;
|
||||
case QUEEN:
|
||||
piece = 'Q';
|
||||
break;
|
||||
case KNIGHT:
|
||||
piece = 'N';
|
||||
break;
|
||||
}
|
||||
printf("%c", piece);
|
||||
}
|
||||
}
|
||||
|
||||
void printMoveln(const struct move_t move) {
|
||||
printMove(move);
|
||||
printf("\n");
|
||||
}
|
||||
78
src/common/preCodeGen/util.c
Normal file
78
src/common/preCodeGen/util.c
Normal 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;
|
||||
}
|
||||
@ -1,22 +0,0 @@
|
||||
#include "chess/types.h"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <chess/bitboard.h>
|
||||
|
||||
void printerll(FILE *file, long long num) {
|
||||
fprintf(file, "%lld", num);
|
||||
}
|
||||
|
||||
void printerull(FILE *file, unsigned long long num) {
|
||||
fprintf(file, "%lluu", num);
|
||||
}
|
||||
|
||||
// for debugging
|
||||
void printPieceMask(uint_least64_t mask) {
|
||||
for(uint_least8_t i = 0; i < BOARD_SIZE; ++i, mask >>= BOARD_SIZE) {
|
||||
for(uint_least8_t j = 0; j < BOARD_SIZE; ++j) {
|
||||
printf("%d ", bitsetGet(mask, j));
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@ -6,21 +6,16 @@
|
||||
#include <stdint.h>
|
||||
#include <chess/print.h>
|
||||
#include <inttypes.h>
|
||||
#include <assert.h>
|
||||
#include <chess/magic.h>
|
||||
#include <chess/generated/moveConsts.h>
|
||||
#include <chess/bitboard.h>
|
||||
#include <chess/bitset.h>
|
||||
#include <chess/util.h>
|
||||
|
||||
#define MAX_BITS 12
|
||||
#define MAX_SIZE (1 << MAX_BITS)
|
||||
#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() {
|
||||
return rand_64() & rand_64() & rand_64();
|
||||
}
|
||||
@ -76,12 +71,17 @@ static bool tryMagicNum(uint_least8_t field, struct magic_t *magic, uint_least64
|
||||
|
||||
static void initMagicField(uint_least64_t *attackMask, struct magic_t *magicTable, uint_least8_t field,
|
||||
const uint_least8_t *direction, uint_least8_t directionLength) {
|
||||
defineDirectionOffset;
|
||||
struct magic_t *magic = magicTable + field;
|
||||
attackMask += field * MAX_SIZE;
|
||||
|
||||
/* & 0x7e... because we dont care about the outer ring of pieces, because the pieces are threated as
|
||||
enemy pieces. */
|
||||
magic->mask = slidingMovementMask(direction, directionLength, field, 0) & 0x7e7e7e7e7e7e7e7e;
|
||||
const uint_least8_t *localDirectionOffset = DIRECTION_OFFSET + field * DIRECTION_LENGTH;
|
||||
for(uint_least8_t j = 0; j < directionLength; ++j) {
|
||||
const int_least8_t modifier = DIRECTION_MODIFIER[direction[j]];
|
||||
for(uint_least8_t currentField = field + modifier, i = 0;
|
||||
i + 1 < localDirectionOffset[direction[j]]; ++i, currentField += modifier) {
|
||||
magic->mask = bitsetSet(magic->mask, currentField);
|
||||
}
|
||||
}
|
||||
magic->shift = 64 - MAX_BITS - 1;
|
||||
uint_least64_t pieceArrangement = 0;
|
||||
size_t i = 0;
|
||||
|
||||
@ -2,8 +2,9 @@
|
||||
#include <chess/types.h>
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#include <chess/bitboard.h>
|
||||
#include <chess/bitset.h>
|
||||
#include <chess/print.h>
|
||||
#include <chess/util.h>
|
||||
|
||||
static void printerMoveDst(FILE *file, struct moveDst_t moveDst) {
|
||||
fprintf(file, "{ %" PRIuLEAST8 ", ", moveDst.length);
|
||||
@ -36,8 +37,8 @@ int main() {
|
||||
);
|
||||
uint_least8_t directionOffset[TOTAL_BOARD_SIZE * DIRECTION_LENGTH];
|
||||
for(uint_least16_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
|
||||
uint_least8_t file = field % BOARD_SIZE;
|
||||
uint_least8_t rank = field / BOARD_SIZE;
|
||||
uint_least8_t file = getFile(field);
|
||||
uint_least8_t rank = getRank(field);
|
||||
uint_least8_t *offsetField = directionOffset + field * DIRECTION_LENGTH;
|
||||
offsetField[NORTH] = rank;
|
||||
offsetField[WEST] = file;
|
||||
@ -111,11 +112,14 @@ int main() {
|
||||
const uint_least8_t pawnDirection = color == WHITE ? NORTH : SOUTH;
|
||||
const uint_least8_t *fieldDirectionOffset = directionOffset + field * DIRECTION_LENGTH;
|
||||
uint_least64_t checkMask = 0;
|
||||
if(fieldDirectionOffset[pawnDirection] == 0) continue;
|
||||
if(fieldDirectionOffset[pawnDirection] == 0) {
|
||||
pawnCheck[2 * field + color] = 0;
|
||||
continue;
|
||||
}
|
||||
const uint_least8_t directions[] = {EAST, WEST};
|
||||
for(uint_least8_t j = 0; j < LENGTH(directions); ++j) {
|
||||
uint_least8_t dst = field + directionModifier[directions[j]] + directionModifier[pawnDirection];
|
||||
if(fieldDirectionOffset[directions[j]] >= 1) {
|
||||
const uint_least8_t dst = field + directionModifier[directions[j]] + directionModifier[pawnDirection];
|
||||
checkMask = bitsetSet(checkMask, dst);
|
||||
}
|
||||
}
|
||||
|
||||
39
src/generateCode/zobrist.c
Normal file
39
src/generateCode/zobrist.c
Normal 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
72
src/uci.c
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@
|
||||
#include <stdint.h>
|
||||
#include <chess/bitboard.h>
|
||||
#include <stdio.h>
|
||||
#include <chess/print.h>
|
||||
|
||||
struct perf_t {
|
||||
char FEN[256];
|
||||
@ -11,19 +12,42 @@ struct perf_t {
|
||||
uint_least64_t nodes;
|
||||
};
|
||||
|
||||
uint_least64_t perft(const struct gameState_t gameState, const uint_least8_t depth) {
|
||||
static uint_least64_t perft(const struct gameState_t gameState, const uint_least8_t depth) {
|
||||
struct move_t moves[MAX_VALID_MOVES];
|
||||
uint_least64_t nodes = 0;
|
||||
const uint_least8_t movesLength = validMoves(gameState, moves);
|
||||
if(depth == 1) return movesLength;
|
||||
//if(depth == 0) return 1;
|
||||
for(uint_least8_t i = 0; i < movesLength; ++i) {
|
||||
const struct gameState_t newGameState = makeMove(gameState, moves[i]);
|
||||
nodes += perft(newGameState, depth - 1);
|
||||
undoMove(gameState.board, moves[i], gameState.color);
|
||||
undoMove(newGameState, moves[i], gameState.color);
|
||||
}
|
||||
return nodes;
|
||||
}
|
||||
|
||||
static void test(struct perf_t perf, const uint_least8_t i) {
|
||||
uint_least64_t board[BITBOARD_LENGTH];
|
||||
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);
|
||||
if(perf.nodes != nodes) {
|
||||
printf("Test %" PRIuLEAST16 " failed: FEN: %s, depth: %" PRIuLEAST8 " calculated nodes: %" PRIuLEAST64
|
||||
", expected nodes: %" PRIuLEAST64 "\n", i, perf.FEN, perf.depth, nodes, perf.nodes);
|
||||
struct move_t moves[MAX_VALID_MOVES];
|
||||
const uint_least8_t movesLength = validMoves(gameState, moves);
|
||||
for(uint_least8_t j = 0; j < movesLength; ++j) {
|
||||
const struct gameState_t newGameState = makeMove(gameState, moves[j]);
|
||||
const uint_least64_t nodes = perft(newGameState, perf.depth - 1);
|
||||
undoMove(newGameState, moves[j], gameState.color);
|
||||
{
|
||||
printMove(moves[j]);
|
||||
printf(" %" PRIuLEAST64 "\n", nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main() {
|
||||
struct perf_t testPos[] = {
|
||||
{"rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", 5, 4865609}, // start Position 0
|
||||
@ -32,15 +56,16 @@ int main() {
|
||||
{"r3k2r/Pppp1ppp/1b3nbN/nP6/BBP1P3/q4N2/Pp1P2PP/R2Q1RK1 w kq - 0 1", 4, 422333}, // 3
|
||||
{"rnbq1k1r/pp1Pbppp/2p5/8/2B5/8/PPP1NnPP/RNBQK2R w KQ - 1 8", 4, 2103487}, // 4
|
||||
{"r4rk1/1pp1qppp/p1np1n2/2b1p1B1/2B1P1b1/P1NP1N2/1PP1QPPP/R4RK1 w - - 0 10", 4, 3894594}, // 5
|
||||
{"5k2/5pp1/1b3pP1/6P1/rN3P2/p5Nn/3r4/5n1K w - - 0 1", 5, 3949613}, // 6
|
||||
{"8/r2q2rQ/1b1pbkPp/BPPnNB2/pP2ppRP/n2pPpp1/PKR4P/1N6 w - - 0 1", 4, 2349215}, // 7
|
||||
{"n2Q2R1/K1B1pkbP/2p2P1P/2rPpB2/pq1p1Ppn/1PRN1bP1/P2p1p2/Nr6 w - - 0 1", 4, 4382892}, // 8
|
||||
{"B3n2N/1p1p3p/1Pr1P2B/3ppk2/P2q1p1p/Pbr1p1PP/P4NP1/b2RKnRQ w - - 0 1", 4, 1185726}, // 9
|
||||
{"8/1P2Q3/7R/3P4/R7/5p2/K7/2k4n w - - 0 1", 5, 5509638}, // 10
|
||||
{"8/N6P/K7/8/2RP4/p2n4/r7/5k2 w - - 0 1", 5, 4119222}, // 11
|
||||
{"n7/6kq/8/4P1p1/K7/3P4/3p1p2/8 w - - 0 1", 6, 11762127}, // 12
|
||||
};
|
||||
initMagicTable();
|
||||
for(uint_least16_t i = 0; i < LENGTH(testPos); ++i) {
|
||||
uint_least64_t board[BITBOARD_LENGTH];
|
||||
const struct gameState_t gameState = newGameState(board, testPos[i].FEN);
|
||||
const uint_least64_t nodes = perft(gameState, testPos[i].depth);
|
||||
if(testPos[i].nodes != nodes) {
|
||||
printf("Test %" PRIuLEAST16 " failed: FEN: %s, depth: %" PRIuLEAST8 " calculated nodes: %" PRIuLEAST64
|
||||
", expected nodes: %" PRIuLEAST64 "\n", i, testPos[i].FEN, testPos[i].depth, nodes, testPos[i].nodes);
|
||||
}
|
||||
test(testPos[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user