Compare commits

...

5 Commits

Author SHA1 Message Date
dddc0a4e86 not working: check 2024-03-17 22:04:13 +01:00
2e813e5744 gen magic Number 2024-03-17 10:41:26 +01:00
e21fabb94c generate Direction Consts 2024-03-13 11:03:29 +01:00
79419d2007 bitboard 2024-03-12 17:45:02 +01:00
5b4dbee026 sudo legal moves 2024-03-10 20:11:14 +01:00
19 changed files with 1181 additions and 218 deletions

View File

@ -1,13 +1,44 @@
cmake_minimum_required(VERSION 3.18)
project(chess C)
#include_directories("lib/acl/include")
#file(GLOB SOURCES "src/*.c" "lib/*/src/*.c")
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
include_directories(include)
file(GLOB SOURCES "src/chess/*.c")
file(GLOB LIB_SOURCES "lib/chess/*.c")
add_executable(genMoveConsts src/generateCode/moveConsts.c ${LIB_SOURCES})
add_custom_command(
OUTPUT moveConsts.h # Output file from code generation
COMMAND genMoveConsts > include/chess/generated/moveConsts.h
DEPENDS genMoveConsts # Depends on the code generation executable
)
add_custom_target(generateCode DEPENDS moveConsts.h)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK4 REQUIRED IMPORTED_TARGET gtk4)
pkg_check_modules(LIBRSVG REQUIRED IMPORTED_TARGET librsvg-2.0)
add_executable(${PROJECT_NAME} ${SOURCES})
add_executable(findMagicNumber src/findMagicNumber.c ${LIB_SOURCES})
add_dependencies(findMagicNumber generateCode)
add_executable(${PROJECT_NAME} ${SOURCES} ${LIB_SOURCES})
set_property(TARGET findMagicNumber PROPERTY C_STANDARD 99)
set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99)
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})
set_property(TARGET chessNoComputer PROPERTY C_STANDARD 99)
target_link_libraries(chessNoComputer PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG)
add_dependencies(chessNoComputer generateCode)
target_compile_definitions(chessNoComputer PUBLIC NO_COMPUTER)

View File

@ -1,3 +1,8 @@
# chess
A chess engine
A chess engine
# sources
https://www.chessprogramming.org/Magic_Bitboards
https://www.chessprogramming.org/Looking_for_Magics

15
include/chess/bitboard.h Normal file
View File

@ -0,0 +1,15 @@
#include <stdbool.h>
#include <stdint.h>
#include <chess/types.h>
bool bitsetGet(uint_least64_t bitset, uint_least8_t i);
uint_least64_t bitsetClear(uint_least64_t bitset, uint_least8_t i);
uint_least64_t bitsetSet(uint_least64_t bitset, uint_least8_t i);
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);

View File

10
include/chess/magic.h Normal file
View 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

25
include/chess/print.h Normal file
View File

@ -0,0 +1,25 @@
#include <stdio.h>
#define fprintArray(file, printer, arr) \
do { \
fprintf(file, "{"); \
for(size_t i = 0; i < LENGTH(arr); ++i) { \
printer(file, arr[i]); \
if (i < LENGTH(arr) - 1) { \
fprintf(file, ", "); \
} \
} \
fprintf(file, " }"); \
} while(0)
#define fdefineArray(file, declaration, printer, arr) \
do { \
fprintf(file, "%s[%zu] = ", declaration, LENGTH(arr)); \
fprintArray(file, printer, arr); \
fprintf(file, ";\n"); \
} while(0)
#define defineArray(declaration, printer, arr) fdefineArray(stdout, declaration, printer, arr)
void printerll(FILE *file, long long num);
void printerull(FILE *file, unsigned long long num);

49
include/chess/types.h Normal file
View File

@ -0,0 +1,49 @@
#ifndef CHESS_TYPES_H
#define CHESS_TYPES_H
#include <stdbool.h>
#include <stdint.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
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 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;
};
#endif

Submodule lib/acl deleted from a83de1b97b

75
lib/chess/bitboard.c Normal file
View File

@ -0,0 +1,75 @@
#include <bits/stdint-least.h>
#include <stdbool.h>
#include <stdint.h>
#include <chess/types.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);
}
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;
}
}

28
lib/chess/magic.c Normal file
View File

@ -0,0 +1,28 @@
#include <stdint.h>
#include <chess/types.h>
#include <chess/generated/moveConsts.h>
#include <chess/bitboard.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]] - 1; ++i, currentField += modifier) {
movementMask = bitsetSet(movementMask, currentField);
if(bitsetGet(blockMask, currentField)) break;
}
}
return movementMask;
}
uint_least64_t getMagicI(const uint_least64_t mask, const struct magic_t magic) {
return (mask * magic.magicNumber) >> magic.shift;
}
uint_least64_t* getMagicAttackPtr(const uint_least64_t mask, const struct magic_t magic) {
return magic.attackTable + getMagicI(mask, magic);
}

9
lib/chess/print.c Normal file
View File

@ -0,0 +1,9 @@
#include <stdio.h>
void printerll(FILE *file, long long num) {
fprintf(file, "%lld", num);
}
void printerull(FILE *file, unsigned long long num) {
fprintf(file, "%lluu", num);
}

270
src/chess/main.c Normal file
View File

@ -0,0 +1,270 @@
#include <ctype.h>
#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 "move.h"
#include <chess/bitboard.h>
#define drawPiece(file, rank, piece) do { \
const RsvgRectangle rect = {xOffset + (file) * fieldSize, yOffset + (rank) * fieldSize, fieldSize, fieldSize};\
rsvg_handle_render_document(piecesSvg[pieceToSvgI(piece)], cr, &rect, NULL); \
} while(0)
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 bool turn = WHITE;
static uint_least8_t pieceToSvgI(struct piece_t piece) {
return piece.color == WHITE ? piece.type | 8 : piece.type;
}
static void selectCircle(cairo_t *cr, const struct drawData_t *drawData, uint_least8_t field, double radius) {
uint_least8_t file = field % BOARD_SIZE;
uint_least8_t rank = field / BOARD_SIZE;
double xOffset = drawData->xOffset;
double yOffset = drawData->yOffset;
double fieldSize = drawData->fieldSize;
cairo_arc(cr, xOffset + (file + 0.5) * fieldSize, yOffset + (rank + 0.5) * fieldSize, radius, 0, 2 * M_PI);
}
static struct gameState_t userMove(struct gameState_t gameState, struct move_t move) {
gameState = makeMove(gameState, move);
#ifdef NO_COMPUTER
turn = !turn;
#else
gameState = computerMove(gameState);
#endif
return gameState;
}
static uint_least8_t promotionUiPos(uint_least8_t dst) {
return dst >= 1 ? dst - 1 : 0;
}
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 uint_least64_t *board = drawData->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;
cairo_rectangle(cr, xOffset + file * fieldSize, yOffset + rank * fieldSize, fieldSize, fieldSize);
if((file + rank) & 1) cairo_set_source_rgb(cr, 0.46274, 0.5882, 0.3373);
else cairo_set_source_rgb(cr, 0.9333, 0.9333, 0.8235);
cairo_fill(cr);
if(bitboardGetAllPieces(board, field)) {
const struct piece_t piece = pieceAtField(board, rank * BOARD_SIZE + file);
drawPiece(file, rank, 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);
if(uiPos + 3 >= BOARD_SIZE) uiPos = BOARD_SIZE - LENGTH(PROMOTION_PIECES);
cairo_set_source_rgb(cr, 0.5, 0.5, 0.5);
cairo_rectangle(cr, xOffset + uiPos * fieldSize, yOffset, 4 * fieldSize, fieldSize);
cairo_fill(cr);
for(uint_least8_t i = 0; i < LENGTH(PROMOTION_PIECES); ++i) {
const struct piece_t piece = {PROMOTION_PIECES[i], true};
drawPiece(uiPos + i, 0, piece);
}
}
}
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;
if(drawData->selectDst == NOT_SELECTED) {
const struct piece_t allPiece = {ALL_PIECES, turn};
if(bitboardGet(board, allPiece, field)) {
// deaktivated 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, field);
if(piece.type == PAWN && field < BOARD_SIZE) { // 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(rank != 0) return;
const uint_least8_t uiPos = promotionUiPos(drawData->selectDst);
const uint_least8_t dstPiece = PROMOTION_PIECES[file - uiPos];
const uint_least8_t capturedPiece = pieceAtField(board, drawData->selectDst).type;
const struct move_t move = {drawData->clickedPiece, drawData->selectDst, 0, PAWN, dstPiece};
*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[16];
static uint_least64_t board[2 * PIECES_LENGTH] = {};
static struct gameState_t gameState = {board, WHITE, {{true, true}, {true, true}}, 0, NOT_SELECTED};
{
struct piece_t piece;
const char startFEN[] = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
uint_least8_t field = 0;
for(const char *c = startFEN; *c != ' '; ++c) {
switch(tolower(*c)) {
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(*c)) {
field += *c - '0';
}
continue;
}
if(isalpha(*c)) piece.color = isupper(*c);
bitboardSet(board, piece, field);
++field;
}
}
{
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;
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;
}

357
src/chess/move.c Normal file
View File

@ -0,0 +1,357 @@
#include <stdbool.h>
#include <stdint.h>
#include <chess/types.h>
#include "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>
#define trailingBits(num) __builtin_ctzll(num)
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 getDirectionOffset(uint_least8_t field, uint_least8_t direction) {
defineDirectionOffset;
return DIRECTION_OFFSET[field * DIRECTION_LENGTH + direction];
}
static uint_least8_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;
}
void initMagicTable() {
size_t size = getTotalMagicSize(rookMagic) + getTotalMagicSize(bishopMagic);
uint_least64_t *attackTable = malloc(size * sizeof *attackTable);
if(attackTable == NULL) {
perror("failed to allocate memory: ");
exit(1);
}
for(uint_least8_t field = 0; field < TOTAL_BOARD_SIZE; ++field) {
struct magic_t *magic = rookMagic + field;
attackTable += getMagicSize(*magic);
magic->attackTable = attackTable;
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(ROOK_DIRECTION, LENGTH(ROOK_DIRECTION), field, pieceArangement);
*getMagicAttackPtr(pieceArangement, *magic) = attackMask;
pieceArangement = (pieceArangement - magic->mask) & magic->mask;
} while(pieceArangement != 0);
}
}
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);
}
static uint_least8_t getCastleRankI(bool color) {
if(color == WHITE) {
return (BOARD_SIZE - 1) * BOARD_SIZE;
}
return 0;
}
static void castleMoveRook(uint_least64_t *board, uint_least8_t spezialMove, bool color, bool undo) {
if(spezialMove == SHORT_CASTLE || spezialMove == LONG_CASTLE) {
const uint_least8_t rookSrc = getCastleRankI(color) + (spezialMove == SHORT_CASTLE ? BOARD_SIZE - 1 : 0);
const uint_least8_t rookDst = getCastleRankI(color) + (spezialMove == SHORT_CASTLE ? 5 : 3);
const struct piece_t rook = {ROOK, color};
bitboardSet(board, rook, undo ? rookSrc : rookDst);
bitboardClear(board, rook, undo ? rookDst : rookSrc);
}
}
static void undoMove(uint_least64_t *board, struct move_t move, bool color) {
const struct piece_t srcPiece = {move.srcPiece, color};
const struct piece_t dstPiece = {move.dstPiece, color};
bitboardSet(board, srcPiece, move.src);
bitboardClear(board, dstPiece, move.dst);
if(move.capturedPiece != NOT_SELECTED) {
const struct piece_t piece = {move.capturedPiece, !color};
bitboardSet(board, piece, move.dst);
}
castleMoveRook(board, move.spezialMove, color, true);
}
static void addMove(struct addMoveCtx_t ctx, struct piece_t movedPiece, uint_least8_t src,
uint_least8_t dst, uint_least8_t spezialMove) {
uint_least64_t *board = ctx.gameState.board;
uint_least8_t *movesLength = ctx.movesLength;
uint_least8_t capturedPiece = NOT_SELECTED;
const struct piece_t pieceAll = {ALL_PIECES, movedPiece.color};
if(bitboardGet(board, pieceAll, dst)) return;
{
const struct piece_t pieceAllOtherColor = {ALL_PIECES, !movedPiece.color};
const uint_least8_t capturePos = getCapturePos(src, dst, spezialMove);
if(bitboardGet(board, pieceAllOtherColor, capturePos)) {
for(uint_least8_t pieceType = QUEEN; pieceType < PIECES_LENGTH; ++pieceType) {
const struct piece_t piece = {pieceType, !movedPiece.color};
if(bitboardGet(board, piece, capturePos)) {
capturedPiece = pieceType;
break;
}
}
}
}
const struct move_t move = {src, dst, spezialMove, movedPiece.type, movedPiece.type, capturedPiece};
makeMove(ctx.gameState, move);
{
const bool color = ctx.gameState.color;
const struct piece_t king = {KING, color};
if(!inCheck(board, bitboardGetMask(board, king), color)) {
ctx.moves[*movesLength] = move;
++*movesLength;
}
undoMove(board, 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);
}
}
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);
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); // always adds new move
moves[*movesLength].dstPiece = promotionPiece;
}
}
else {
addMove(ctx, piece, src, dst, spezialMove);
}
}
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 = piece.color == WHITE ? BOARD_SIZE * (BOARD_SIZE - 1) : 0;
const uint_least64_t shortCastleMask = (uint_least64_t)0b11 << (5 + rankShift);
const uint_least64_t longCastleMask = (uint_least64_t)0b111 << (1 + rankShift);
if(canCastle.shortCastle && !(allPiecesMask & shortCastleMask) && !inCheck(board, shortCastleMask, piece.color)) {
addMove(addmoveCtx, piece, src, src + 2 * DIRECTION_MODIFIER[EAST], SHORT_CASTLE);
}
if(canCastle.longCastle && !(allPiecesMask & longCastleMask) && !inCheck(board, longCastleMask, piece.color)) {
addMove(addmoveCtx, piece, src, src + 2 * DIRECTION_MODIFIER[WEST], LONG_CASTLE);
}
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(src / BOARD_SIZE == (piece.color == WHITE ? BOARD_SIZE - 2 : 1)) {
dst += DIRECTION_MODIFIER[pawnDirection];
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, bool color, 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, 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 setCastleInfo(struct castle_t *canCastle, uint_least8_t posOnRank) {
switch(posOnRank) {
case 0:
canCastle->longCastle = false;
break;
case BOARD_SIZE - 1:
canCastle->shortCastle = false;
break;
}
}
struct gameState_t makeMove(struct gameState_t gameState, 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);
{
struct castle_t *canCastleCurrentPlayer = gameState.canCastle + gameState.color;
if(canCastleCurrentPlayer->longCastle || canCastleCurrentPlayer->shortCastle) {
if(move.dstPiece == KING) {
canCastleCurrentPlayer->longCastle = false;
canCastleCurrentPlayer->shortCastle = false;
}
else {
setCastleInfo(canCastleCurrentPlayer, move.src - getCastleRankI(gameState.color));
}
}
}
{
// if the rook is captured
struct castle_t *canCastleOther = gameState.canCastle + !gameState.color;
if(canCastleOther->longCastle || canCastleOther->shortCastle) {
setCastleInfo(canCastleOther, move.dst - getCastleRankI(!gameState.color));
}
}
{
const int_least8_t dist = (int_least8_t)move.dst - (int_least8_t)move.src;
gameState.enPassantTo = 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;
return gameState;
}
struct gameState_t computerMove(struct gameState_t gameState) {
struct move_t moves[MAX_VALID_MOVES];
validMoves(gameState, false, moves);
return makeMove(gameState, moves[0]);
}

44
src/chess/move.h Normal file
View File

@ -0,0 +1,44 @@
#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 castle_t {
bool shortCastle, longCastle;
};
struct move_t {
uint_least8_t src, dst, spezialMove;
uint_least8_t srcPiece, dstPiece, capturedPiece;
};
struct gameState_t {
uint_least64_t *board;
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
};
void genDirectionConsts();
uint_least8_t validMoves(struct gameState_t gameState, bool color, 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);
struct gameState_t makeMove(struct gameState_t gameState, struct move_t move);
struct gameState_t computerMove(struct gameState_t gameState);
#endif

131
src/findMagicNumber.c Normal file
View 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 <assert.h>
#include <chess/magic.h>
#include <chess/generated/moveConsts.h>
#define MAX_BITS 12
#define MAX_SIZE (1 << MAX_BITS)
#define ATTACK_TABLE_LENGTH (2 * MAX_SIZE * TOTAL_BOARD_SIZE)
static uint_least64_t rand_64() {
uint_least64_t u1, u2, u3, u4;
u1 = (uint_least64_t)(rand()) & 0xFFFF; u2 = (uint_least64_t)(rand()) & 0xFFFF;
u3 = (uint_least64_t)(rand()) & 0xFFFF; u4 = (uint_least64_t)(rand()) & 0xFFFF;
return u1 | (u2 << 16) | (u3 << 32) | (u4 << 48);
}
static uint_least64_t randFewBits() {
return rand_64() & rand_64() & rand_64();
}
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];
//assert(slidingMovementMask(direction, 4, field, pieceArrangement) == calcAttack);
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) {
struct magic_t *magic = magicTable + field;
attackMask += field * MAX_SIZE;
magic->mask = slidingMovementMask(direction, directionLength, field, 0);
magic->shift = 64 - MAX_BITS - 1;
uint_least64_t pieceArrangement = 0;
size_t i = 0;
do {
assert(i < MAX_SIZE);
attackMask[i] = slidingMovementMask(direction, directionLength, field, pieceArrangement);
assert(countOnes(attackMask[i]) <= 12);
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);
}
}
}

View File

@ -0,0 +1,128 @@
#include <stdio.h>
#include <chess/types.h>
#include <stdint.h>
#include <inttypes.h>
#include <chess/bitboard.h>
#include <chess/print.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, 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 = field % BOARD_SIZE;
uint_least8_t rank = field / BOARD_SIZE;
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 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 uint_least64_t KNIGHT_CHECK", printerull, knightCheck);
defineArray("#define defineKnightMoves const 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 uint_least64_t KING_CHECK", printerull, kingCheck);
defineArray("#define defineKingMoves const 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) continue;
const uint_least8_t directions[] = {EAST, WEST};
for(uint_least8_t j = 0; j < LENGTH(directions); ++j) {
uint_least8_t dst = field + directionModifier[directions[j]] + directionModifier[pawnDirection];
if(fieldDirectionOffset[directions[j]] >= 1) {
checkMask = bitsetSet(checkMask, dst);
}
}
pawnCheck[2 * field + color] = checkMask;
}
}
defineArray("#define definePawnCheck const uint_least64_t PAWN_CHECK", printerull, pawnCheck);
}
printf("#endif\n");
}

View File

@ -1,131 +0,0 @@
#include <ctype.h>
#include <gtk/gtk.h>
#include <cairo.h>
#include <librsvg/rsvg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include "types.h"
struct drawData_t {
RsvgHandle **piecesSvg;
struct piece_t *chessboard;
};
static uint_least8_t pieceToSvgI(struct piece_t piece) {
return piece.white ? piece.type | 8 : piece.type;
}
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;
const struct drawData_t drawData = *(struct drawData_t*)data;
const struct piece_t *chessboard = drawData.chessboard;
RsvgHandle **piecesSvg = drawData.piecesSvg;
for(int x = 0; x < 8; ++x) {
for(int y = 0; y < 8; ++y) {
cairo_rectangle(cr, xOffset + x * fieldSize, yOffset + y * fieldSize, fieldSize, fieldSize);
if((x + y) & 1) cairo_set_source_rgb(cr, 0.46274, 0.5882, 0.3373);
else cairo_set_source_rgb(cr, 0.9333, 0.9333, 0.8235);
cairo_fill(cr);
const struct piece_t piece = chessboard[x + y * BOARD_SIZE];
if(piece.type) {
const RsvgRectangle rect = {xOffset + x * fieldSize, yOffset + y * fieldSize, fieldSize, fieldSize};
rsvg_handle_render_document(piecesSvg[pieceToSvgI(piece)], cr, &rect, NULL);
}
}
}
}
static void on_click(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) {
g_print("Mouse clicked at (%f, %f)\n", x, y);
}
static void app_activate(GApplication *app, gpointer data) {
GtkWindow *window = GTK_WINDOW(gtk_window_new());
static RsvgHandle *piecesSvg[16];
static struct piece_t chessboard[BOARD_SIZE * BOARD_SIZE] = {};
{
struct piece_t *piece = chessboard;
const char startFEN[] = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
for(const char *c = startFEN; *c != ' '; ++c) {
switch(tolower(*c)) {
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(*c)) piece += *c - '0';
continue;
}
if(isalpha(*c)) piece->white = isupper(*c);
++piece;
}
}
static struct drawData_t drawData = {piecesSvg, chessboard};
{
GtkWidget *drawArea = gtk_drawing_area_new();
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), NULL);
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;
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;
}

View File

@ -1,61 +0,0 @@
#include <stdbool.h>
#include <stdint.h>
#include "types.h"
// 218
struct move_t {
uint_least8_t src, dst;
};
struct addMoveCtx_t {
struct piece_t *board;
struct move_t *moves;
uint_least8_t *movesLength;
};
enum directions {
NORTH, WEST, SOUTH, EAST, DIRECTION_LENGTH
};
static int_least8_t directionOffset[BOARD_SIZE * DIRECTION_LENGTH];
void genDirectionOffset() {
for(uint_least8_t i = 0; i < TOTAL_BOARD_SIZE; ++i) {
uint_least8_t file = i % BOARD_SIZE;
uint_least8_t rank = i / BOARD_SIZE;
directionOffset[i * DIRECTION_LENGTH + NORTH] = rank;
directionOffset[i * DIRECTION_LENGTH + WEST] = file;
directionOffset[i * DIRECTION_LENGTH + EAST] = BOARD_SIZE - file - 1;
directionOffset[i * DIRECTION_LENGTH + SOUTH] = BOARD_SIZE - rank - 1;
}
}
static void addMove(struct addMoveCtx_t ctx, uint_least8_t src, uint_least8_t dst) {
struct piece_t *board = ctx.board;
struct move_t *moves = ctx.moves;
uint_least8_t *movesLength = ctx.movesLength;
if(board[src].white == board[dst].white) return;
moves[*movesLength].src = src;
moves[*movesLength].dst = dst;
++movesLength;
}
void validMoves(struct piece_t *board, bool whiteToMove, struct move_t *moves) {
uint_least8_t movesLength = 0;
struct addMoveCtx_t addmoveCtx = {board, moves, &movesLength};
for(uint_least8_t i = 0; i < TOTAL_BOARD_SIZE; ++i) {
const struct piece_t piece = board[i];
if(piece.type && piece.white == whiteToMove) {
uint_least8_t file = i % BOARD_SIZE;
switch(piece.type) {
case KING:
if(i >= BOARD_SIZE) addMove(addmoveCtx, i, i - BOARD_SIZE);
if(i < 7 * BOARD_SIZE) addMove(addmoveCtx, i, i + BOARD_SIZE);
if(file > 0) addMove(addmoveCtx, i, i - 1);
if(file < BOARD_SIZE - 1) addMove(addmoveCtx, i, i + 1);
case ROOK:
}
}
}
}

View File

@ -1,21 +0,0 @@
#include <stdbool.h>
#include <stdint.h>
#define BOARD_SIZE 8
#define TOTAL_BOARD_SIZE BOARD_SIZE * BOARD_SIZE
#define LENGTH(array) (sizeof array / sizeof *array)
enum pieces {
NOTHING,
KING,
QUEEN,
ROOK,
BISHOP,
KNIGHT,
PAWN
};
struct piece_t {
uint_least8_t type;
bool white;
};