diff --git a/src/bitboard.c b/src/bitboard.c new file mode 100644 index 0000000..733cee5 --- /dev/null +++ b/src/bitboard.c @@ -0,0 +1,75 @@ +#include +#include +#include +#include "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; + } +} diff --git a/src/bitboard.h b/src/bitboard.h new file mode 100644 index 0000000..7f230a9 --- /dev/null +++ b/src/bitboard.h @@ -0,0 +1,15 @@ +#include +#include +#include "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); diff --git a/src/main.c b/src/main.c index 2b29ea8..dca1f88 100644 --- a/src/main.c +++ b/src/main.c @@ -10,6 +10,7 @@ #include #include "types.h" #include "move.h" +#include "bitboard.h" #define drawPiece(file, rank, piece) { \ const RsvgRectangle rect = {xOffset + (file) * fieldSize, yOffset + (rank) * fieldSize, fieldSize, fieldSize};\ @@ -30,12 +31,10 @@ struct drawData_t { }; const uint_least8_t PROMOTION_PIECES[] = {QUEEN, KNIGHT, BISHOP, ROOK}; -#ifdef NO_COMPUTER - static bool NocTurn = true; -#endif +static bool turn = WHITE; static uint_least8_t pieceToSvgI(struct piece_t piece) { - return piece.white ? piece.type | 8 : piece.type; + 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) { @@ -48,9 +47,9 @@ static void selectCircle(cairo_t *cr, const struct drawData_t *drawData, uint_le } static struct gameState_t userMove(struct gameState_t gameState, struct move_t move) { - gameState = makeMove(gameState, move); + gameState = makeMove(gameState, turn, move); #ifdef NO_COMPUTER - NocTurn = !NocTurn; + turn = !turn; #else gameState = computerMove(gameState); #endif @@ -66,20 +65,21 @@ static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height, const double xOffset = (width - fieldSize * BOARD_SIZE) / 2; const double yOffset = (height - fieldSize * BOARD_SIZE) / 2; struct drawData_t *drawData = (struct drawData_t*)data; - const struct piece_t *chessboard = drawData->gameState->board; + const uint_least64_t *board = 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); - if((x + y) & 1) cairo_set_source_rgb(cr, 0.46274, 0.5882, 0.3373); + 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); - const struct piece_t piece = chessboard[x + y * BOARD_SIZE]; - if(piece.type) { - drawPiece(x, y, piece) + if(bitboardGetAllPieces(board, field)) { + const struct piece_t piece = pieceAtField(board, rank * BOARD_SIZE + file); + drawPiece(file, rank, piece); } } } @@ -109,7 +109,7 @@ static void draw_event(GtkDrawingArea *area, cairo_t *cr, int width, int height, 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; - struct piece_t *chessboard = gameState->board; + const uint_least64_t *board = gameState->board; const double fieldSize = drawData->fieldSize; const double adjustedX = x - drawData->xOffset; const double adjustedY = y - drawData->yOffset; @@ -119,19 +119,17 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y, 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 - ) { + 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, chessboard[field].white, drawData->moves, field, false); + drawData->movesLength = pieceValidMoves(*gameState, piece, field, drawData->moves, false); } } else if(drawData->clickedPiece != NOT_SELECTED){ @@ -143,7 +141,7 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y, } } if(isValidDst) { - const struct piece_t piece = chessboard[drawData->clickedPiece]; + const struct piece_t piece = pieceAtField(board, field); if(piece.type == PAWN && field < BOARD_SIZE) { // promotion drawData->selectDst = field; } @@ -167,8 +165,9 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y, 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}; + 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; @@ -179,39 +178,43 @@ static void on_click(GtkGestureClick *gesture, int n_press, double x, double y, static void app_activate(GApplication *app, gpointer data) { GtkWindow *window = GTK_WINDOW(gtk_window_new()); static RsvgHandle *piecesSvg[16]; - static struct piece_t chessboard[TOTAL_BOARD_SIZE] = {}; - static struct gameState_t gameState = {chessboard, {{true, true,}, {true, true}}, 0, NOT_SELECTED}; + static uint_least64_t board[2 * PIECES_LENGTH] = {}; + static struct gameState_t gameState = {board, {{true, true}, {true, true}}, 0, NOT_SELECTED}; { - struct piece_t *piece = chessboard; + 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; + piece.type = KING; break; case 'q': - piece->type = QUEEN; + piece.type = QUEEN; break; case 'r': - piece->type = ROOK; + piece.type = ROOK; break; case 'n': - piece->type = KNIGHT; + piece.type = KNIGHT; break; case 'b': - piece->type = BISHOP; + piece.type = BISHOP; break; case 'p': - piece->type = PAWN; + piece.type = PAWN; break; case '/': continue; default: - if(isdigit(*c)) piece += *c - '0'; + if(isdigit(*c)) { + field += *c - '0'; + } continue; } - if(isalpha(*c)) piece->white = isupper(*c); - ++piece; + if(isalpha(*c)) piece.color = isupper(*c); + bitboardSet(board, piece, field); + ++field; } } { diff --git a/src/move.c b/src/move.c index 1831064..62756b3 100644 --- a/src/move.c +++ b/src/move.c @@ -3,10 +3,12 @@ #include #include "types.h" #include "move.h" +#include "bitboard.h" +#define trailingBits(num) __builtin_ctzll(num) struct addMoveCtx_t { - struct piece_t *board; + uint_least64_t *board; struct move_t *moves; uint_least8_t *movesLength; }; @@ -20,6 +22,10 @@ const static uint_least8_t MAIN_DIRECTION[] = {NORTH, WEST, EAST, SOUTH}; static uint_least8_t directionOffset[TOTAL_BOARD_SIZE * DIRECTION_LENGTH]; static int_least8_t directionModifier[DIRECTION_LENGTH]; +static uint_least8_t getDirectionOffset(uint_least8_t field, uint_least8_t direction) { + return directionOffset[field * DIRECTION_LENGTH + direction]; +} + static uint_least8_t min(uint_least8_t a, uint_least8_t b) { return a < b ? a : b; } @@ -48,150 +54,178 @@ void genDirectionConsts() { directionModifier[NORTHWEST] = directionModifier[NORTH] + directionModifier[WEST]; } -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; +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 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.board; uint_least8_t *movesLength = ctx.movesLength; - const struct move_t move = {src, dst, spezialMove, board[src]}; - if(board[dst].type && board[src].white == board[dst].white) return; + uint_least8_t capturedPiece = NOT_SELECTED; + { + 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}; + const struct piece_t pieceAll = {ALL_PIECES, movedPiece.color}; + if(bitboardGet(board, pieceAll, dst)) return; ctx.moves[*movesLength] = move; ++*movesLength; return; } 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; + uint_least8_t field, struct piece_t movedPiece) { + uint_least64_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; + addMove(ctx, movedPiece, field, currentField, 0); + if(bitboardGetAllPieces(board, currentField)) { + break; + } } } } -static void movePawn(struct addMoveCtx_t ctx, uint_least8_t src, uint_least8_t dst, +static void movePawn(struct addMoveCtx_t ctx, bool color, uint_least8_t src, uint_least8_t dst, bool promotion, uint_least8_t spezialMove) { - struct piece_t *board = ctx.board; + uint_least64_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))) { + 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) { - const struct move_t move = {src, dst, spezialMove, {promotionPiece, board[src].white}}; - moves[*movesLength] = move; - ++*movesLength; + addMove(ctx, piece, src, dst, spezialMove); // always adds new move + moves[*movesLength].dstPiece = promotionPiece; } } else { - const struct move_t move = {src, dst, spezialMove, {PAWN, board[src].white}}; - moves[*movesLength] = move; - ++*movesLength; + addMove(ctx, piece, src, dst, spezialMove); } } -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 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 = {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); - } + switch(piece.type) { + case KING: + { // King + for(uint_least8_t direction = 0; direction < DIRECTION_LENGTH; ++direction) { + if(getDirectionOffset(src, direction) >= 1) { // if sparen mit pre gen moves + addMove(addmoveCtx, piece, 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); + 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)) { + addMove(addmoveCtx, piece, src, src + 2 * directionModifier[EAST], SHORT_CASTLE); + } + if(canCastle.longCastle && !(allPiecesMask & longCastleMask)) { + addMove(addmoveCtx, piece, src, src + 2 * directionModifier[WEST], LONG_CASTLE); + } break; - case BISHOP: + } + 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 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 directions[] = {EAST, WEST}; + for(uint_least8_t j = 0; j < LENGTH(directions); ++j) { + uint_least8_t dst = src + directionModifier[directions[j]] + directionModifier[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 + 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); - } + uint_least8_t dst = src + directionModifier[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 += directionModifier[pawnDirection]; + movePawn(addmoveCtx, piece.color, src, dst, promotion, FUTURE_EN_PASSANT); } - break; } - } + } + break; + } + case KNIGHT: + { + 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 + }; + for(uint_least8_t i = 0; i < LENGTH(direction); i += 2) { + if(getDirectionOffset(src, direction[i]) >= 2 && getDirectionOffset(src, direction[i + 1])) { + addMove(addmoveCtx, piece, src, + src + 2 * directionModifier[direction[i]] + 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 validMoves(struct gameState_t gameState, bool color, 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); + 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; } @@ -207,51 +241,46 @@ static void setCastleInfo(struct castle_t *canCastle, uint_least8_t posOnRank) { } } -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; +struct gameState_t makeMove(struct gameState_t gameState, bool color, struct move_t move) { + uint_least64_t *board = gameState.board; + const uint_least8_t castleRankI[2] = {0, (BOARD_SIZE - 1) * BOARD_SIZE}; + const struct piece_t srcPiece = {move.srcPiece, color}; + const struct piece_t dstPiece = {move.dstPiece, color}; + bitboardClear(board, srcPiece, move.src); + bitboardSet(board, dstPiece, move.dst); + if(move.capturedPiece != NOT_SELECTED) { + const struct piece_t capturedPiece = {move.capturedPiece, !color}; + bitboardClear(board, capturedPiece, getCapturePos(move.src, move.dst, move.spezialMove)); + } + if(move.spezialMove == SHORT_CASTLE || move.spezialMove == LONG_CASTLE) { + const uint_least8_t rookSrc = castleRankI[color] + (move.spezialMove == SHORT_CASTLE ? BOARD_SIZE - 1 : 0); + const uint_least8_t rookDst = castleRankI[color] + (move.spezialMove == SHORT_CASTLE ? 5 : 3); + const struct piece_t rook = {ROOK, color}; + bitboardSet(board, rook, rookDst); + bitboardClear(board, rook, rookSrc); + } + { + struct castle_t *canCastleCurrentPlayer = gameState.canCastle + color; + if(canCastleCurrentPlayer->longCastle || canCastleCurrentPlayer->shortCastle) { + if(move.dstPiece == KING) { + canCastleCurrentPlayer->longCastle = false; + canCastleCurrentPlayer->shortCastle = false; + } + else { + setCastleInfo(canCastleCurrentPlayer, move.src - castleRankI[color]); } - 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]); + { + // if the rook is captured + struct castle_t *canCastleOther = gameState.canCastle + !color; + if(canCastleOther->longCastle || canCastleOther->shortCastle) { + setCastleInfo(canCastleOther, move.dst - castleRankI[!color]); + } } { 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.enPassantTo = FUTURE_EN_PASSANT ? move.src + dist / 2 : NOT_SELECTED; } ++gameState.halfMoveCounter; return gameState; @@ -260,5 +289,5 @@ struct gameState_t makeMove(struct gameState_t gameState, struct move_t move) { struct gameState_t computerMove(struct gameState_t gameState) { struct move_t moves[MAX_VALID_MOVES]; validMoves(gameState, false, moves); - return makeMove(gameState, moves[0]); + return makeMove(gameState, BLACK, moves[0]); } diff --git a/src/move.h b/src/move.h index d3236a7..d22b2c7 100644 --- a/src/move.h +++ b/src/move.h @@ -9,8 +9,9 @@ enum spezialMoves { EN_PASSANT = 1, + FUTURE_EN_PASSANT, SHORT_CASTLE, - LONG_CASTLE + LONG_CASTLE, }; struct castle_t { @@ -19,17 +20,11 @@ struct castle_t { 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; + uint_least8_t srcPiece, dstPiece, capturedPiece; }; struct gameState_t { - struct piece_t *board; + uint_least64_t *board; struct castle_t canCastle[2]; // The number of halfmoves since the last capture or pawn advance, used for the fifty-move rule. @@ -38,11 +33,11 @@ struct gameState_t { }; 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); +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 makeMove(struct gameState_t gameState, bool color, struct move_t move); struct gameState_t computerMove(struct gameState_t gameState); #endif diff --git a/src/types.h b/src/types.h index 9eb144b..270d672 100644 --- a/src/types.h +++ b/src/types.h @@ -10,18 +10,24 @@ #define NOT_SELECTED UINT_LEAST8_MAX enum pieces { - NOTHING, + ALL_PIECES, KING, QUEEN, ROOK, BISHOP, KNIGHT, - PAWN + PAWN, + PIECES_LENGTH +}; + +enum color { + BLACK, + WHITE }; struct piece_t { uint_least8_t type; - bool white; + bool color; }; #endif