Compare commits
32 Commits
a7282de590
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 4458fb6491 | |||
| 924e7d5eed | |||
| 6ccd2a4efe | |||
| 6e61a14cdb | |||
| 2c3d1bc021 | |||
| 6c5ef0586f | |||
| db958dcac1 | |||
| 4d2a86c7b7 | |||
| 5209dfec92 | |||
| a649db77ef | |||
| 1ab928cc6f | |||
| 6178e7c963 | |||
| 2d49f86517 | |||
| 7900546964 | |||
| f0f12be88d | |||
| 1d5f0b7d75 | |||
| 21de797d66 | |||
| 84fad9abb8 | |||
| 69790345c5 | |||
| 3b9fc1f872 | |||
| f0fe9454f8 | |||
| d49e1971e1 | |||
| 93d3c6f648 | |||
| 461c7fe680 | |||
| 1b779b7667 | |||
| 2e813e5744 | |||
| e21fabb94c | |||
| 79419d2007 | |||
| 5b4dbee026 | |||
| 5964cd7239 | |||
| ad2f8a7005 | |||
| d1bbee41e1 |
13
.gitignore
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
/*
|
||||||
|
!/src
|
||||||
|
/src/generated
|
||||||
|
!/CMakeLists.txt
|
||||||
|
!/README.md
|
||||||
|
!/LICENSE
|
||||||
|
!/pieces
|
||||||
|
!/lib
|
||||||
|
!.gitkeep
|
||||||
|
!/include
|
||||||
|
/include/chess/generated/*
|
||||||
|
!/.gitignore
|
||||||
|
!/test
|
||||||
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "lib/acl"]
|
||||||
|
path = lib/acl
|
||||||
|
url = https://git.zinkel.org/MrGeorgen/advanced_C_standard_library.git
|
||||||
54
CMakeLists.txt
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
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 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 ${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_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})
|
||||||
|
add_dependencies(findMagicNumber generateCode)
|
||||||
|
|
||||||
|
add_executable(${PROJECT_NAME} ${SOURCES} ${LIB_SOURCES})
|
||||||
|
target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG)
|
||||||
|
add_dependencies(${PROJECT_NAME} generateCode)
|
||||||
|
|
||||||
|
# Enable LTO for release build type
|
||||||
|
if(CMAKE_BUILD_TYPE MATCHES "Release")
|
||||||
|
include(CheckIPOSupported)
|
||||||
|
check_ipo_supported(RESULT result OUTPUT output)
|
||||||
|
if(result)
|
||||||
|
set_target_properties(${PROJECT_NAME} PROPERTIES INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||||
|
else()
|
||||||
|
message(WARNING "IPO is not supported: ${output}")
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_executable(chessNoComputer ${SOURCES} ${LIB_SOURCES})
|
||||||
|
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})
|
||||||
|
add_test(NAME moveGen COMMAND moveGenTest)
|
||||||
16
LICENSE
@ -1,9 +1,23 @@
|
|||||||
|
License for the program:
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|
||||||
Copyright (c) <year> <copyright holders>
|
Copyright (c) 2023 Marcel Zinkel
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
License for the chess pieces (https://commons.wikimedia.org/wiki/Category:SVG_chess_pieces):
|
||||||
|
|
||||||
|
Copyright © Cburnett
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||||
|
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||||
|
Neither the name of The author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
This software is provided by The author and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall The author and contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.
|
||||||
|
|||||||
81
README.md
@ -1,3 +1,80 @@
|
|||||||
# chess
|
<!-- LTeX: language=en-US -->
|
||||||
|
# Chess
|
||||||
|
|
||||||
A chess engine
|
A chess engine with focus on fast move generation.
|
||||||
|
|
||||||
|
## Building
|
||||||
|
|
||||||
|
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.
|
||||||
|
|||||||
18
include/chess/bitboard.h
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/types.h>
|
||||||
|
|
||||||
|
#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);
|
||||||
|
bool bitboardGetAllPieces(const uint_least64_t *board, uint_least8_t i);
|
||||||
|
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,
|
||||||
|
struct zobristTableElement *repetitionTableStore, const char *FEN);
|
||||||
|
struct gameState_t parseFen(uint_least64_t *board, const char *FEN);
|
||||||
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);
|
||||||
0
include/chess/generated/.gitkeep
Normal file
10
include/chess/magic.h
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#ifndef CHESS_MAGIC_H
|
||||||
|
#define CHESS_MAGIC_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/types.h>
|
||||||
|
|
||||||
|
uint_least64_t slidingMovementMask(const uint_least8_t *direction, uint_least8_t directionLength,
|
||||||
|
uint_least8_t field, uint_least64_t blockMask);
|
||||||
|
uint_least64_t* getMagicAttackPtr(const uint_least64_t mask, const struct magic_t magic);
|
||||||
|
#endif
|
||||||
35
include/chess/move.h
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#ifndef CHESS_MOVE_H
|
||||||
|
#define CHESS_MOVE_H
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
|
||||||
|
#define MAX_VALID_MOVES 218
|
||||||
|
|
||||||
|
enum spezialMoves {
|
||||||
|
EN_PASSANT = 1,
|
||||||
|
FUTURE_EN_PASSANT,
|
||||||
|
SHORT_CASTLE,
|
||||||
|
LONG_CASTLE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct move_t {
|
||||||
|
uint_least8_t src, dst, spezialMove;
|
||||||
|
uint_least8_t srcPiece, dstPiece, capturedPiece;
|
||||||
|
uint_least64_t hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
void genDirectionConsts();
|
||||||
|
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(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
|
||||||
34
include/chess/print.h
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/move.h>
|
||||||
|
|
||||||
|
#define fprintArrayCustomLength(file, printer, arr, length) \
|
||||||
|
do { \
|
||||||
|
fprintf(file, "{"); \
|
||||||
|
for(size_t i = 0; i < length; ++i) { \
|
||||||
|
printer(file, arr[i]); \
|
||||||
|
if (i < length - 1) { \
|
||||||
|
fprintf(file, ", "); \
|
||||||
|
} \
|
||||||
|
} \
|
||||||
|
fprintf(file, " }"); \
|
||||||
|
} while(0)
|
||||||
|
#define fprintArray(file, printer, arr) fprintArrayCustomLength(file, printer, arr, LENGTH(arr))
|
||||||
|
|
||||||
|
#define fdefineArrayCustomLength(file, declaration, printer, arr, length) \
|
||||||
|
do { \
|
||||||
|
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 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);
|
||||||
77
include/chess/types.h
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
#ifndef CHESS_TYPES_H
|
||||||
|
#define CHESS_TYPES_H
|
||||||
|
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
|
||||||
|
#define BOARD_SIZE 8
|
||||||
|
#define TOTAL_BOARD_SIZE (BOARD_SIZE * BOARD_SIZE)
|
||||||
|
#define LENGTH(array) (sizeof array / sizeof *array)
|
||||||
|
#define NOT_SELECTED UINT_LEAST8_MAX
|
||||||
|
#define BITBOARD_LENGTH 2 * PIECES_LENGTH
|
||||||
|
|
||||||
|
enum pieces {
|
||||||
|
ALL_PIECES,
|
||||||
|
KING,
|
||||||
|
QUEEN,
|
||||||
|
ROOK,
|
||||||
|
BISHOP,
|
||||||
|
KNIGHT,
|
||||||
|
PAWN,
|
||||||
|
PIECES_LENGTH
|
||||||
|
};
|
||||||
|
|
||||||
|
enum color {
|
||||||
|
BLACK,
|
||||||
|
WHITE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct piece_t {
|
||||||
|
uint_least8_t type;
|
||||||
|
bool color;
|
||||||
|
};
|
||||||
|
|
||||||
|
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];
|
||||||
|
};
|
||||||
|
|
||||||
|
struct magic_t {
|
||||||
|
uint_least64_t *attackTable;
|
||||||
|
uint_least64_t mask; // mask for relvant squares
|
||||||
|
uint_least64_t magicNumber;
|
||||||
|
uint_least8_t shift;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct castle_t {
|
||||||
|
bool shortCastle, longCastle;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct gameState_t {
|
||||||
|
uint_least64_t *board;
|
||||||
|
uint_least64_t zobrist;
|
||||||
|
struct zobristTable repetitionTable;
|
||||||
|
bool color; // color to move
|
||||||
|
struct castle_t canCastle[2];
|
||||||
|
|
||||||
|
// The number of halfmoves since the last capture or pawn advance, used for the fifty-move rule.
|
||||||
|
uint_least8_t halfMoveCounter;
|
||||||
|
uint_least8_t enPassantTo; // index of the destination for a possible en passant capture
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
||||||
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
@ -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
|
||||||
12
pieces/black_bishop.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,0.6)">
|
||||||
|
<g style="fill:#000000; stroke:#000000; stroke-linecap:butt;">
|
||||||
|
<path d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.65,38.99 6.68,38.97 6,38 C 7.35,36.54 9,36 9,36 z"/>
|
||||||
|
<path d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z"/>
|
||||||
|
<path d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z"/>
|
||||||
|
</g>
|
||||||
|
<path d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18" style="fill:none; stroke:#ffffff; stroke-linejoin:miter;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
12
pieces/black_king.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;">
|
||||||
|
<path d="M 22.5,11.63 L 22.5,6" style="fill:none; stroke:#000000; stroke-linejoin:miter;" id="path6570"/>
|
||||||
|
<path d="M 22.5,25 C 22.5,25 27,17.5 25.5,14.5 C 25.5,14.5 24.5,12 22.5,12 C 20.5,12 19.5,14.5 19.5,14.5 C 18,17.5 22.5,25 22.5,25" style="fill:#000000;fill-opacity:1; stroke-linecap:butt; stroke-linejoin:miter;"/>
|
||||||
|
<path d="M 12.5,37 C 18,40.5 27,40.5 32.5,37 L 32.5,30 C 32.5,30 41.5,25.5 38.5,19.5 C 34.5,13 25,16 22.5,23.5 L 22.5,27 L 22.5,23.5 C 20,16 10.5,13 6.5,19.5 C 3.5,25.5 12.5,30 12.5,30 L 12.5,37" style="fill:#000000; stroke:#000000;"/>
|
||||||
|
<path d="M 20,8 L 25,8" style="fill:none; stroke:#000000; stroke-linejoin:miter;"/>
|
||||||
|
<path d="M 32,29.5 C 32,29.5 40.5,25.5 38.03,19.85 C 34.15,14 25,18 22.5,24.5 L 22.5,26.6 L 22.5,24.5 C 20,18 10.85,14 6.97,19.85 C 4.5,25.5 13,29.5 13,29.5" style="fill:none; stroke:#ffffff;"/>
|
||||||
|
<path d="M 12.5,30 C 18,27 27,27 32.5,30 M 12.5,33.5 C 18,30.5 27,30.5 32.5,33.5 M 12.5,37 C 18,34 27,34 32.5,37" style="fill:none; stroke:#ffffff;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
22
pieces/black_knight.svg
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,0.3)">
|
||||||
|
<path
|
||||||
|
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
|
||||||
|
style="fill:#ffffff; stroke:#ffffff;" />
|
||||||
|
<path
|
||||||
|
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
|
||||||
|
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
|
||||||
|
style="fill:#ffffff; stroke:#ffffff;" />
|
||||||
|
<path
|
||||||
|
d="M 24.55,10.4 L 24.1,11.85 L 24.6,12 C 27.75,13 30.25,14.49 32.5,18.75 C 34.75,23.01 35.75,29.06 35.25,39 L 35.2,39.5 L 37.45,39.5 L 37.5,39 C 38,28.94 36.62,22.15 34.25,17.66 C 31.88,13.17 28.46,11.02 25.06,10.5 L 24.55,10.4 z "
|
||||||
|
style="fill:#ffffff; stroke:none;" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.6 KiB |
5
pieces/black_pawn.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<path d="m 22.5,9 c -2.21,0 -4,1.79 -4,4 0,0.89 0.29,1.71 0.78,2.38 C 17.33,16.5 16,18.59 16,21 c 0,2.03 0.94,3.84 2.41,5.03 C 15.41,27.09 11,31.58 11,39.5 H 34 C 34,31.58 29.59,27.09 26.59,26.03 28.06,24.84 29,23.03 29,21 29,18.59 27.67,16.5 25.72,15.38 26.21,14.71 26.5,13.89 26.5,13 c 0,-2.21 -1.79,-4 -4,-4 z" style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 766 B |
27
pieces/black_queen.svg
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
|
||||||
|
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45"
|
||||||
|
height="45">
|
||||||
|
<g style="fill:#000000;stroke:#000000;stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round">
|
||||||
|
|
||||||
|
<path d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z"
|
||||||
|
style="stroke-linecap:butt;fill:#000000" />
|
||||||
|
<path d="m 9,26 c 0,2 1.5,2 2.5,4 1,1.5 1,1 0.5,3.5 -1.5,1 -1,2.5 -1,2.5 -1.5,1.5 0,2.5 0,2.5 6.5,1 16.5,1 23,0 0,0 1.5,-1 0,-2.5 0,0 0.5,-1.5 -1,-2.5 -0.5,-2.5 -0.5,-2 0.5,-3.5 1,-2 2.5,-2 2.5,-4 -8.5,-1.5 -18.5,-1.5 -27,0 z" />
|
||||||
|
<path d="M 11.5,30 C 15,29 30,29 33.5,30" />
|
||||||
|
<path d="m 12,33.5 c 6,-1 15,-1 21,0" />
|
||||||
|
<circle cx="6" cy="12" r="2" />
|
||||||
|
<circle cx="14" cy="9" r="2" />
|
||||||
|
<circle cx="22.5" cy="8" r="2" />
|
||||||
|
<circle cx="31" cy="9" r="2" />
|
||||||
|
<circle cx="39" cy="12" r="2" />
|
||||||
|
<path d="M 11,38.5 A 35,35 1 0 0 34,38.5"
|
||||||
|
style="fill:none; stroke:#000000;stroke-linecap:butt;" />
|
||||||
|
<g style="fill:none; stroke:#ffffff;">
|
||||||
|
<path d="M 11,29 A 35,35 1 0 1 34,29" />
|
||||||
|
<path d="M 12.5,31.5 L 32.5,31.5" />
|
||||||
|
<path d="M 11.5,34.5 A 35,35 1 0 0 33.5,34.5" />
|
||||||
|
<path d="M 10.5,37.5 A 35,35 1 0 0 34.5,37.5" />
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
39
pieces/black_rook.svg
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="opacity:1; fill:#000000; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,0.3)">
|
||||||
|
<path
|
||||||
|
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 12.5,32 L 14,29.5 L 31,29.5 L 32.5,32 L 12.5,32 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 14,29.5 L 14,16.5 L 31,16.5 L 31,29.5 L 14,29.5 z "
|
||||||
|
style="stroke-linecap:butt;stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 14,16.5 L 11,14 L 34,14 L 31,16.5 L 14,16.5 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14 L 11,14 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 12,35.5 L 33,35.5 L 33,35.5"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 13,31.5 L 32,31.5"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 14,29.5 L 31,29.5"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 14,16.5 L 31,16.5"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 11,14 L 34,14"
|
||||||
|
style="fill:none; stroke:#ffffff; stroke-width:1; stroke-linejoin:miter;" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.8 KiB |
12
pieces/white_bishop.svg
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="opacity:1; fill:none; fill-rule:evenodd; fill-opacity:1; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:round; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,0.6)">
|
||||||
|
<g style="fill:#ffffff; stroke:#000000; stroke-linecap:butt;">
|
||||||
|
<path d="M 9,36 C 12.39,35.03 19.11,36.43 22.5,34 C 25.89,36.43 32.61,35.03 36,36 C 36,36 37.65,36.54 39,38 C 38.32,38.97 37.35,38.99 36,38.5 C 32.61,37.53 25.89,38.96 22.5,37.5 C 19.11,38.96 12.39,37.53 9,38.5 C 7.65,38.99 6.68,38.97 6,38 C 7.35,36.54 9,36 9,36 z"/>
|
||||||
|
<path d="M 15,32 C 17.5,34.5 27.5,34.5 30,32 C 30.5,30.5 30,30 30,30 C 30,27.5 27.5,26 27.5,26 C 33,24.5 33.5,14.5 22.5,10.5 C 11.5,14.5 12,24.5 17.5,26 C 17.5,26 15,27.5 15,30 C 15,30 14.5,30.5 15,32 z"/>
|
||||||
|
<path d="M 25 8 A 2.5 2.5 0 1 1 20,8 A 2.5 2.5 0 1 1 25 8 z"/>
|
||||||
|
</g>
|
||||||
|
<path d="M 17.5,26 L 27.5,26 M 15,30 L 30,30 M 22.5,15.5 L 22.5,20.5 M 20,18 L 25,18" style="fill:none; stroke:#000000; stroke-linejoin:miter;"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
9
pieces/white_king.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="45" height="45">
|
||||||
|
<g fill="none" fill-rule="evenodd" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
|
||||||
|
<path stroke-linejoin="miter" d="M22.5 11.63V6M20 8h5"/>
|
||||||
|
<path fill="#fff" stroke-linecap="butt" stroke-linejoin="miter" d="M22.5 25s4.5-7.5 3-10.5c0 0-1-2.5-3-2.5s-3 2.5-3 2.5c-1.5 3 3 10.5 3 10.5"/>
|
||||||
|
<path fill="#fff" d="M12.5 37c5.5 3.5 14.5 3.5 20 0v-7s9-4.5 6-10.5c-4-6.5-13.5-3.5-16 4V27v-3.5c-2.5-7.5-12-10.5-16-4-3 6 6 10.5 6 10.5v7"/>
|
||||||
|
<path d="M12.5 30c5.5-3 14.5-3 20 0m-20 3.5c5.5-3 14.5-3 20 0m-20 3.5c5.5-3 14.5-3 20 0"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 700 B |
19
pieces/white_knight.svg
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="opacity:1; fill:none; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,0.3)">
|
||||||
|
<path
|
||||||
|
d="M 22,10 C 32.5,11 38.5,18 38,39 L 15,39 C 15,30 25,32.5 23,18"
|
||||||
|
style="fill:#ffffff; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 24,18 C 24.38,20.91 18.45,25.37 16,27 C 13,29 13.18,31.34 11,31 C 9.958,30.06 12.41,27.96 11,28 C 10,28 11.19,29.23 10,30 C 9,30 5.997,31 6,26 C 6,24 12,14 12,14 C 12,14 13.89,12.1 14,10.5 C 13.27,9.506 13.5,8.5 13.5,7.5 C 14.5,6.5 16.5,10 16.5,10 L 18.5,10 C 18.5,10 19.28,8.008 21,7 C 22,7 22,10 22,10"
|
||||||
|
style="fill:#ffffff; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 9.5 25.5 A 0.5 0.5 0 1 1 8.5,25.5 A 0.5 0.5 0 1 1 9.5 25.5 z"
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
<path
|
||||||
|
d="M 15 15.5 A 0.5 1.5 0 1 1 14,15.5 A 0.5 1.5 0 1 1 15 15.5 z"
|
||||||
|
transform="matrix(0.866,0.5,-0.5,0.866,9.693,-5.173)"
|
||||||
|
style="fill:#000000; stroke:#000000;" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.3 KiB |
5
pieces/white_pawn.svg
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<path d="m 22.5,9 c -2.21,0 -4,1.79 -4,4 0,0.89 0.29,1.71 0.78,2.38 C 17.33,16.5 16,18.59 16,21 c 0,2.03 0.94,3.84 2.41,5.03 C 15.41,27.09 11,31.58 11,39.5 H 34 C 34,31.58 29.59,27.09 26.59,26.03 28.06,24.84 29,23.03 29,21 29,18.59 27.67,16.5 25.72,15.38 26.21,14.71 26.5,13.89 26.5,13 c 0,-2.21 -1.79,-4 -4,-4 z" style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:nonzero; stroke:#000000; stroke-width:1.5; stroke-linecap:round; stroke-linejoin:miter; stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 766 B |
15
pieces/white_queen.svg
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="fill:#ffffff;stroke:#000000;stroke-width:1.5;stroke-linejoin:round">
|
||||||
|
<path d="M 9,26 C 17.5,24.5 30,24.5 36,26 L 38.5,13.5 L 31,25 L 30.7,10.9 L 25.5,24.5 L 22.5,10 L 19.5,24.5 L 14.3,10.9 L 14,25 L 6.5,13.5 L 9,26 z"/>
|
||||||
|
<path d="M 9,26 C 9,28 10.5,28 11.5,30 C 12.5,31.5 12.5,31 12,33.5 C 10.5,34.5 11,36 11,36 C 9.5,37.5 11,38.5 11,38.5 C 17.5,39.5 27.5,39.5 34,38.5 C 34,38.5 35.5,37.5 34,36 C 34,36 34.5,34.5 33,33.5 C 32.5,31 32.5,31.5 33.5,30 C 34.5,28 36,28 36,26 C 27.5,24.5 17.5,24.5 9,26 z"/>
|
||||||
|
<path d="M 11.5,30 C 15,29 30,29 33.5,30" style="fill:none"/>
|
||||||
|
<path d="M 12,33.5 C 18,32.5 27,32.5 33,33.5" style="fill:none"/>
|
||||||
|
<circle cx="6" cy="12" r="2" />
|
||||||
|
<circle cx="14" cy="9" r="2" />
|
||||||
|
<circle cx="22.5" cy="8" r="2" />
|
||||||
|
<circle cx="31" cy="9" r="2" />
|
||||||
|
<circle cx="39" cy="12" r="2" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.1 KiB |
25
pieces/white_rook.svg
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="45" height="45">
|
||||||
|
<g style="opacity:1; fill:#ffffff; fill-opacity:1; fill-rule:evenodd; stroke:#000000; stroke-width:1.5; stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4; stroke-dasharray:none; stroke-opacity:1;" transform="translate(0,0.3)">
|
||||||
|
<path
|
||||||
|
d="M 9,39 L 36,39 L 36,36 L 9,36 L 9,39 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 12,36 L 12,32 L 33,32 L 33,36 L 12,36 z "
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 11,14 L 11,9 L 15,9 L 15,11 L 20,11 L 20,9 L 25,9 L 25,11 L 30,11 L 30,9 L 34,9 L 34,14"
|
||||||
|
style="stroke-linecap:butt;" />
|
||||||
|
<path
|
||||||
|
d="M 34,14 L 31,17 L 14,17 L 11,14" />
|
||||||
|
<path
|
||||||
|
d="M 31,17 L 31,29.5 L 14,29.5 L 14,17"
|
||||||
|
style="stroke-linecap:butt; stroke-linejoin:miter;" />
|
||||||
|
<path
|
||||||
|
d="M 31,29.5 L 32.5,32 L 12.5,32 L 14,29.5" />
|
||||||
|
<path
|
||||||
|
d="M 11,14 L 34,14"
|
||||||
|
style="fill:none; stroke:#000000; stroke-linejoin:miter;" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
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
@ -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
|
||||||
3
src/chess/magicNumber.h
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
#include <chess/types.h>
|
||||||
|
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} };
|
||||||
246
src/chess/main.c
Normal file
@ -0,0 +1,246 @@
|
|||||||
|
#include <gtk/gtk.h>
|
||||||
|
#include <cairo.h>
|
||||||
|
#include <librsvg/rsvg.h>
|
||||||
|
#include <math.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <chess/types.h>
|
||||||
|
#include <chess/move.h>
|
||||||
|
#include <chess/bitboard.h>
|
||||||
|
#include "evaluate.h"
|
||||||
|
#include <chess/util.h>
|
||||||
|
|
||||||
|
struct drawData_t {
|
||||||
|
RsvgHandle **piecesSvg;
|
||||||
|
struct gameState_t *gameState;
|
||||||
|
uint_least8_t clickedPiece;
|
||||||
|
struct move_t *moves;
|
||||||
|
uint_least8_t movesLength;
|
||||||
|
uint_least8_t selectDst; // only for promotion
|
||||||
|
GtkWidget *drawArea;
|
||||||
|
double xOffset;
|
||||||
|
double yOffset;
|
||||||
|
double fieldSize;
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint_least8_t PROMOTION_PIECES[] = {QUEEN, KNIGHT, BISHOP, ROOK};
|
||||||
|
|
||||||
|
static uint_least8_t pieceToSvgI(struct piece_t piece) {
|
||||||
|
return piece.color == WHITE ? PIECES_LENGTH + piece.type : piece.type;
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
#ifndef NO_COMPUTER
|
||||||
|
move = bestMove(gameState);
|
||||||
|
gameState = makeMove(gameState, move);
|
||||||
|
#endif
|
||||||
|
return gameState;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
const double fieldSize = (double)(width < height ? width : height) / BOARD_SIZE;
|
||||||
|
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 struct gameState_t *gameState = drawData->gameState;
|
||||||
|
const uint_least64_t *board = gameState->board;
|
||||||
|
RsvgHandle **piecesSvg = drawData->piecesSvg;
|
||||||
|
drawData->xOffset = xOffset;
|
||||||
|
drawData->yOffset = yOffset;
|
||||||
|
drawData->fieldSize = fieldSize;
|
||||||
|
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;
|
||||||
|
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);
|
||||||
|
drawRetangle(cr, drawData, field, fieldSize);
|
||||||
|
if(bitboardGetAllPieces(board, field)) {
|
||||||
|
const struct piece_t piece = pieceAtField(board, rank * BOARD_SIZE + file);
|
||||||
|
drawPiece(drawData, cr, field, piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(drawData->clickedPiece != NOT_SELECTED && drawData->selectDst == NOT_SELECTED) {
|
||||||
|
cairo_set_source_rgba(cr, 0.0, 1.0, 0, 0.5);
|
||||||
|
selectCircle(cr, drawData, drawData->clickedPiece, fieldSize * 0.47);
|
||||||
|
cairo_set_line_width(cr, fieldSize / 20);
|
||||||
|
cairo_stroke(cr);
|
||||||
|
for(uint_least8_t i = 0; i < drawData->movesLength; ++i) {
|
||||||
|
selectCircle(cr, drawData, drawData->moves[i].dst, fieldSize / 6);
|
||||||
|
cairo_fill(cr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(drawData->selectDst != NOT_SELECTED) {
|
||||||
|
uint_least8_t uiPos = promotionUiPos(drawData->selectDst, gameState->color);
|
||||||
|
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
|
||||||
|
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], gameState->color};
|
||||||
|
drawPiece(drawData, cr, uiPos + i, piece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void on_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) {
|
||||||
|
struct drawData_t *drawData = (struct drawData_t*)data;
|
||||||
|
struct gameState_t *gameState = drawData->gameState;
|
||||||
|
const uint_least64_t *board = gameState->board;
|
||||||
|
const double fieldSize = drawData->fieldSize;
|
||||||
|
const double adjustedX = x - drawData->xOffset;
|
||||||
|
const double adjustedY = y - drawData->yOffset;
|
||||||
|
if(adjustedY < 0 || adjustedX < 0 ||
|
||||||
|
adjustedY > BOARD_SIZE * fieldSize || adjustedX > BOARD_SIZE * fieldSize) return;
|
||||||
|
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, gameState->color};
|
||||||
|
if(bitboardGet(board, allPiece, field)) {
|
||||||
|
// deactivated piece by clicking on it again
|
||||||
|
if(field == drawData->clickedPiece) {
|
||||||
|
drawData->clickedPiece = NOT_SELECTED;
|
||||||
|
drawData->movesLength = 0;
|
||||||
|
}
|
||||||
|
else { // clicked on new piece
|
||||||
|
const struct piece_t piece = pieceAtField(board, field);
|
||||||
|
drawData->clickedPiece = field;
|
||||||
|
drawData->movesLength = pieceValidMoves(*gameState, piece, field, drawData->moves, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(drawData->clickedPiece != NOT_SELECTED){
|
||||||
|
bool isValidDst = false;
|
||||||
|
for(uint_least8_t i = 0; i < drawData->movesLength; ++i) {
|
||||||
|
if(drawData->moves[i].dst == field) {
|
||||||
|
isValidDst = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isValidDst) {
|
||||||
|
const struct piece_t piece = pieceAtField(board, drawData->clickedPiece);
|
||||||
|
if(piece.type == PAWN && onPromotionField) { // promotion
|
||||||
|
drawData->selectDst = field;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
struct move_t move;
|
||||||
|
for(uint_least8_t i = 0; i < drawData->movesLength; ++i) {
|
||||||
|
if(drawData->moves[i].dst == field) {
|
||||||
|
move = drawData->moves[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawData->clickedPiece = NOT_SELECTED;
|
||||||
|
*gameState = userMove(*gameState, move);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
drawData->clickedPiece = NOT_SELECTED;
|
||||||
|
}
|
||||||
|
drawData->movesLength = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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, capturedPiece};
|
||||||
|
*gameState = userMove(*gameState, move);
|
||||||
|
drawData->selectDst = NOT_SELECTED;
|
||||||
|
drawData->clickedPiece = NOT_SELECTED;
|
||||||
|
}
|
||||||
|
gtk_widget_queue_draw(drawData->drawArea);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void app_activate(GApplication *app, gpointer data) {
|
||||||
|
GtkWindow *window = GTK_WINDOW(gtk_window_new());
|
||||||
|
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, repetitionTableStore, START_FEN);
|
||||||
|
{
|
||||||
|
static struct move_t moves[TOTAL_BOARD_SIZE];
|
||||||
|
static struct drawData_t drawData = {piecesSvg, &gameState, NOT_SELECTED, moves, 0, NOT_SELECTED};
|
||||||
|
|
||||||
|
GtkWidget *drawArea = gtk_drawing_area_new();
|
||||||
|
drawData.drawArea = drawArea;
|
||||||
|
gtk_window_set_child(window, drawArea);
|
||||||
|
gtk_drawing_area_set_draw_func(GTK_DRAWING_AREA(drawArea), draw_event, &drawData, NULL);
|
||||||
|
|
||||||
|
GtkGesture *gesture = gtk_gesture_click_new();
|
||||||
|
g_signal_connect(gesture, "pressed", G_CALLBACK(on_click), &drawData);
|
||||||
|
gtk_widget_add_controller(GTK_WIDGET(window), GTK_EVENT_CONTROLLER(gesture));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const char *const piecesFilenames[] = {
|
||||||
|
"king",
|
||||||
|
"queen",
|
||||||
|
"rook",
|
||||||
|
"bishop",
|
||||||
|
"knight",
|
||||||
|
"pawn",
|
||||||
|
};
|
||||||
|
for(uint i = 0; i < LENGTH(piecesFilenames); ++i) {
|
||||||
|
const char *const color[] = {"black", "white"};
|
||||||
|
for(uint j = 0; j < LENGTH(color); ++j) {
|
||||||
|
char filename[25];
|
||||||
|
GError *error = NULL;
|
||||||
|
const struct piece_t piece = {i + 1, j};
|
||||||
|
sprintf(filename, "pieces/%s_%s.svg", color[j], piecesFilenames[i]);
|
||||||
|
piecesSvg[pieceToSvgI(piece)] = rsvg_handle_new_from_file(filename, &error);
|
||||||
|
if(error != NULL) {
|
||||||
|
perror(error->message);
|
||||||
|
g_clear_error(&error);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gtk_window_set_application(window, GTK_APPLICATION(app));
|
||||||
|
gtk_window_present(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv) {
|
||||||
|
GtkApplication *app;
|
||||||
|
int stat;
|
||||||
|
initMagicTable();
|
||||||
|
|
||||||
|
app = gtk_application_new("org.zinkel.chess", G_APPLICATION_DEFAULT_FLAGS);
|
||||||
|
g_signal_connect(app, "activate", G_CALLBACK (app_activate), NULL);
|
||||||
|
stat = g_application_run(G_APPLICATION(app), argc, argv);
|
||||||
|
g_object_unref(app);
|
||||||
|
return stat;
|
||||||
|
}
|
||||||
442
src/chess/move.c
Normal file
@ -0,0 +1,442 @@
|
|||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/types.h>
|
||||||
|
#include <chess/move.h>
|
||||||
|
#include <chess/bitboard.h>
|
||||||
|
#include <chess/generated/moveConsts.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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>
|
||||||
|
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#include <intrin.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct addMoveCtx_t {
|
||||||
|
struct gameState_t gameState;
|
||||||
|
struct move_t *moves;
|
||||||
|
uint_least8_t *movesLength;
|
||||||
|
};
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t getMagicSize(const struct magic_t magic) {
|
||||||
|
return 1 << (64 - magic.shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t getTotalMagicSize(const struct magic_t *magic) {
|
||||||
|
size_t size = 0;
|
||||||
|
for(uint_least8_t i = 0; i < TOTAL_BOARD_SIZE; ++i) {
|
||||||
|
size += getMagicSize(magic[i]);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
magic->attackTable = attackTable;
|
||||||
|
attackTable += getMagicSize(*magic);
|
||||||
|
uint_least64_t pieceArangement = 0;
|
||||||
|
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);
|
||||||
|
*ptr = attackMask;
|
||||||
|
pieceArangement = (pieceArangement - magic->mask) & magic->mask;
|
||||||
|
} while(pieceArangement != 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void initMagicTable() {
|
||||||
|
const size_t rookMagicSize = getTotalMagicSize(rookMagic);
|
||||||
|
const size_t size = rookMagicSize + getTotalMagicSize(bishopMagic);
|
||||||
|
uint_least64_t *attackTable = malloc(size * sizeof *attackTable);
|
||||||
|
if(attackTable == NULL) {
|
||||||
|
perror("failed to allocate memory: ");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
initMagicHelper(attackTable, rookMagic, ROOK_DIRECTION, LENGTH(ROOK_DIRECTION));
|
||||||
|
initMagicHelper(attackTable + rookMagicSize, bishopMagic, BISHOP_DIRECTION, LENGTH(BISHOP_DIRECTION));
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint_least8_t getCapturePos(uint_least8_t src, uint_least8_t dst, uint_least8_t spezialMove) {
|
||||||
|
if(spezialMove != EN_PASSANT) return dst;
|
||||||
|
const uint_least8_t file = dst % 8;
|
||||||
|
const uint_least8_t rank = src / 8;
|
||||||
|
return rank * BOARD_SIZE + file;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkMagic(const uint_least64_t *board, const struct piece_t attackPiece,
|
||||||
|
const struct magic_t *magic, const uint_least64_t kingMask) {
|
||||||
|
uint_least8_t attackerField;
|
||||||
|
for(uint_least64_t attackerMask = bitboardGetMask(board, attackPiece); attackerMask != 0;
|
||||||
|
attackerMask = bitsetClear(attackerMask, attackerField)) {
|
||||||
|
attackerField = trailingBits(attackerMask);
|
||||||
|
const struct magic_t magicField = magic[attackerField];
|
||||||
|
const uint_least64_t checkMask = *getMagicAttackPtr(bitboardMaskAllPieces(board) & magicField.mask, magicField);
|
||||||
|
if(checkMask & kingMask) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool checkSimpleLookup(const uint_least64_t *board, const uint_least8_t field, const bool color) {
|
||||||
|
defineKnightCheck;
|
||||||
|
defineKingCheck;
|
||||||
|
definePawnCheck;
|
||||||
|
const struct piece_t knight = {KNIGHT, !color};
|
||||||
|
const struct piece_t pawn = {PAWN, !color};
|
||||||
|
const struct piece_t king = {KING, !color};
|
||||||
|
return bitboardGetMask(board, knight) & KNIGHT_CHECK[field] ||
|
||||||
|
bitboardGetMask(board, pawn) & PAWN_CHECK[2 * field + color] ||
|
||||||
|
bitboardGetMask(board, king) & KING_CHECK[field];
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool inCheck(const uint_least64_t *board, const uint_least64_t kingMask, const bool color) {
|
||||||
|
const struct piece_t rook = {ROOK, !color};
|
||||||
|
const struct piece_t bishop = {BISHOP, !color};
|
||||||
|
const struct piece_t queen = {QUEEN, !color};
|
||||||
|
uint_least8_t field;
|
||||||
|
for(uint_least64_t mask = kingMask; mask != 0; mask = bitsetClear(mask, field)) {
|
||||||
|
field = trailingBits(mask);
|
||||||
|
if(checkSimpleLookup(board, field, color)) return true;
|
||||||
|
}
|
||||||
|
return checkMagic(board, rook, rookMagic, kingMask) || checkMagic(board, bishop, bishopMagic, kingMask) ||
|
||||||
|
checkMagic(board, queen, rookMagic, kingMask) || checkMagic(board, queen, bishopMagic, kingMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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 = 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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_least8_t promotionPiece) {
|
||||||
|
struct gameState_t gameState = ctx.gameState;
|
||||||
|
uint_least64_t *board = gameState.board;
|
||||||
|
{
|
||||||
|
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)) {
|
||||||
|
const struct piece_t capturedPiece = {pieceType, otherColor};
|
||||||
|
hash ^= ZOBRIST_PIECE[zobristPieceI(capturedPiece, dst)];
|
||||||
|
capturedPieceType = pieceType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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};
|
||||||
|
{
|
||||||
|
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(newGameState, move, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void addPreGenMoves(struct addMoveCtx_t ctx, const struct moveDst_t *moveDst,
|
||||||
|
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, NOT_SELECTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void moveSliding(const uint_least8_t *direction, uint_least8_t directionLength, struct addMoveCtx_t ctx,
|
||||||
|
uint_least8_t field, struct piece_t movedPiece) {
|
||||||
|
uint_least64_t *board = ctx.gameState.board;
|
||||||
|
|
||||||
|
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 < getDirectionOffset(field, direction[j]); ++i, currentField += modifier) {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void movePawn(struct addMoveCtx_t ctx, bool color, uint_least8_t src, uint_least8_t dst,
|
||||||
|
bool promotion, uint_least8_t spezialMove) {
|
||||||
|
uint_least64_t *board = ctx.gameState.board;
|
||||||
|
struct move_t *moves = ctx.moves;
|
||||||
|
uint_least8_t *movesLength = ctx.movesLength;
|
||||||
|
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, promotionPiece);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
addMove(ctx, piece, src, dst, spezialMove, NOT_SELECTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint_least8_t pieceValidMoves(struct gameState_t gameState, struct piece_t piece, uint_least8_t src,
|
||||||
|
struct move_t *moves, bool promotion) {
|
||||||
|
uint_least64_t *board = gameState.board;
|
||||||
|
uint_least8_t movesLength = 0;
|
||||||
|
struct addMoveCtx_t addmoveCtx = {gameState, moves, &movesLength};
|
||||||
|
switch(piece.type) {
|
||||||
|
case KING:
|
||||||
|
{
|
||||||
|
defineKingMoves;
|
||||||
|
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 = 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);
|
||||||
|
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, longCastleCheckMask, piece.color)) {
|
||||||
|
addMove(addmoveCtx, piece, src, src + 2 * DIRECTION_MODIFIER[WEST], LONG_CASTLE, NOT_SELECTED);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ROOK:
|
||||||
|
moveSliding(MAIN_DIRECTION, LENGTH(MAIN_DIRECTION), addmoveCtx, src, piece);
|
||||||
|
break;
|
||||||
|
case BISHOP:
|
||||||
|
{
|
||||||
|
uint_least8_t direction[] = {NORTHWEST, SOUTHWEST, SOUTHEAST, NORTHEAST};
|
||||||
|
moveSliding(direction, LENGTH(direction), addmoveCtx, src, piece);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QUEEN:
|
||||||
|
{
|
||||||
|
uint_least8_t direction[DIRECTION_LENGTH];
|
||||||
|
for(uint_least8_t i = 0; i < DIRECTION_LENGTH; ++i) direction[i] = i;
|
||||||
|
moveSliding(direction, LENGTH(direction), addmoveCtx, src, piece);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PAWN:
|
||||||
|
{
|
||||||
|
uint_least8_t pawnDirection = piece.color == WHITE ? NORTH : SOUTH;
|
||||||
|
{
|
||||||
|
uint_least8_t directions[] = {EAST, WEST};
|
||||||
|
for(uint_least8_t j = 0; j < LENGTH(directions); ++j) {
|
||||||
|
uint_least8_t dst = src + DIRECTION_MODIFIER[directions[j]] + DIRECTION_MODIFIER[pawnDirection];
|
||||||
|
if(getDirectionOffset(src, directions[j]) >= 1) {
|
||||||
|
const struct piece_t pieceOtherColor = {ALL_PIECES, !piece.color};
|
||||||
|
if(bitboardGet(board, pieceOtherColor, dst)) {
|
||||||
|
movePawn(addmoveCtx, piece.color, src, dst, promotion, 0);
|
||||||
|
}
|
||||||
|
else if(dst == gameState.enPassantTo) {
|
||||||
|
movePawn(addmoveCtx, piece.color, src, dst, promotion, EN_PASSANT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint_least8_t dst = src + DIRECTION_MODIFIER[pawnDirection];
|
||||||
|
if(!bitboardGetAllPieces(board, dst)) {
|
||||||
|
movePawn(addmoveCtx, piece.color, src, dst, promotion, 0);
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case KNIGHT:
|
||||||
|
{
|
||||||
|
defineKnightMoves;
|
||||||
|
addPreGenMoves(addmoveCtx, KNIGHT_MOVES, src, piece);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return movesLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint_least8_t validMoves(struct gameState_t gameState, struct move_t *moves) {
|
||||||
|
uint_least8_t movesLength = 0;
|
||||||
|
for(uint_least8_t pieceType = KING; pieceType < PIECES_LENGTH; ++pieceType) {
|
||||||
|
const struct piece_t piece = {pieceType, gameState.color};
|
||||||
|
uint_least8_t field;
|
||||||
|
for(uint_least64_t pieceMask = bitboardGetMask(gameState.board, piece);
|
||||||
|
pieceMask != 0; pieceMask = bitsetClear(pieceMask, field)) {
|
||||||
|
field = trailingBits(pieceMask);
|
||||||
|
movesLength += pieceValidMoves(gameState, piece, field, moves + movesLength, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return movesLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
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, 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};
|
||||||
|
bitboardClear(board, srcPiece, move.src);
|
||||||
|
bitboardSet(board, dstPiece, move.dst);
|
||||||
|
if(move.capturedPiece != NOT_SELECTED) {
|
||||||
|
const struct piece_t capturedPiece = {move.capturedPiece, !gameState.color};
|
||||||
|
bitboardClear(board, capturedPiece, getCapturePos(move.src, move.dst, move.spezialMove));
|
||||||
|
}
|
||||||
|
castleMoveRook(board, move.spezialMove, gameState.color, false);
|
||||||
|
setCastleInfo(gameState.canCastle, gameState.color, move);
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
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;
|
||||||
|
}
|
||||||
146
src/common/postCodeGen/bitboard.c
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
#include "chess/zobristConsts.h"
|
||||||
|
#include <ctype.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/types.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#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];
|
||||||
|
}
|
||||||
|
|
||||||
|
void bitboardSetMask(uint_least64_t *board, struct piece_t piece, uint_least64_t value) {
|
||||||
|
board[piece.color * PIECES_LENGTH + piece.type] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint_least64_t bitboardMaskAllPieces(const uint_least64_t *board) {
|
||||||
|
const struct piece_t blackPiece = {ALL_PIECES, BLACK};
|
||||||
|
const struct piece_t whitePiece = {ALL_PIECES, WHITE};
|
||||||
|
return bitboardGetMask(board, blackPiece) | bitboardGetMask(board, whitePiece);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bitboardGetAllPieces(const uint_least64_t *board, uint_least8_t i) {
|
||||||
|
return bitsetGet(bitboardMaskAllPieces(board), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool bitboardGet(const uint_least64_t *board, struct piece_t piece, uint_least8_t i) {
|
||||||
|
return bitsetGet(bitboardGetMask(board, piece), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bitboardClearHelper(uint_least64_t *board, struct piece_t piece, uint_least8_t i) {
|
||||||
|
const uint_least64_t newMask = bitsetClear(bitboardGetMask(board, piece), i);
|
||||||
|
bitboardSetMask(board, piece, newMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bitboardClear(uint_least64_t *board, struct piece_t piece, uint_least8_t i) {
|
||||||
|
const struct piece_t allPiece = {ALL_PIECES, piece.color};
|
||||||
|
bitboardClearHelper(board, allPiece, i);
|
||||||
|
bitboardClearHelper(board, piece, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void bitboardSetHelper(uint_least64_t *board, struct piece_t piece, uint_least8_t i) {
|
||||||
|
const uint_least64_t newMask = bitsetSet(bitboardGetMask(board, piece), i);
|
||||||
|
bitboardSetMask(board, piece, newMask);
|
||||||
|
}
|
||||||
|
|
||||||
|
void bitboardSet(uint_least64_t *board, struct piece_t piece, uint_least8_t i) {
|
||||||
|
const struct piece_t allPiece = {ALL_PIECES, piece.color};
|
||||||
|
bitboardSetHelper(board, allPiece, i);
|
||||||
|
bitboardSetHelper(board, piece, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct piece_t pieceAtField(const uint_least64_t *board, uint_least8_t i) {
|
||||||
|
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};
|
||||||
|
if(bitboardGet(board, piece, i)) {
|
||||||
|
return piece;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
const struct piece_t pieceError = {NOT_SELECTED, BLACK};
|
||||||
|
return pieceError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
24
src/common/postCodeGen/magic.c
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/types.h>
|
||||||
|
#include <chess/generated/moveConsts.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) {
|
||||||
|
defineDirectionOffset;
|
||||||
|
const uint_least8_t *localDirectionOffset = DIRECTION_OFFSET + field * DIRECTION_LENGTH;
|
||||||
|
uint_least64_t movementMask = 0;
|
||||||
|
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 < localDirectionOffset[direction[j]]; ++i, currentField += modifier) {
|
||||||
|
movementMask = bitsetSet(movementMask, currentField);
|
||||||
|
if(bitsetGet(blockMask, currentField)) break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return movementMask;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint_least64_t* getMagicAttackPtr(const uint_least64_t mask, const struct magic_t magic) {
|
||||||
|
return magic.attackTable + ((mask * magic.magicNumber) >> magic.shift);
|
||||||
|
}
|
||||||
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
@ -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
@ -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;
|
||||||
|
}
|
||||||
131
src/findMagicNumber.c
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
#include <chess/types.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/print.h>
|
||||||
|
#include <inttypes.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 randFewBits() {
|
||||||
|
return rand_64() & rand_64() & rand_64();
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint_least8_t countOnes(uint_least64_t n) {
|
||||||
|
int count = 0;
|
||||||
|
while (n != 0) {
|
||||||
|
n = n & (n - 1);
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void printerMagic(FILE *file, struct magic_t magic) {
|
||||||
|
fprintf(file, "{ 0, %" PRIuLEAST64 "u, %" PRIuLEAST64 "u, %" PRIuLEAST8 "}", magic.mask, magic.magicNumber, magic.shift);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool tryMagicNum(uint_least8_t field, struct magic_t *magic, uint_least64_t magicNumber,
|
||||||
|
const uint_least64_t *preClacAttack, const uint_least8_t *direction) {
|
||||||
|
preClacAttack += field * MAX_SIZE;
|
||||||
|
bool usedMagic = false;
|
||||||
|
if(countOnes((magic->mask * magicNumber) & 0xFF00000000000000ULL) < 6) return false;
|
||||||
|
for(uint_least8_t shift = magic->shift + 1;; ++shift) {
|
||||||
|
uint_least64_t attackTable[MAX_SIZE];
|
||||||
|
for(size_t i = 0; i < LENGTH(attackTable); ++i) {
|
||||||
|
attackTable[i] = UINT_LEAST64_MAX;
|
||||||
|
}
|
||||||
|
bool validMagic = true;
|
||||||
|
uint_least64_t pieceArrangement = 0;
|
||||||
|
size_t j = 0;
|
||||||
|
do {
|
||||||
|
const uint_least64_t i = (pieceArrangement * magicNumber) >> shift;
|
||||||
|
const uint_least64_t storedAttack = attackTable[i];
|
||||||
|
const uint_least64_t calcAttack = preClacAttack[j];
|
||||||
|
if(storedAttack != UINT_LEAST64_MAX && storedAttack != calcAttack) {
|
||||||
|
validMagic = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
attackTable[i] = calcAttack;
|
||||||
|
pieceArrangement = (pieceArrangement - magic->mask) & magic->mask;
|
||||||
|
++j;
|
||||||
|
} while(pieceArrangement != 0);
|
||||||
|
if(validMagic) {
|
||||||
|
magic->shift = shift;
|
||||||
|
magic->magicNumber = magicNumber;
|
||||||
|
usedMagic = true;
|
||||||
|
}
|
||||||
|
else break;
|
||||||
|
}
|
||||||
|
return usedMagic;
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
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;
|
||||||
|
do {
|
||||||
|
attackMask[i] = slidingMovementMask(direction, directionLength, field, pieceArrangement);
|
||||||
|
pieceArrangement = (pieceArrangement - magic->mask) & magic->mask;
|
||||||
|
++i;
|
||||||
|
} while(pieceArrangement != 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
struct magic_t rookMagicTable[TOTAL_BOARD_SIZE] = {};
|
||||||
|
struct magic_t bishopMagicTable[TOTAL_BOARD_SIZE] = {};
|
||||||
|
uint_least64_t *attackMask = malloc(ATTACK_TABLE_LENGTH * sizeof *attackMask);
|
||||||
|
uint_least64_t *rookAttackMask = attackMask;
|
||||||
|
uint_least64_t *bishopAttackMask = attackMask + TOTAL_BOARD_SIZE * MAX_SIZE;
|
||||||
|
for(uint_least8_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
|
||||||
|
initMagicField(rookAttackMask, rookMagicTable, field, ROOK_DIRECTION, LENGTH(ROOK_DIRECTION));
|
||||||
|
initMagicField(bishopAttackMask, bishopMagicTable, field, BISHOP_DIRECTION, LENGTH(BISHOP_DIRECTION));
|
||||||
|
}
|
||||||
|
srand(time(NULL));
|
||||||
|
for(;;) {
|
||||||
|
uint_least64_t magicNumber = randFewBits();
|
||||||
|
bool isMagic = false;
|
||||||
|
for(uint_least8_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
|
||||||
|
{
|
||||||
|
struct magic_t *magic = rookMagicTable + field;
|
||||||
|
isMagic |= tryMagicNum(field, magic, magicNumber, rookAttackMask, ROOK_DIRECTION);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
struct magic_t *magic = bishopMagicTable + field;
|
||||||
|
isMagic |= tryMagicNum(field, magic, magicNumber, bishopAttackMask, BISHOP_DIRECTION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(isMagic) {
|
||||||
|
FILE *file = fopen("src/chess/magicNumber.h", "w");
|
||||||
|
if (file == NULL) {
|
||||||
|
perror("Error opening file");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
fprintf(file, "#include <chess/types.h>\n");
|
||||||
|
fdefineArray(file, "static struct magic_t rookMagic", printerMagic, rookMagicTable);
|
||||||
|
fdefineArray(file, "static struct magic_t bishopMagic", printerMagic, bishopMagicTable);
|
||||||
|
fclose(file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
132
src/generateCode/moveConsts.c
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <chess/types.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <inttypes.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);
|
||||||
|
fprintArray(file, printerull, moveDst.dst);
|
||||||
|
fprintf(file, "}");
|
||||||
|
}
|
||||||
|
|
||||||
|
static long long min(long long a, long long b) {
|
||||||
|
return a < b ? a : b;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void genCheckMask(const struct moveDst_t *moveDst, uint_least64_t *checkMask) {
|
||||||
|
for(uint_least8_t i = 0; i < TOTAL_BOARD_SIZE; ++i) {
|
||||||
|
uint_least64_t mask = 0;
|
||||||
|
const uint_least8_t *dst = moveDst[i].dst;
|
||||||
|
for(uint_least8_t j = 0; j < moveDst[i].length; ++j) {
|
||||||
|
mask = bitsetSet(mask, dst[j]);
|
||||||
|
}
|
||||||
|
checkMask[i] = mask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main() {
|
||||||
|
printf("#ifndef CHESS_GENERATED_MOVE_CONSTS_H\n"
|
||||||
|
"#define CHESS_GENERATED_MOVE_CONSTS_H\n"
|
||||||
|
"#include <stdint.h>\n"
|
||||||
|
"#include <chess/types.h>\n"
|
||||||
|
"const static uint_least8_t ROOK_DIRECTION[] = {NORTH, SOUTH, EAST, WEST};\n"
|
||||||
|
"const static uint_least8_t BISHOP_DIRECTION[] = {NORTHEAST, NORTHWEST, SOUTHEAST, SOUTHWEST};\n"
|
||||||
|
);
|
||||||
|
uint_least8_t directionOffset[TOTAL_BOARD_SIZE * DIRECTION_LENGTH];
|
||||||
|
for(uint_least16_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
|
||||||
|
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;
|
||||||
|
offsetField[NORTHWEST] = min(file, rank);
|
||||||
|
offsetField[EAST] = BOARD_SIZE - file - 1;
|
||||||
|
offsetField[SOUTH] = BOARD_SIZE - rank - 1;
|
||||||
|
offsetField[SOUTHEAST] = min(offsetField[SOUTH], offsetField[EAST]);
|
||||||
|
offsetField[SOUTHWEST] = min(offsetField[SOUTH], file);
|
||||||
|
offsetField[NORTHEAST] = min(rank, offsetField[EAST]);
|
||||||
|
}
|
||||||
|
defineArray("#define defineDirectionOffset const static uint_least8_t DIRECTION_OFFSET", printerull, directionOffset);
|
||||||
|
int_least8_t directionModifier[DIRECTION_LENGTH];
|
||||||
|
directionModifier[NORTH] = -BOARD_SIZE;
|
||||||
|
directionModifier[SOUTH] = BOARD_SIZE;
|
||||||
|
directionModifier[WEST] = -1;
|
||||||
|
directionModifier[EAST] = 1;
|
||||||
|
directionModifier[SOUTHEAST] = directionModifier[SOUTH] + directionModifier[EAST];
|
||||||
|
directionModifier[SOUTHWEST] = directionModifier[SOUTH] + directionModifier[WEST];
|
||||||
|
directionModifier[NORTHEAST] = directionModifier[NORTH] + directionModifier[EAST];
|
||||||
|
directionModifier[NORTHWEST] = directionModifier[NORTH] + directionModifier[WEST];
|
||||||
|
defineArray("const static int_least8_t DIRECTION_MODIFIER", printerll, directionModifier);
|
||||||
|
{
|
||||||
|
uint_least64_t knightCheck[TOTAL_BOARD_SIZE];
|
||||||
|
struct moveDst_t knightMoves[TOTAL_BOARD_SIZE] = {};
|
||||||
|
for(uint_least8_t file = 0; file < BOARD_SIZE; ++file) {
|
||||||
|
for(uint_least8_t rank = 0; rank < BOARD_SIZE; ++rank) {
|
||||||
|
uint_least8_t field = rank * BOARD_SIZE + file;
|
||||||
|
const uint_least8_t *offsetField = directionOffset + field * DIRECTION_LENGTH;
|
||||||
|
const uint_least8_t direction[] = {
|
||||||
|
SOUTH, EAST, // each line is a pair of directions
|
||||||
|
EAST, SOUTH,
|
||||||
|
SOUTH, WEST,
|
||||||
|
WEST, SOUTH,
|
||||||
|
NORTH, WEST,
|
||||||
|
WEST, NORTH,
|
||||||
|
NORTH, EAST,
|
||||||
|
EAST, NORTH
|
||||||
|
};
|
||||||
|
struct moveDst_t *moveDst = knightMoves + field;
|
||||||
|
for(uint_least8_t i = 0; i < LENGTH(direction); i += 2) {
|
||||||
|
if(offsetField[direction[i]] >= 2 && offsetField[direction[i + 1]] >= 1) {
|
||||||
|
const uint_least8_t dst = field + 2 * directionModifier[direction[i]] + directionModifier[direction[i + 1]];
|
||||||
|
moveDst->dst[moveDst->length++] = dst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genCheckMask(knightMoves, knightCheck);
|
||||||
|
defineArray("#define defineKnightCheck const static uint_least64_t KNIGHT_CHECK", printerull, knightCheck);
|
||||||
|
defineArray("#define defineKnightMoves const static struct moveDst_t KNIGHT_MOVES", printerMoveDst, knightMoves);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint_least64_t kingCheck[TOTAL_BOARD_SIZE];
|
||||||
|
struct moveDst_t kingMoves[TOTAL_BOARD_SIZE] = {};
|
||||||
|
for(uint_least8_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
|
||||||
|
struct moveDst_t *moveDst = kingMoves + field;
|
||||||
|
for(uint_least8_t direction = 0; direction < DIRECTION_LENGTH; ++direction) {
|
||||||
|
if(directionOffset[field * DIRECTION_LENGTH + direction] >= 1) {
|
||||||
|
moveDst->dst[moveDst->length++] = field + directionModifier[direction];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
genCheckMask(kingMoves, kingCheck);
|
||||||
|
defineArray("#define defineKingCheck const static uint_least64_t KING_CHECK", printerull, kingCheck);
|
||||||
|
defineArray("#define defineKingMoves const static struct moveDst_t KING_MOVES", printerMoveDst, kingMoves);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
uint_least64_t pawnCheck[2 * TOTAL_BOARD_SIZE];
|
||||||
|
for(uint_least8_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
|
||||||
|
for(uint_least8_t color = BLACK; color <= WHITE; ++color) {
|
||||||
|
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) {
|
||||||
|
pawnCheck[2 * field + color] = 0;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const uint_least8_t directions[] = {EAST, WEST};
|
||||||
|
for(uint_least8_t j = 0; j < LENGTH(directions); ++j) {
|
||||||
|
if(fieldDirectionOffset[directions[j]] >= 1) {
|
||||||
|
const uint_least8_t dst = field + directionModifier[directions[j]] + directionModifier[pawnDirection];
|
||||||
|
checkMask = bitsetSet(checkMask, dst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pawnCheck[2 * field + color] = checkMask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineArray("#define definePawnCheck const static uint_least64_t PAWN_CHECK", printerull, pawnCheck);
|
||||||
|
}
|
||||||
|
printf("#endif\n");
|
||||||
|
}
|
||||||
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
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
71
test/moveGen.c
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
#include "chess/types.h"
|
||||||
|
#include <chess/move.h>
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <chess/bitboard.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <chess/print.h>
|
||||||
|
|
||||||
|
struct perf_t {
|
||||||
|
char FEN[256];
|
||||||
|
uint_least8_t depth;
|
||||||
|
uint_least64_t nodes;
|
||||||
|
};
|
||||||
|
|
||||||
|
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(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
|
||||||
|
{"r3k2r/p1ppqpb1/bn2pnp1/3PN3/1p2P3/2N2Q1p/PPPBBPPP/R3K2R w KQkq 0 1", 4, 4085603}, // 1
|
||||||
|
{"8/2p5/3p4/KP5r/1R3p1k/8/4P1P1/8 w - 0 1", 5, 674624}, // 2
|
||||||
|
{"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) {
|
||||||
|
test(testPos[i], i);
|
||||||
|
}
|
||||||
|
}
|
||||||