From 5b4dbee026d6aee9aa6badb1eada7ff337501135 Mon Sep 17 00:00:00 2001 From: MrGeorgen Date: Sun, 10 Mar 2024 17:40:49 +0100 Subject: [PATCH] sudo legal moves --- CMakeLists.txt | 19 +++- src/main.c | 165 ++++++++++++++++++++++++++--- src/move.c | 275 ++++++++++++++++++++++++++++++++++++++++++------- src/move.h | 48 +++++++++ src/types.h | 6 ++ 5 files changed, 462 insertions(+), 51 deletions(-) create mode 100644 src/move.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 82e1789..74c6db4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,7 +1,6 @@ cmake_minimum_required(VERSION 3.18) project(chess C) -#include_directories("lib/acl/include") -#file(GLOB SOURCES "src/*.c" "lib/*/src/*.c") +file(GLOB SOURCES "src/*.c") set(CMAKE_EXPORT_COMPILE_COMMANDS ON) find_package(PkgConfig REQUIRED) @@ -11,3 +10,19 @@ pkg_check_modules(LIBRSVG REQUIRED IMPORTED_TARGET librsvg-2.0) add_executable(${PROJECT_NAME} ${SOURCES}) set_property(TARGET ${PROJECT_NAME} PROPERTY C_STANDARD 99) target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG) + +# 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}) +set_property(TARGET chessNoComputer PROPERTY C_STANDARD 99) +target_link_libraries(chessNoComputer PRIVATE PkgConfig::GTK4 PkgConfig::LIBRSVG) +target_compile_definitions(chessNoComputer PUBLIC NO_COMPUTER) diff --git a/src/main.c b/src/main.c index 87eb47e..2b29ea8 100644 --- a/src/main.c +++ b/src/main.c @@ -1,28 +1,76 @@ +#include #include #include #include #include +#include +#include #include #include #include #include "types.h" +#include "move.h" + +#define drawPiece(file, rank, piece) { \ + const RsvgRectangle rect = {xOffset + (file) * fieldSize, yOffset + (rank) * fieldSize, fieldSize, fieldSize};\ + rsvg_handle_render_document(piecesSvg[pieceToSvgI(piece)], cr, &rect, NULL); \ +} struct drawData_t { RsvgHandle **piecesSvg; - struct piece_t *chessboard; + 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}; +#ifdef NO_COMPUTER + static bool NocTurn = true; +#endif + static uint_least8_t pieceToSvgI(struct piece_t piece) { return piece.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 + NocTurn = !NocTurn; +#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; - const struct drawData_t drawData = *(struct drawData_t*)data; - const struct piece_t *chessboard = drawData.chessboard; - RsvgHandle **piecesSvg = drawData.piecesSvg; + struct drawData_t *drawData = (struct drawData_t*)data; + const struct piece_t *chessboard = drawData->gameState->board; + RsvgHandle **piecesSvg = drawData->piecesSvg; + drawData->xOffset = xOffset; + drawData->yOffset = yOffset; + drawData->fieldSize = fieldSize; 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); @@ -31,21 +79,108 @@ static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height, 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); + drawPiece(x, y, 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) { - g_print("Mouse clicked at (%f, %f)\n", x, y); + struct drawData_t *drawData = (struct drawData_t*)data; + struct gameState_t *gameState = drawData->gameState; + struct piece_t *chessboard = 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) { + if(chessboard[field].type && chessboard[field].white +#ifdef NO_COMPUTER + == NocTurn +#endif + ) { + // deaktivated piece by clicking on it again + if(field == drawData->clickedPiece) { + drawData->clickedPiece = NOT_SELECTED; + drawData->movesLength = 0; + } + else { // clicked on new piece + drawData->clickedPiece = field; + drawData->movesLength = pieceValidMoves(*gameState, chessboard[field].white, drawData->moves, field, 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 = chessboard[drawData->clickedPiece]; + 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 struct piece_t piece = {PROMOTION_PIECES[file - uiPos], true}; + const struct move_t move = {drawData->clickedPiece, drawData->selectDst, 0, piece}; + *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 struct piece_t chessboard[BOARD_SIZE * BOARD_SIZE] = {}; + static struct piece_t chessboard[TOTAL_BOARD_SIZE] = {}; + static struct gameState_t gameState = {chessboard, {{true, true,}, {true, true}}, 0, NOT_SELECTED}; { struct piece_t *piece = chessboard; const char startFEN[] = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; @@ -79,15 +214,17 @@ static void app_activate(GApplication *app, gpointer data) { ++piece; } } - static struct drawData_t drawData = {piecesSvg, chessboard}; { + 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), NULL); + g_signal_connect(gesture, "pressed", G_CALLBACK(on_click), &drawData); gtk_widget_add_controller(GTK_WIDGET(window), GTK_EVENT_CONTROLLER(gesture)); } { @@ -123,9 +260,11 @@ int main(int argc, char **argv) { GtkApplication *app; int stat; + genDirectionConsts(); + 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); + g_object_unref(app); return stat; } diff --git a/src/move.c b/src/move.c index 4d354af..1831064 100644 --- a/src/move.c +++ b/src/move.c @@ -1,11 +1,9 @@ +#include #include #include #include "types.h" -// 218 +#include "move.h" -struct move_t { - uint_least8_t src, dst; -}; struct addMoveCtx_t { struct piece_t *board; @@ -14,48 +12,253 @@ struct addMoveCtx_t { }; enum directions { - NORTH, WEST, SOUTH, EAST, DIRECTION_LENGTH + NORTH, NORTHWEST, WEST, SOUTHWEST, SOUTH, SOUTHEAST, EAST, NORTHEAST, DIRECTION_LENGTH }; -static int_least8_t directionOffset[BOARD_SIZE * DIRECTION_LENGTH]; +const static uint_least8_t MAIN_DIRECTION[] = {NORTH, WEST, EAST, SOUTH}; -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 uint_least8_t directionOffset[TOTAL_BOARD_SIZE * DIRECTION_LENGTH]; +static int_least8_t directionModifier[DIRECTION_LENGTH]; + +static uint_least8_t min(uint_least8_t a, uint_least8_t b) { + return a < b ? a : b; +} + +void genDirectionConsts() { + 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]); } + 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]; } -static void addMove(struct addMoveCtx_t ctx, uint_least8_t src, uint_least8_t dst) { +static void addMove(struct addMoveCtx_t ctx, uint_least8_t src, uint_least8_t dst, uint_least8_t spezialMove) { 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; + const struct move_t move = {src, dst, spezialMove, board[src]}; + if(board[dst].type && board[src].white == board[dst].white) return; + ctx.moves[*movesLength] = move; + ++*movesLength; + return; } -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: - - } +static void moveSliding(const uint_least8_t *direction, uint_least8_t directionLength, struct addMoveCtx_t ctx, + uint_least8_t field) { + struct piece_t *board = ctx.board; + + for(uint_least8_t j = 0; j < directionLength; ++j) { + const int_least8_t modifier = directionModifier[direction[j]]; + for(uint_least8_t currentField = field + modifier, i = 0; + i < directionOffset[field * DIRECTION_LENGTH + direction[j]]; ++i, currentField += modifier) { + addMove(ctx, field, currentField, 0); + if(board[currentField].type) break; } } } + +static void movePawn(struct addMoveCtx_t ctx, uint_least8_t src, uint_least8_t dst, + bool promotion, uint_least8_t spezialMove) { + struct piece_t *board = ctx.board; + struct move_t *moves = ctx.moves; + uint_least8_t *movesLength = ctx.movesLength; + bool whiteToMove = board[src].white; + if(promotion && (whiteToMove && dst < BOARD_SIZE || !whiteToMove && dst >= BOARD_SIZE * (BOARD_SIZE - 1))) { + for(uint_least8_t promotionPiece = QUEEN; promotionPiece < PAWN; ++promotionPiece) { + const struct move_t move = {src, dst, spezialMove, {promotionPiece, board[src].white}}; + moves[*movesLength] = move; + ++*movesLength; + } + } + else { + const struct move_t move = {src, dst, spezialMove, {PAWN, board[src].white}}; + moves[*movesLength] = move; + ++*movesLength; + } +} + +uint_least8_t pieceValidMoves(struct gameState_t gameState, bool whiteToMove, struct move_t *moves, + uint_least8_t src, bool promotion) { + struct piece_t *board = gameState.board; + uint_least8_t movesLength = 0; + struct addMoveCtx_t addmoveCtx = {board, moves, &movesLength}; + const uint_least8_t *localDirectionOffset = directionOffset + src * DIRECTION_LENGTH; + const struct piece_t piece = board[src]; + if(piece.type && piece.white == whiteToMove) { + switch(piece.type) { + case KING: + { + const struct castle_t canCastle = gameState.canCastle[whiteToMove]; + for(uint_least8_t direction = 0; direction < DIRECTION_LENGTH; ++direction) { + if(directionOffset[src * DIRECTION_LENGTH + direction] >= 1) { + addMove(addmoveCtx, src, src + directionModifier[direction], 0); + } + } + if(canCastle.shortCastle && board[src + 1].type == NOTHING && board[src + 2].type == NOTHING) { + addMove(addmoveCtx, src, src + 2, SHORT_CASTLE); + } + if(canCastle.longCastle && board[src - 1].type == NOTHING && + board[src - 2].type == NOTHING && board[src - 3].type == NOTHING) { + addMove(addmoveCtx, src, src - 2, LONG_CASTLE); + } + break; + } + case ROOK: + moveSliding(MAIN_DIRECTION, LENGTH(MAIN_DIRECTION), addmoveCtx, src); + break; + case BISHOP: + { + uint_least8_t direction[] = {NORTHWEST, SOUTHWEST, SOUTHEAST, NORTHEAST}; + moveSliding(direction, LENGTH(direction), addmoveCtx, src); + 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); + break; + } + case PAWN: + { + uint_least8_t pawnDirection = whiteToMove ? NORTH : SOUTH; + { + uint_least8_t directions[] = {EAST, WEST}; + for(uint_least8_t j = 0; j < LENGTH(directions); ++j) { + uint_least8_t dst = src + directionModifier[directions[j]] + directionModifier[pawnDirection]; + if(localDirectionOffset[directions[j]] >= 1) { + if(board[dst].type && board[dst].white != whiteToMove) { + movePawn(addmoveCtx, src, dst, promotion, 0); + } + else if(dst == gameState.enPassantTo) { + movePawn(addmoveCtx, src, dst, promotion, EN_PASSANT); + } + } + } + } + { + uint_least8_t dst = src + directionModifier[pawnDirection]; + if(board[dst].type == NOTHING) { + movePawn(addmoveCtx, src, dst, promotion, 0); + if(src / BOARD_SIZE == (whiteToMove ? BOARD_SIZE - 2 : 1) && board[dst].type == NOTHING) { + dst += directionModifier[pawnDirection]; + movePawn(addmoveCtx, src, dst, promotion, 0); + } + } + } + break; + } + case KNIGHT: + { + uint_least8_t direction[][2] = { + {SOUTH, EAST}, + {EAST, SOUTH}, + {SOUTH, WEST}, + {WEST, SOUTH}, + {NORTH, WEST}, + {WEST, NORTH}, + {NORTH, EAST}, + {EAST, NORTH} + }; + for(uint_least8_t i = 0; i < LENGTH(direction); ++i) { + if(localDirectionOffset[direction[i][0]] >= 2 && localDirectionOffset[direction[i][1]] >= 1) { + addMove(addmoveCtx, src, + src + 2 * directionModifier[direction[i][0]] + directionModifier[direction[i][1]], 0); + } + } + break; + } + } + } + return movesLength; +} + +uint_least8_t validMoves(struct gameState_t gameState, bool whiteToMove, struct move_t *moves) { + uint_least8_t movesLength = 0; + for(uint_least8_t src = 0; src < TOTAL_BOARD_SIZE; ++src) { + movesLength += pieceValidMoves(gameState, whiteToMove, moves + movesLength, src, 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) { + struct piece_t *board = gameState.board; + const struct piece_t noPiece = {NOTHING, false}; + const bool whiteToMove = board[move.src].white; + const bool otherPlayer = !whiteToMove; + const uint_least8_t pieceType = board[move.src].type; + const uint_least8_t castleRankI[] = {0, (BOARD_SIZE - 1) * BOARD_SIZE}; + struct piece_t *castleRankPtr = board + castleRankI[whiteToMove]; + board[move.src] = noPiece; + board[move.dst] = move.dstPiece; + switch(move.spezialMove) { + case EN_PASSANT: + { + const uint_least8_t file = move.dst % 8; + const uint_least8_t rank = move.src / 8; + board[rank * BOARD_SIZE + file] = noPiece; + break; + } + case SHORT_CASTLE: + castleRankPtr[5] = castleRankPtr[BOARD_SIZE - 1]; + castleRankPtr[BOARD_SIZE - 1] = noPiece; + break; + case LONG_CASTLE: + castleRankPtr[3] = castleRankPtr[0]; + castleRankPtr[0] = noPiece; + break; + } + struct castle_t *canCastle = gameState.canCastle; + if(canCastle[whiteToMove].longCastle || canCastle[whiteToMove].shortCastle) { + if(pieceType == KING) { + canCastle[whiteToMove].longCastle = false; + canCastle[whiteToMove].shortCastle = false; + } + else { + setCastleInfo(canCastle + whiteToMove, move.src - castleRankI[whiteToMove]); + } + } + // if the rook is captured + if(canCastle[otherPlayer].longCastle || canCastle[otherPlayer].shortCastle) { + setCastleInfo(canCastle + otherPlayer, move.dst - castleRankI[otherPlayer]); + } + { + const int_least8_t dist = (int_least8_t)move.dst - (int_least8_t)move.src; + gameState.enPassantTo = pieceType == PAWN && (dist == 2 * BOARD_SIZE || dist == -2 * BOARD_SIZE) + ? move.src + dist / 2 : NOT_SELECTED; + } + ++gameState.halfMoveCounter; + 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]); +} diff --git a/src/move.h b/src/move.h new file mode 100644 index 0000000..d3236a7 --- /dev/null +++ b/src/move.h @@ -0,0 +1,48 @@ +#ifndef CHESS_MOVE_H +#define CHESS_MOVE_H + +#include +#include "types.h" +#include + +#define MAX_VALID_MOVES 218 + +enum spezialMoves { + EN_PASSANT = 1, + SHORT_CASTLE, + LONG_CASTLE +}; + +struct castle_t { + bool shortCastle, longCastle; +}; + +struct move_t { + uint_least8_t src, dst, spezialMove; + struct piece_t dstPiece; +}; + +struct moveUndo_t { + uint_least8_t src, dst, enPassantTo; + struct castle_t canCastle; + struct piece_t srcPiece, capturedPiece; +}; + +struct gameState_t { + struct piece_t *board; + 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 whiteToMove, struct move_t *moves); +uint_least8_t pieceValidMoves(struct gameState_t gameState, bool whiteToMove, struct move_t *moves, + uint_least8_t src, bool promotion); + +struct gameState_t makeMove(struct gameState_t gameState, struct move_t move); +struct gameState_t computerMove(struct gameState_t gameState); + +#endif diff --git a/src/types.h b/src/types.h index f03e26b..9eb144b 100644 --- a/src/types.h +++ b/src/types.h @@ -1,9 +1,13 @@ +#ifndef CHESS_TYPES_H +#define CHESS_TYPES_H + #include #include #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 { NOTHING, @@ -19,3 +23,5 @@ struct piece_t { uint_least8_t type; bool white; }; + +#endif