Flipper/Applications/Official/DEV_FW/source/kyhwana/chess/fast_chess.c
2022-12-28 22:34:27 -08:00

2979 lines
91 KiB
C

/*
============================================================================
Name : fast-chess.c
Author : Frederico Jordan <fredericojordan@gmail.com>
Version :
Copyright : Copyright (c) 2016 Frederico Jordan
Description : Simple chess game!
============================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <time.h>
#include <ctype.h>
#include "fast_chess.h"
char FILES[8] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'};
char RANKS[8] = {'1', '2', '3', '4', '5', '6', '7', '8'};
Bitboard FILES_BB[8] = {FILE_A, FILE_B, FILE_C, FILE_D, FILE_E, FILE_F, FILE_G, FILE_H};
Bitboard RANKS_BB[8] = {RANK_1, RANK_2, RANK_3, RANK_4, RANK_5, RANK_6, RANK_7, RANK_8};
char INITIAL_FEN[] = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
Board EMPTY_BOARD = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
Board INITIAL_BOARD = {
FILE_E & RANK_1, // whiteKing;
FILE_D& RANK_1, // whiteQueens;
(FILE_A | FILE_H) & RANK_1, // whiteRooks;
(FILE_B | FILE_G) & RANK_1, // whiteKnights;
(FILE_C | FILE_F) & RANK_1, // whiteBishops;
RANK_2, // whitePawns;
FILE_E& RANK_8, // blackKing;
FILE_D& RANK_8, // blackQueens;
(FILE_A | FILE_H) & RANK_8, // blackRooks;
(FILE_B | FILE_G) & RANK_8, // blackKnights;
(FILE_C | FILE_F) & RANK_8, // blackBishops;
RANK_7, // blackPawns;
};
int PIECE_VALUES[] = {
0, // EMPTY
100, // PAWN
300, // KNIGHT
300, // BISHOP
500, // ROOK
900, // QUEEN
42000 // KING
};
int PAWN_BONUS[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -40, -40, 0, 0, 0,
1, 2, 3, -10, -10, 3, 2, 1, 2, 4, 6, 8, 8, 6, 4, 2,
3, 6, 9, 12, 12, 9, 6, 3, 4, 8, 12, 16, 16, 12, 8, 4,
5, 10, 15, 20, 20, 15, 10, 5, 0, 0, 0, 0, 0, 0, 0, 0};
int KNIGHT_BONUS[] = {-10, -30, -10, -10, -10, -10, -30, -10, -10, 0, 0, 0, 0,
0, 0, -10, -10, 0, 5, 5, 5, 5, 0, -10, -10, 0,
5, 10, 10, 5, 0, -10, -10, 0, 5, 10, 10, 5, 0,
-10, -10, 0, 5, 5, 5, 5, 0, -10, -10, 0, 0, 0,
0, 0, 0, -10, -10, -10, -10, -10, -10, -10, -10, -10};
int BISHOP_BONUS[] = {-10, -10, -20, -10, -10, -20, -10, -10, -10, 0, 0, 0, 0,
0, 0, -10, -10, 0, 5, 5, 5, 5, 0, -10, -10, 0,
5, 10, 10, 5, 0, -10, -10, 0, 5, 10, 10, 5, 0,
-10, -10, 0, 5, 5, 5, 5, 0, -10, -10, 0, 0, 0,
0, 0, 0, -10, -10, -10, -10, -10, -10, -10, -10, -10};
int KING_BONUS[] = {0, 20, 40, -20, 0, -20, 40, 20, -20, -20, -20, -20, -20,
-20, -20, -20, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40,
-40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40, -40};
int KING_ENDGAME_BONUS[] = {0, 10, 20, 30, 30, 20, 10, 0, 10, 20, 30, 40, 40, 30, 20, 10,
20, 30, 40, 50, 50, 40, 30, 20, 30, 40, 50, 60, 60, 50, 40, 30,
30, 40, 50, 60, 60, 50, 40, 30, 20, 30, 40, 50, 50, 40, 30, 20,
10, 20, 30, 40, 40, 30, 20, 10, 0, 10, 20, 30, 30, 20, 10, 0};
int FLIP_VERTICAL[] = {56, 57, 58, 59, 60, 61, 62, 63, 48, 49, 50, 51, 52, 53, 54, 55,
40, 41, 42, 43, 44, 45, 46, 47, 32, 33, 34, 35, 36, 37, 38, 39,
24, 25, 26, 27, 28, 29, 30, 31, 16, 17, 18, 19, 20, 21, 22, 23,
8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7};
void getInitialGame(Game* game) {
game->position.board = INITIAL_BOARD;
game->position.toMove = WHITE;
game->position.epSquare = -1;
game->position.castlingRights = CASTLE_KINGSIDE_WHITE | CASTLE_QUEENSIDE_WHITE |
CASTLE_KINGSIDE_BLACK | CASTLE_QUEENSIDE_BLACK;
game->position.halfmoveClock = 0;
game->position.fullmoveNumber = 1;
game->moveListLen = 0;
memset(game->moveList, 0, MAX_PLYS_PER_GAME * sizeof(int));
memset(game->positionHistory, 0, MAX_PLYS_PER_GAME * MAX_FEN_LEN * sizeof(char));
memcpy(game->positionHistory[0], INITIAL_FEN, sizeof(INITIAL_FEN));
}
void getFenGame(Game* game, char fen[]) {
int fenLen = loadFen(&(game->position), fen);
game->moveListLen = 0;
memset(game->moveList, 0, MAX_PLYS_PER_GAME * sizeof(int));
memset(game->positionHistory, 0, MAX_PLYS_PER_GAME * MAX_FEN_LEN * sizeof(char));
memcpy(game->positionHistory[0], fen, fenLen);
}
void insertPiece(Board* board, Bitboard position, char pieceCode) {
switch(pieceCode) {
case 'P':
board->whitePawns |= position;
break;
case 'N':
board->whiteKnights |= position;
break;
case 'B':
board->whiteBishops |= position;
break;
case 'R':
board->whiteRooks |= position;
break;
case 'Q':
board->whiteQueens |= position;
break;
case 'K':
board->whiteKing |= position;
break;
case 'p':
board->blackPawns |= position;
break;
case 'n':
board->blackKnights |= position;
break;
case 'b':
board->blackBishops |= position;
break;
case 'r':
board->blackRooks |= position;
break;
case 'q':
board->blackQueens |= position;
break;
case 'k':
board->blackKing |= position;
break;
}
}
int loadFen(Position* position, char fen[]) {
// ===== BOARD =====
position->board = EMPTY_BOARD;
int rank = 7;
int boardPos = rank * 8;
char* charPos = fen;
char pieceCode = *(charPos);
while(pieceCode != ' ') {
if(pieceCode == '/') {
rank--;
boardPos = rank * 8;
} else if(isdigit(pieceCode)) {
int emptySquares = atoi(charPos);
boardPos += emptySquares;
} else {
insertPiece(&(position->board), index2bb(boardPos++), pieceCode);
}
pieceCode = *(++charPos);
}
// ===== TO MOVE =====
char* nextFenField = strchr(fen, ' ') + 1;
if(*nextFenField == 'b') {
position->toMove = BLACK;
} else {
position->toMove = WHITE;
}
// ===== CASTLING RIGHTS =====
nextFenField = strchr(nextFenField, ' ') + 1;
position->castlingRights = 0;
if(strchr(nextFenField, 'K')) position->castlingRights |= CASTLE_KINGSIDE_WHITE;
if(strchr(nextFenField, 'Q')) position->castlingRights |= CASTLE_QUEENSIDE_WHITE;
if(strchr(nextFenField, 'k')) position->castlingRights |= CASTLE_KINGSIDE_BLACK;
if(strchr(nextFenField, 'q')) position->castlingRights |= CASTLE_QUEENSIDE_BLACK;
// ===== EN PASSANT =====
nextFenField = strchr(nextFenField, ' ') + 1;
if(*nextFenField == '-') {
position->epSquare = -1;
} else {
position->epSquare = str2index(nextFenField);
}
// ===== HALF MOVE CLOCK =====
if(!strchr(nextFenField, ' ')) {
position->halfmoveClock = 0;
position->fullmoveNumber = 1;
return 1 + nextFenField - fen;
}
nextFenField = strchr(nextFenField, ' ') + 1;
position->halfmoveClock = atoi(nextFenField);
// ===== FULL MOVE NUMBER =====
if(!strchr(nextFenField, ' ')) {
position->fullmoveNumber = 1;
return 1 + nextFenField - fen;
}
nextFenField = strchr(nextFenField, ' ') + 1;
position->fullmoveNumber = atoi(nextFenField);
return 1 + nextFenField - fen;
}
int toFen(char* fen, Position* position) {
int charCount = toMinFen(fen, position);
fen[charCount - 1] = ' ';
// ===== HALF MOVE CLOCK =====
snprintf(&fen[charCount++], sizeof(&fen[charCount++]), "%d", position->halfmoveClock);
if(position->halfmoveClock >= 10) {
charCount++;
if(position->halfmoveClock >= 100) {
charCount++;
}
}
fen[charCount++] = ' ';
// ===== FULL MOVE NUMBER =====
snprintf(&fen[charCount++], sizeof(&fen[charCount++]), "%d", position->fullmoveNumber);
if(position->fullmoveNumber >= 10) {
charCount++;
if(position->fullmoveNumber >= 100) {
charCount++;
}
}
fen[charCount++] = '\0';
return charCount;
}
int toMinFen(char* fen, Position* position) {
int charCount = 0;
// ===== BOARD =====
int rank = 7;
int file = 0;
int* emptyCount = 0;
Bitboard empties = getEmptySquares(&(position->board));
Bitboard bb;
while(rank >= 0) {
bb = index2bb(8 * rank + file);
if(bb & empties) {
emptyCount++;
} else {
if(emptyCount != 0) {
snprintf(&fen[charCount++], 2, "%ls", emptyCount);
emptyCount = 0;
}
fen[charCount++] = bb2char(bb, &(position->board));
}
file++;
if(file > 7) {
if(emptyCount != 0) {
snprintf(&fen[charCount++], 2, "%ls", emptyCount);
emptyCount = 0;
}
file = 0;
rank--;
fen[charCount++] = '/';
}
}
fen[charCount - 1] = ' ';
// ===== TO MOVE =====
if(position->toMove == BLACK) {
fen[charCount++] = 'b';
} else {
fen[charCount++] = 'w';
}
fen[charCount++] = ' ';
// ===== CASTLING RIGHTS =====
if(position->castlingRights == 0) {
fen[charCount++] = '-';
} else {
if(position->castlingRights & CASTLE_KINGSIDE_WHITE) {
fen[charCount++] = 'K';
}
if(position->castlingRights & CASTLE_QUEENSIDE_WHITE) {
fen[charCount++] = 'Q';
}
if(position->castlingRights & CASTLE_KINGSIDE_BLACK) {
fen[charCount++] = 'k';
}
if(position->castlingRights & CASTLE_QUEENSIDE_BLACK) {
fen[charCount++] = 'q';
}
}
fen[charCount++] = ' ';
// ===== EN PASSANT =====
if(position->epSquare == -1) {
fen[charCount++] = '-';
} else {
fen[charCount++] = getFile(position->epSquare);
fen[charCount++] = getRank(position->epSquare);
}
fen[charCount++] = '\0';
return charCount;
}
void getMovelistGame(Game* game, char moves[]) {
getInitialGame(game);
for(int i = 0; i < strlen(moves) - 3; i += 5) {
makeMove(game, parseMove(&moves[i]));
if(moves[i + 5] == ' ') i++; // FIXME Queening
}
}
// ========= UTILITY =========
BOOL fromInitial(Game* game) {
if(strcmp(game->positionHistory[0], INITIAL_FEN) == 0)
return TRUE;
else
return FALSE;
}
Bitboard index2bb(int index) {
Bitboard bb = 1;
return bb << index;
}
int str2index(char* str) {
int i, file_num = 0, rank_num = 0;
for(i = 0; i < 8; i++) {
if(str[0] == FILES[i]) file_num = i;
if(str[1] == RANKS[i]) rank_num = i;
}
return 8 * rank_num + file_num;
}
Bitboard str2bb(char* str) {
return index2bb(str2index(str));
}
BOOL isSet(Bitboard bb, int index) {
if(bb & index2bb(index))
return TRUE;
else
return FALSE;
}
Bitboard lsb(Bitboard bb) {
int i;
for(i = 0; i < NUM_SQUARES; i++) {
Bitboard bit = index2bb(i);
if(bb & bit) return bit;
}
return 0;
}
Bitboard msb(Bitboard bb) {
int i;
for(i = 0; i < NUM_SQUARES; i++) {
Bitboard bit = index2bb(63 - i);
if(bb & bit) return bit;
}
return 0;
}
int bb2index(Bitboard bb) {
int i;
for(i = 0; i < NUM_SQUARES; i++) {
Bitboard bit = index2bb(i);
if(bb & bit) return i;
}
return -1;
}
char* movelist2str(Game* game) {
char* movestr = NULL;
if(game->moveListLen == 0) {
movestr = (char*)malloc(sizeof(char));
movestr[0] = 0;
return movestr;
}
movestr = (char*)malloc(5 * game->moveListLen);
int i;
for(i = 0; i < game->moveListLen; i++) {
int leaving = getFrom(game->moveList[i]);
int arriving = getTo(game->moveList[i]);
movestr[5 * i] = getFile(leaving);
movestr[5 * i + 1] = getRank(leaving);
movestr[5 * i + 2] = getFile(arriving);
movestr[5 * i + 3] = getRank(arriving);
movestr[5 * i + 4] = ' ';
}
movestr[5 * game->moveListLen - 1] = 0;
return movestr;
}
Move getLastMove(Game* game) {
if(game->moveListLen == 0)
return 0;
else
return game->moveList[game->moveListLen - 1];
}
BOOL startsWith(const char* str, const char* pre) {
size_t lenpre = strlen(pre), lenstr = strlen(str);
if(lenpre > lenstr) return FALSE;
return strncmp(pre, str, lenpre) == 0 ? TRUE : FALSE;
}
int countBookOccurrences(Game* game) {
FILE* fp = fopen("book.txt", "r");
if(fp == NULL) return 0;
char* moveList = movelist2str(game);
char* line = (char*)malloc(sizeof(char) * MAX_BOOK_ENTRY_LEN);
int charPos = 0, occurrences = 0;
while(TRUE) {
char ch = getc(fp);
line[charPos++] = ch;
if(ch == '\n' || ch == EOF) {
line[charPos - 1] = '\0';
if(startsWith(line, moveList) && strlen(line) > strlen(moveList) + 4) {
occurrences++;
}
if(ch == EOF) break;
charPos = 0;
}
}
fclose(fp);
free(line);
free(moveList);
return occurrences;
}
Move getBookMove(Game* game) {
Move move = 0;
int moveNum = rand() % countBookOccurrences(game);
FILE* fp = fopen("book.txt", "r");
if(fp == NULL) return 0;
char* moveList = movelist2str(game);
char* line = (char*)malloc(sizeof(char) * MAX_BOOK_ENTRY_LEN);
int charPos = 0, occurrences = 0;
while(TRUE) {
char ch = getc(fp);
line[charPos++] = ch;
if(ch == '\n') {
line[charPos] = '\0';
if(startsWith(line, moveList)) {
if(occurrences == moveNum) {
int ind = game->moveListLen * 5;
move = parseMove(&line[ind]);
break;
}
occurrences++;
}
charPos = 0;
}
}
fclose(fp);
free(line);
free(moveList);
return move;
}
char getFile(int position) {
int file = position % 8;
return FILES[file];
}
char getRank(int position) {
int rank = (int)(position / 8);
return RANKS[rank];
}
Move generateMove(int leavingSquare, int arrivingSquare) {
int leaving = (leavingSquare << 8);
int arriving = arrivingSquare;
return (Move)(leaving + arriving);
}
int getFrom(Move move) {
return (move >> 8) & 0xFF;
}
int getTo(Move move) {
return move & 0xFF;
}
int bb2piece(Bitboard position, Board* board) {
if(position & board->whitePawns) return WHITE | PAWN;
if(position & board->whiteKnights) return WHITE | KNIGHT;
if(position & board->whiteBishops) return WHITE | BISHOP;
if(position & board->whiteRooks) return WHITE | ROOK;
if(position & board->whiteQueens) return WHITE | QUEEN;
if(position & board->whiteKing) return WHITE | KING;
if(position & board->blackPawns) return BLACK | PAWN;
if(position & board->blackKnights) return BLACK | KNIGHT;
if(position & board->blackBishops) return BLACK | BISHOP;
if(position & board->blackRooks) return BLACK | ROOK;
if(position & board->blackQueens) return BLACK | QUEEN;
if(position & board->blackKing) return BLACK | KING;
return EMPTY;
}
char bb2char(Bitboard position, Board* board) {
if(position & board->whitePawns) return 'P';
if(position & board->whiteKnights) return 'N';
if(position & board->whiteBishops) return 'B';
if(position & board->whiteRooks) return 'R';
if(position & board->whiteQueens) return 'Q';
if(position & board->whiteKing) return 'K';
if(position & board->blackPawns) return 'p';
if(position & board->blackKnights) return 'n';
if(position & board->blackBishops) return 'b';
if(position & board->blackRooks) return 'r';
if(position & board->blackQueens) return 'q';
if(position & board->blackKing) return 'k';
return '?';
}
char* bb2str(Bitboard position, Board* board) {
if((position & board->whitePawns) | (position & board->blackPawns)) return "Pawn";
if((position & board->whiteKnights) | (position & board->blackKnights)) return "Knight";
if((position & board->whiteBishops) | (position & board->blackBishops)) return "Bishop";
if((position & board->whiteRooks) | (position & board->blackRooks)) return "Rook";
if((position & board->whiteQueens) | (position & board->blackQueens)) return "Queen";
if((position & board->whiteKing) | (position & board->blackKing)) return "King";
return "?";
}
void printBitboard(Bitboard bitboard) {
int rank, file;
printf("\n");
for(rank = 0; rank < 8; rank++) {
printf("%d", 8 - rank);
for(file = 0; file < 8; file++) {
if(bitboard >> (file + (7 - rank) * 8) & 1) {
printf(" #");
} else {
printf(" .");
}
}
printf("\n");
}
printf(" a b c d e f g h\n");
fflush(stdout);
}
char getPieceChar(Bitboard position, Board* board) {
if(position & board->whiteKing) return 'K';
if(position & board->whiteQueens) return 'Q';
if(position & board->whiteRooks) return 'R';
if(position & board->whiteKnights) return 'N';
if(position & board->whiteBishops) return 'B';
if(position & board->whitePawns) return 'P';
if(position & board->blackKing) return 'k';
if(position & board->blackQueens) return 'q';
if(position & board->blackRooks) return 'r';
if(position & board->blackKnights) return 'n';
if(position & board->blackBishops) return 'b';
if(position & board->blackPawns) return 'p';
return '.';
}
void printBoard(Board* board) {
int rank, file;
printf("\n");
for(rank = 0; rank < 8; rank++) {
printf("%d", 8 - rank);
for(file = 0; file < 8; file++) {
printf(" %c", getPieceChar((FILES_BB[file] & RANKS_BB[7 - rank]), board));
}
printf("\n");
}
printf(" a b c d e f g h\n");
fflush(stdout);
}
void printGame(Game* game) {
printf("Game -> %p (%lu)", game, sizeof(*game));
// printBoard(&(game->position.board));
// printf("board -> %p (%lu)\n", &(game->position.board), sizeof(game->position.board));
// printf("toMove = %d -> %p (%lu)\n", game->position.toMove, &game->position.toMove, sizeof(game->position.toMove));
// printf("ep = %d -> %p (%lu)\n", game->position.epSquare, &game->position.epSquare, sizeof(game->position.epSquare));
// printf("castle rights = %d -> %p (%lu)\n", game->position.castlingRights, &game->position.castlingRights, sizeof(game->position.castlingRights));
// printf("half clock = %d -> %p (%lu)\n", game->position.halfmoveClock, &game->position.halfmoveClock, sizeof(game->position.halfmoveClock));
// printf("full num = %d -> %p (%lu)\n", game->position.fullmoveNumber, &game->position.fullmoveNumber, sizeof(game->position.fullmoveNumber));
// printf("moveListLen = %d -> %p (%lu)\n", game->moveListLen, &game->moveListLen, sizeof(game->moveListLen));
// printf("moveList -> %p (%lu)\n", game->moveList, sizeof(game->moveList));
// printf("positionHistory -> %p (%lu)\n", game->positionHistory, sizeof(game->positionHistory));
// fflush(stdout);
}
Bitboard not(Bitboard bb) {
return ~bb & ALL_SQUARES;
}
char opponent(char color) {
switch(color) {
case WHITE:
return BLACK;
case BLACK:
return WHITE;
}
return -1;
}
int countBits(Bitboard bb) {
int bitCount = 0;
Bitboard position = 1;
for(int i = 0; i < NUM_SQUARES; i++) {
if(position & bb) {
bitCount++;
}
position = position << 1;
}
return bitCount;
}
void sortNodes(Node* sortedNodes, Node* nodes, int len, char color) {
Node nodeBuffer[len];
int i, j;
BOOL sorted;
for(i = 0; i < len; i++) {
sorted = FALSE;
for(j = 0; j < i; j++) {
if((color == WHITE && nodes[i].score > sortedNodes[j].score) ||
(color == BLACK && nodes[i].score < sortedNodes[j].score)) {
sorted = TRUE;
memcpy(nodeBuffer, &sortedNodes[j], (i - j) * sizeof(Node));
memcpy(&sortedNodes[j + 1], nodeBuffer, (i - j) * sizeof(Node));
sortedNodes[j] = nodes[i];
break;
}
}
if(sorted == FALSE) {
sortedNodes[i] = nodes[i];
}
}
}
void printMove(Move move) {
printf(
"%c%c to %c%c",
getFile(getFrom(move)),
getRank(getFrom(move)),
getFile(getTo(move)),
getRank(getTo(move)));
}
void printFullMove(Move move, Board* board) {
Bitboard leavingBB = index2bb(getFrom(move));
printf("%s from ", bb2str(leavingBB, board));
printMove(move);
}
void printLegalMoves(Position* position) {
int i;
Move moves[MAX_BRANCHING_FACTOR];
int moveCount = legalMoves(moves, position, position->toMove);
for(i = 0; i < moveCount; i++) {
printf("%2d. ", i + 1);
printFullMove(moves[i], &(position->board));
// printMove(moves[i]);
printf("\n");
}
fflush(stdout);
}
void printNode(Node node) {
printMove(node.move);
printf(": %d", node.score);
}
void getTimestamp(char* timestamp) {
time_t timer;
struct tm* tm_info;
time(&timer);
tm_info = localtime(&timer);
strftime(timestamp, 20, "%Y-%m-%d_%H.%M.%S", tm_info);
}
void dumpContent(Game* game) {
char* movelist = movelist2str(game);
char filename[50];
sprintf(filename, "chess_game_");
getTimestamp(&filename[strlen(filename)]);
sprintf(&filename[strlen(filename)], ".txt");
FILE* file = fopen(filename, "w+");
fprintf(file, "movelist = %s\nposition history:\n", movelist);
int i;
for(i = 0; i < game->moveListLen + 1; i++) fprintf(file, "%s\n", game->positionHistory[i]);
free(movelist);
fclose(file);
printf("Dumped game content to: %s\n", filename);
fflush(stdout);
}
void dumpPGN(Game* game, char color, BOOL hasAI) {
char filename[50];
sprintf(filename, "chess_game_");
getTimestamp(&filename[strlen(filename)]);
sprintf(&filename[strlen(filename)], ".pgn");
FILE* file = fopen(filename, "w+");
char date[12];
time_t timer;
struct tm* tm_info;
time(&timer);
tm_info = localtime(&timer);
strftime(date, 11, "%Y.%m.%d", tm_info);
fprintf(file, "[Event \"Casual Game\"]\n");
fprintf(file, "[Site \"?\"]\n");
fprintf(file, "[Date \"%s\"]\n", date);
fprintf(file, "[Round \"-\"]\n");
if(hasAI) {
if(color == WHITE) {
fprintf(file, "[White \"%s\"]\n", HUMAN_NAME);
fprintf(file, "[Black \"%s\"]\n", ENGINE_NAME);
} else {
fprintf(file, "[White \"%s\"]\n", ENGINE_NAME);
fprintf(file, "[Black \"%s\"]\n", HUMAN_NAME);
}
} else {
fprintf(file, "[White \"Unknown Human Player\"]\n");
fprintf(file, "[Black \"Unknown Human Player\"]\n");
}
if(hasGameEnded(&game->position)) {
if(endNodeEvaluation(&game->position) == winScore(WHITE)) {
fprintf(file, "[Result \"1-0\"]\n");
} else if(endNodeEvaluation(&game->position) == winScore(BLACK)) {
fprintf(file, "[Result \"0-1\"]\n");
} else if(endNodeEvaluation(&game->position) == 0) {
fprintf(file, "[Result \"1/2-1/2\"]\n");
}
} else {
fprintf(file, "[Result \"*\"]\n");
}
if(strcmp(game->positionHistory[0], INITIAL_FEN) == 0) {
fprintf(file, "[Variant \"Standard\"]\n");
} else {
fprintf(file, "[Variant \"From Position\"]\n");
fprintf(file, "[FEN \"%s\"]\n", game->positionHistory[0]);
}
fprintf(file, "[PlyCount \"%d\"]\n\n", game->moveListLen);
int i;
char ply[8];
for(i = 0; i < game->moveListLen; i++) {
if(i % 2 == 0) fprintf(file, "%d. ", 1 + (i / 2));
move2str(ply, game, i);
fprintf(file, "%s ", ply);
}
fclose(file);
printf("Dumped game pgn to: %s\n", filename);
fflush(stdout);
}
void move2str(char* str, Game* game, int moveNumber) {
Position posBefore, posAfter;
loadFen(&posBefore, game->positionHistory[moveNumber]);
loadFen(&posAfter, game->positionHistory[moveNumber + 1]);
Move move = game->moveList[moveNumber];
int leavingSquare = getFrom(move);
int arrivingSquare = getTo(move);
Bitboard leavingBB = index2bb(leavingSquare);
Bitboard arrivingBB = index2bb(arrivingSquare);
int length = 0;
if((leavingBB & (posBefore.board.whiteKing | posBefore.board.blackKing)) &&
abs(leavingSquare - arrivingSquare) == 2) { // if castling
if(arrivingBB & FILE_G) {
sprintf(str, "O-O");
length += 3;
} else if(arrivingBB & FILE_C) {
sprintf(str, "O-O-O");
length += 5;
}
} else { // if not castling
if(leavingBB & (posBefore.board.whitePawns | posBefore.board.blackPawns)) {
if(arrivingBB & getOccupiedSquares(&(posBefore.board))) {
str[length++] = getFile(leavingSquare);
}
} else {
str[length++] = bb2char(leavingBB, &(posBefore.board));
}
if(isAmbiguous(&posBefore, move)) {
if(countBits(getTwinPieces(leavingBB, &(posBefore.board)) & fileFilter(leavingBB)) ==
1) {
str[length++] = getFile(leavingSquare);
} else {
str[length++] = getRank(leavingSquare);
}
}
if(bb2piece(arrivingBB, &(posBefore.board)) != EMPTY) {
str[length++] = 'x';
}
str[length++] = getFile(arrivingSquare);
str[length++] = getRank(arrivingSquare);
}
if(isCheckmate(&posAfter)) {
str[length++] = '#';
} else if(isCheck(&(posAfter.board), posAfter.toMove)) {
str[length++] = '+';
}
str[length++] = 0;
}
BOOL isAmbiguous(Position* position, Move move) {
Move moves[MAX_BRANCHING_FACTOR];
int moveCount = legalMoves(moves, position, position->toMove);
Bitboard targetBB = index2bb(getTo(move));
Bitboard attackerBB = index2bb(getFrom(move));
int attackerPiece = bb2piece(attackerBB, &(position->board));
int attackerCount = 0;
for(int i = 0; i < moveCount; i++) {
Bitboard tgtBB = index2bb(getTo(moves[i]));
Bitboard atkBB = index2bb(getFrom(moves[i]));
if(attackerPiece == bb2piece(atkBB, &(position->board)) && (targetBB & tgtBB)) {
attackerCount++;
}
}
return attackerCount > 1;
}
unsigned long hashPosition(Position* position) {
char fen[MAX_FEN_LEN];
toMinFen(fen, position);
unsigned long hash = 5381;
int c, i = 0;
while((c = fen[i++])) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
void writeToHashFile(Position* position, int evaluation, int depth) {
FILE* fp = fopen("hashfile", "a");
if(fp == NULL) return;
char fen[MAX_FEN_LEN];
toMinFen(fen, position);
fprintf(fp, "%08lx %d %d %s\n", hashPosition(position), depth, evaluation, fen);
fclose(fp);
}
// ====== BOARD FILTERS ======
Bitboard getColoredPieces(Board* board, char color) {
if(color == WHITE) {
return board->whiteKing | board->whiteQueens | board->whiteRooks | board->whiteKnights |
board->whiteBishops | board->whitePawns;
} else {
return board->blackKing | board->blackQueens | board->blackRooks | board->blackKnights |
board->blackBishops | board->blackPawns;
}
}
Bitboard getEmptySquares(Board* board) {
return not(getOccupiedSquares(board));
}
Bitboard getOccupiedSquares(Board* board) {
return getColoredPieces(board, WHITE) | getColoredPieces(board, BLACK);
}
Bitboard getTwinPieces(Bitboard position, Board* board) {
if(position & board->whiteKing) return board->whiteKing;
if(position & board->whiteQueens) return board->whiteQueens;
if(position & board->whiteRooks) return board->whiteRooks;
if(position & board->whiteKnights) return board->whiteKnights;
if(position & board->whiteBishops) return board->whiteBishops;
if(position & board->whitePawns) return board->whitePawns;
if(position & board->blackKing) return board->blackKing;
if(position & board->blackQueens) return board->blackQueens;
if(position & board->blackRooks) return board->blackRooks;
if(position & board->blackKnights) return board->blackKnights;
if(position & board->blackBishops) return board->blackBishops;
if(position & board->blackPawns) return board->blackPawns;
return position;
}
Bitboard fileFilter(Bitboard positions) {
Bitboard filter = 0;
int i;
for(i = 0; i < 8; i++)
if(positions & FILES_BB[i]) filter |= FILES_BB[i];
return filter;
}
Bitboard rankFilter(Bitboard positions) {
Bitboard filter = 0;
int i;
for(i = 0; i < 8; i++)
if(positions & RANKS_BB[i]) filter |= RANKS_BB[i];
return filter;
}
// ======= DIRECTIONS ========
Bitboard east(Bitboard bb) {
return (bb << 1) & 0xfefefefefefefefe; // not(FILE_A)
}
Bitboard west(Bitboard bb) {
return (bb >> 1) & 0x7f7f7f7f7f7f7f7f; // not(FILE_H)
}
Bitboard north(Bitboard bb) {
return (bb << 8) & 0xffffffffffffff00; // not(RANK_1)
}
Bitboard south(Bitboard bb) {
return (bb >> 8) & 0x00ffffffffffffff; // not(RANK_8)
}
Bitboard NE(Bitboard bb) {
return (bb << 9) & 0xfefefefefefefe00; // not(RANK_1 | FILE_A)
}
Bitboard NW(Bitboard bb) {
return (bb << 7) & 0x7f7f7f7f7f7f7f00; // not(RANK_1 | FILE_H)
}
Bitboard SE(Bitboard bb) {
return (bb >> 7) & 0x00fefefefefefefe; // not(RANK_8 | FILE_A)
}
Bitboard SW(Bitboard bb) {
return (bb >> 9) & 0x007f7f7f7f7f7f7f; // not(RANK_8 | FILE_H)
}
Bitboard WNW(Bitboard moving_piece) {
return (moving_piece << 6) & 0x3f3f3f3f3f3f3f00; // not(RANK_1 | FILE_G | FILE_H)
}
Bitboard ENE(Bitboard moving_piece) {
return (moving_piece << 10) & 0xfcfcfcfcfcfcfc00; // not(RANK_1 | FILE_A | FILE_B)
}
Bitboard NNW(Bitboard moving_piece) {
return (moving_piece << 15) & 0x7f7f7f7f7f7f0000; // not(RANK_1 | RANK_2 | FILE_H)
}
Bitboard NNE(Bitboard moving_piece) {
return (moving_piece << 17) & 0xfefefefefefe0000; // not(RANK_1 | RANK_2 | FILE_A)
}
Bitboard ESE(Bitboard moving_piece) {
return (moving_piece >> 6) & 0x00fcfcfcfcfcfcfc; // not(RANK_8| FILE_A | FILE_B)
}
Bitboard WSW(Bitboard moving_piece) {
return (moving_piece >> 10) & 0x003f3f3f3f3f3f3f; // not(RANK_8 | FILE_G | FILE_H)
}
Bitboard SSE(Bitboard moving_piece) {
return (moving_piece >> 15) & 0x0000fefefefefefe; // not(RANK_7 | RANK_8 | FILE_A)
}
Bitboard SSW(Bitboard moving_piece) {
return (moving_piece >> 17) & 0x00007f7f7f7f7f7f; // not(RANK_7 | RANK_8 | FILE_H)
}
// ========== PAWN ===========
Bitboard getPawns(Board* board) {
return board->whitePawns | board->blackPawns;
}
Bitboard pawnSimplePushes(Bitboard moving_piece, Board* board, char color) {
switch(color) {
case WHITE:
return north(moving_piece) & getEmptySquares(board);
case BLACK:
return south(moving_piece) & getEmptySquares(board);
}
return 0;
}
Bitboard pawnDoublePushes(Bitboard moving_piece, Board* board, char color) {
switch(color) {
case WHITE:
return north(pawnSimplePushes(moving_piece, board, color)) &
(getEmptySquares(board) & RANK_4);
case BLACK:
return south(pawnSimplePushes(moving_piece, board, color)) &
(getEmptySquares(board) & RANK_5);
}
return 0;
}
Bitboard pawnPushes(Bitboard moving_piece, Board* board, char color) {
return pawnSimplePushes(moving_piece, board, color) |
pawnDoublePushes(moving_piece, board, color);
}
Bitboard pawnEastAttacks(Bitboard moving_piece, Board* board, char color) {
switch(color) {
case WHITE:
return NE(moving_piece);
case BLACK:
return SE(moving_piece);
}
return 0;
}
Bitboard pawnWestAttacks(Bitboard moving_piece, Board* board, char color) {
switch(color) {
case WHITE:
return NW(moving_piece);
case BLACK:
return SW(moving_piece);
}
return 0;
}
Bitboard pawnAttacks(Bitboard moving_piece, Board* board, char color) {
return pawnEastAttacks(moving_piece, board, color) |
pawnWestAttacks(moving_piece, board, color);
}
Bitboard pawnSimpleCaptures(Bitboard moving_piece, Board* board, char color) {
return pawnAttacks(moving_piece, board, color) & getColoredPieces(board, opponent(color));
}
Bitboard pawnEpCaptures(Bitboard moving_piece, Position* position, char color) {
if(position->epSquare == -1) return 0;
Bitboard valid_ep_square = 0;
switch(color) {
case WHITE:
valid_ep_square = index2bb(position->epSquare) & RANK_6;
break;
case BLACK:
valid_ep_square = index2bb(position->epSquare) & RANK_3;
break;
}
return pawnAttacks(moving_piece, &(position->board), color) & valid_ep_square;
}
Bitboard pawnCaptures(Bitboard moving_piece, Position* position, char color) {
return pawnSimpleCaptures(moving_piece, &(position->board), color) |
pawnEpCaptures(moving_piece, position, color);
}
Bitboard pawnMoves(Bitboard moving_piece, Position* position, char color) {
return pawnPushes(moving_piece, &(position->board), color) |
pawnCaptures(moving_piece, position, color);
}
BOOL isDoublePush(int leaving, int arriving) {
if((index2bb(leaving) & RANK_2) && (index2bb(arriving) & RANK_4)) return TRUE;
if((index2bb(leaving) & RANK_7) && (index2bb(arriving) & RANK_5)) return TRUE;
return FALSE;
}
char getEpSquare(int leaving) {
if(index2bb(leaving) & RANK_2) return leaving + 8;
if(index2bb(leaving) & RANK_7) return leaving - 8;
return -1;
}
BOOL isDoubledPawn(Bitboard position, Board* board, char color) {
if(color == WHITE) {
if(countBits(board->whitePawns & fileFilter(position)) > 1) return TRUE;
} else {
if(countBits(board->blackPawns & fileFilter(position)) > 1) return TRUE;
}
return FALSE;
}
BOOL isIsolatedPawn(Bitboard position, Board* board, char color) {
Bitboard sideFiles = fileFilter(east(position) | west(position));
if(color == WHITE) {
if(countBits(board->whitePawns & sideFiles) == 0) return TRUE;
} else {
if(countBits(board->blackPawns & sideFiles) == 0) return TRUE;
}
return FALSE;
}
BOOL isBackwardsPawn(Bitboard position, Board* board, char color) {
Bitboard squaresFilter = east(position) | west(position);
if(color == BLACK) {
squaresFilter |= northRay(squaresFilter);
if(countBits(board->blackPawns & squaresFilter) == 0) return TRUE;
} else {
squaresFilter |= southRay(squaresFilter);
if(countBits(board->whitePawns & squaresFilter) == 0) return TRUE;
}
return FALSE;
}
BOOL isPassedPawn(Bitboard position, Board* board, char color) {
Bitboard squaresFilter = 0;
if(color == BLACK) {
squaresFilter |= southRay(east(position)) | southRay(west(position)) | southRay(position);
if(countBits(board->whitePawns & squaresFilter) == 0) return TRUE;
} else {
squaresFilter |= northRay(east(position)) | northRay(west(position)) | northRay(position);
if(countBits(board->blackPawns & squaresFilter) == 0) return TRUE;
}
return FALSE;
}
BOOL isOpenFile(Bitboard position, Board* board) {
if(countBits(getPawns(board) & fileFilter(position)) == 0) return TRUE;
return FALSE;
}
BOOL isSemiOpenFile(Bitboard position, Board* board) {
if(countBits(getPawns(board) & fileFilter(position)) == 1) return TRUE;
return FALSE;
}
// ========== KNIGHT =========
Bitboard getKnights(Board* board) {
return board->whiteKnights | board->blackKnights;
}
Bitboard knightAttacks(Bitboard moving_piece) {
return NNE(moving_piece) | ENE(moving_piece) | NNW(moving_piece) | WNW(moving_piece) |
SSE(moving_piece) | ESE(moving_piece) | SSW(moving_piece) | WSW(moving_piece);
}
Bitboard knightMoves(Bitboard moving_piece, Board* board, char color) {
return knightAttacks(moving_piece) & not(getColoredPieces(board, color));
}
Bitboard knightFill(Bitboard moving_piece, int jumps) {
Bitboard fill = moving_piece;
int i;
for(i = 0; i < jumps; i++) {
fill |= knightAttacks(fill);
}
return fill;
}
int knightDistance(Bitboard leaving_square, Bitboard arriving_square) {
Bitboard fill = leaving_square;
int dist = 0;
while((fill & arriving_square) == 0) {
dist++;
fill |= knightAttacks(fill);
}
return dist;
}
// ========== KING ===========
Bitboard getKing(Board* board, char color) {
if(color == WHITE) {
return board->whiteKing;
} else {
return board->blackKing;
}
}
Bitboard kingAttacks(Bitboard moving_piece) {
Bitboard kingAtks = moving_piece | east(moving_piece) | west(moving_piece);
kingAtks |= north(kingAtks) | south(kingAtks);
return kingAtks & not(moving_piece);
}
Bitboard kingMoves(Bitboard moving_piece, Board* board, char color) {
return kingAttacks(moving_piece) & not(getColoredPieces(board, color));
}
BOOL canCastleKingside(Position* position, char color) {
Bitboard emptyPositions = getEmptySquares(&(position->board));
switch(color) {
case WHITE:
if((position->castlingRights & CASTLE_KINGSIDE_WHITE) &&
(FILE_E & RANK_1 & position->board.whiteKing) && (FILE_F & RANK_1 & emptyPositions) &&
(FILE_G & RANK_1 & emptyPositions) && (FILE_H & RANK_1 & position->board.whiteRooks) &&
(!isAttacked((FILE_E & RANK_1), &(position->board), opponent(color))) &&
(!isAttacked((FILE_F & RANK_1), &(position->board), opponent(color))) &&
(!isAttacked((FILE_G & RANK_1), &(position->board), opponent(color))))
return TRUE;
else
return FALSE;
case BLACK:
if((position->castlingRights & CASTLE_KINGSIDE_BLACK) &&
(FILE_E & RANK_8 & position->board.blackKing) && (FILE_F & RANK_8 & emptyPositions) &&
(FILE_G & RANK_8 & emptyPositions) && (FILE_H & RANK_8 & position->board.blackRooks) &&
(!isAttacked((FILE_E & RANK_8), &(position->board), opponent(color))) &&
(!isAttacked((FILE_F & RANK_8), &(position->board), opponent(color))) &&
(!isAttacked((FILE_G & RANK_8), &(position->board), opponent(color))))
return TRUE;
else
return FALSE;
}
return FALSE;
}
BOOL canCastleQueenside(Position* position, char color) {
Bitboard emptyPositions = getEmptySquares(&(position->board));
switch(color) {
case WHITE:
if((position->castlingRights & CASTLE_QUEENSIDE_WHITE) &&
(FILE_A & RANK_1 & position->board.whiteRooks) && (FILE_B & RANK_1 & emptyPositions) &&
(FILE_C & RANK_1 & emptyPositions) && (FILE_D & RANK_1 & emptyPositions) &&
(FILE_E & RANK_1 & position->board.whiteKing) &&
(!isAttacked((FILE_C & RANK_1), &(position->board), opponent(color))) &&
(!isAttacked((FILE_D & RANK_1), &(position->board), opponent(color))) &&
(!isAttacked((FILE_E & RANK_1), &(position->board), opponent(color))))
return TRUE;
else
return FALSE;
case BLACK:
if((position->castlingRights & CASTLE_QUEENSIDE_BLACK) &&
(FILE_A & RANK_8 & position->board.blackRooks) && (FILE_B & RANK_8 & emptyPositions) &&
(FILE_C & RANK_8 & emptyPositions) && (FILE_D & RANK_8 & emptyPositions) &&
(FILE_E & RANK_8 & position->board.blackKing) &&
(!isAttacked((FILE_C & RANK_8), &(position->board), opponent(color))) &&
(!isAttacked((FILE_D & RANK_8), &(position->board), opponent(color))) &&
(!isAttacked((FILE_E & RANK_8), &(position->board), opponent(color))))
return TRUE;
else
return FALSE;
}
return FALSE;
}
char removeCastlingRights(char original_rights, char removed_rights) {
return (char)(original_rights & ~(removed_rights));
}
// ========== BISHOP =========
Bitboard getBishops(Board* board) {
return board->whiteBishops | board->blackBishops;
}
Bitboard NE_ray(Bitboard bb) {
int i;
Bitboard ray = NE(bb);
for(i = 0; i < 6; i++) {
ray |= NE(ray);
}
return ray & ALL_SQUARES;
}
Bitboard SE_ray(Bitboard bb) {
int i;
Bitboard ray = SE(bb);
for(i = 0; i < 6; i++) {
ray |= SE(ray);
}
return ray & ALL_SQUARES;
}
Bitboard NW_ray(Bitboard bb) {
int i;
Bitboard ray = NW(bb);
for(i = 0; i < 6; i++) {
ray |= NW(ray);
}
return ray & ALL_SQUARES;
}
Bitboard SW_ray(Bitboard bb) {
int i;
Bitboard ray = SW(bb);
for(i = 0; i < 6; i++) {
ray |= SW(ray);
}
return ray & ALL_SQUARES;
}
Bitboard NE_attack(Bitboard single_piece, Board* board, char color) {
Bitboard blocker = lsb(NE_ray(single_piece) & getOccupiedSquares(board));
if(blocker) {
return NE_ray(single_piece) ^ NE_ray(blocker);
} else {
return NE_ray(single_piece);
}
}
Bitboard NW_attack(Bitboard single_piece, Board* board, char color) {
Bitboard blocker = lsb(NW_ray(single_piece) & getOccupiedSquares(board));
if(blocker) {
return NW_ray(single_piece) ^ NW_ray(blocker);
} else {
return NW_ray(single_piece);
}
}
Bitboard SE_attack(Bitboard single_piece, Board* board, char color) {
Bitboard blocker = msb(SE_ray(single_piece) & getOccupiedSquares(board));
if(blocker) {
return SE_ray(single_piece) ^ SE_ray(blocker);
} else {
return SE_ray(single_piece);
}
}
Bitboard SW_attack(Bitboard single_piece, Board* board, char color) {
Bitboard blocker = msb(SW_ray(single_piece) & getOccupiedSquares(board));
if(blocker) {
return SW_ray(single_piece) ^ SW_ray(blocker);
} else {
return SW_ray(single_piece);
}
}
Bitboard diagonalAttacks(Bitboard single_piece, Board* board, char color) {
return NE_attack(single_piece, board, color) | SW_attack(single_piece, board, color);
}
Bitboard antiDiagonalAttacks(Bitboard single_piece, Board* board, char color) {
return NW_attack(single_piece, board, color) | SE_attack(single_piece, board, color);
}
Bitboard bishopAttacks(Bitboard moving_pieces, Board* board, char color) {
return diagonalAttacks(moving_pieces, board, color) |
antiDiagonalAttacks(moving_pieces, board, color);
}
Bitboard bishopMoves(Bitboard moving_piece, Board* board, char color) {
return bishopAttacks(moving_piece, board, color) & not(getColoredPieces(board, color));
}
// ========== ROOK ===========
Bitboard getRooks(Board* board) {
return board->whiteRooks | board->blackRooks;
}
Bitboard northRay(Bitboard moving_pieces) {
Bitboard ray_atks = north(moving_pieces);
int i;
for(i = 0; i < 6; i++) {
ray_atks |= north(ray_atks);
}
return ray_atks & ALL_SQUARES;
}
Bitboard southRay(Bitboard moving_pieces) {
Bitboard ray_atks = south(moving_pieces);
int i;
for(i = 0; i < 6; i++) {
ray_atks |= south(ray_atks);
}
return ray_atks & ALL_SQUARES;
}
Bitboard eastRay(Bitboard moving_pieces) {
Bitboard ray_atks = east(moving_pieces);
int i;
for(i = 0; i < 6; i++) {
ray_atks |= east(ray_atks);
}
return ray_atks & ALL_SQUARES;
}
Bitboard westRay(Bitboard moving_pieces) {
Bitboard ray_atks = west(moving_pieces);
int i;
for(i = 0; i < 6; i++) {
ray_atks |= west(ray_atks);
}
return ray_atks & ALL_SQUARES;
}
Bitboard northAttack(Bitboard single_piece, Board* board, char color) {
Bitboard blocker = lsb(northRay(single_piece) & getOccupiedSquares(board));
if(blocker)
return northRay(single_piece) ^ northRay(blocker);
else
return northRay(single_piece);
}
Bitboard southAttack(Bitboard single_piece, Board* board, char color) {
Bitboard blocker = msb(southRay(single_piece) & getOccupiedSquares(board));
if(blocker)
return southRay(single_piece) ^ southRay(blocker);
else
return southRay(single_piece);
}
Bitboard fileAttacks(Bitboard single_piece, Board* board, char color) {
return northAttack(single_piece, board, color) | southAttack(single_piece, board, color);
}
Bitboard eastAttack(Bitboard single_piece, Board* board, char color) {
Bitboard blocker = lsb(eastRay(single_piece) & getOccupiedSquares(board));
if(blocker)
return eastRay(single_piece) ^ eastRay(blocker);
else
return eastRay(single_piece);
}
Bitboard westAttack(Bitboard single_piece, Board* board, char color) {
Bitboard blocker = msb(westRay(single_piece) & getOccupiedSquares(board));
if(blocker)
return westRay(single_piece) ^ westRay(blocker);
else
return westRay(single_piece);
}
Bitboard rankAttacks(Bitboard single_piece, Board* board, char color) {
return eastAttack(single_piece, board, color) | westAttack(single_piece, board, color);
}
Bitboard rookAttacks(Bitboard moving_piece, Board* board, char color) {
return fileAttacks(moving_piece, board, color) | rankAttacks(moving_piece, board, color);
}
Bitboard rookMoves(Bitboard moving_piece, Board* board, char color) {
return rookAttacks(moving_piece, board, color) & not(getColoredPieces(board, color));
}
// ========== QUEEN ==========
Bitboard getQueens(Board* board) {
return board->whiteQueens | board->blackQueens;
}
Bitboard queenAttacks(Bitboard moving_piece, Board* board, char color) {
return rookAttacks(moving_piece, board, color) | bishopAttacks(moving_piece, board, color);
}
Bitboard queenMoves(Bitboard moving_piece, Board* board, char color) {
return rookMoves(moving_piece, board, color) | bishopMoves(moving_piece, board, color);
}
// ======== MAKE MOVE ========
void clearPositions(Board* board, Bitboard positions) {
board->whiteKing = board->whiteKing & not(positions);
board->whiteQueens = board->whiteQueens & not(positions);
board->whiteRooks = board->whiteRooks & not(positions);
board->whiteKnights = board->whiteKnights & not(positions);
board->whiteBishops = board->whiteBishops & not(positions);
board->whitePawns = board->whitePawns & not(positions);
board->blackKing = board->blackKing & not(positions);
board->blackQueens = board->blackQueens & not(positions);
board->blackRooks = board->blackRooks & not(positions);
board->blackKnights = board->blackKnights & not(positions);
board->blackBishops = board->blackBishops & not(positions);
board->blackPawns = board->blackPawns & not(positions);
}
void movePiece(Board* board, Move move) {
Bitboard leaving = index2bb(getFrom(move));
Bitboard arriving = index2bb(getTo(move));
if(leaving & board->whiteKing) {
clearPositions(board, arriving | leaving);
board->whiteKing = arriving;
} else if(leaving & board->whiteQueens) {
clearPositions(board, arriving | leaving);
board->whiteQueens = board->whiteQueens | arriving;
} else if(leaving & board->whiteRooks) {
clearPositions(board, arriving | leaving);
board->whiteRooks = board->whiteRooks | arriving;
} else if(leaving & board->whiteKnights) {
clearPositions(board, arriving | leaving);
board->whiteKnights = board->whiteKnights | arriving;
} else if(leaving & board->whiteBishops) {
clearPositions(board, arriving | leaving);
board->whiteBishops = board->whiteBishops | arriving;
} else if(leaving & board->whitePawns) {
clearPositions(board, arriving | leaving);
board->whitePawns = board->whitePawns | arriving;
} else if(leaving & board->blackKing) {
clearPositions(board, arriving | leaving);
board->blackKing = arriving;
} else if(leaving & board->blackQueens) {
clearPositions(board, arriving | leaving);
board->blackQueens = board->blackQueens | arriving;
} else if(leaving & board->blackRooks) {
clearPositions(board, arriving | leaving);
board->blackRooks = board->blackRooks | arriving;
} else if(leaving & board->blackKnights) {
clearPositions(board, arriving | leaving);
board->blackKnights = board->blackKnights | arriving;
} else if(leaving & board->blackBishops) {
clearPositions(board, arriving | leaving);
board->blackBishops = board->blackBishops | arriving;
} else if(leaving & board->blackPawns) {
clearPositions(board, arriving | leaving);
board->blackPawns = board->blackPawns | arriving;
}
}
void updatePosition(Position* newPosition, Position* position, Move move) {
memcpy(newPosition, position, sizeof(Position));
int leavingSquare = getFrom(move);
int arrivingSquare = getTo(move);
Bitboard leavingBB = index2bb(leavingSquare);
Bitboard arrivingBB = index2bb(arrivingSquare);
// ===== MOVE PIECE =====
movePiece(&(newPosition->board), move);
// ===== TO MOVE =====
newPosition->toMove = opponent(position->toMove);
// ===== MOVE COUNTS =====
newPosition->halfmoveClock += 1;
if(position->toMove == BLACK) {
newPosition->fullmoveNumber += 1;
}
if(arrivingBB & getOccupiedSquares(&(position->board))) {
newPosition->halfmoveClock = 0;
}
// ===== PAWNS =====
newPosition->epSquare = -1;
if((leavingBB & position->board.whitePawns) | (leavingBB & position->board.blackPawns)) {
newPosition->halfmoveClock = 0;
if(arrivingSquare == position->epSquare) {
if(index2bb(position->epSquare) & RANK_3) {
clearPositions(&(newPosition->board), index2bb((int)(position->epSquare + 8)));
}
if(index2bb(position->epSquare) & RANK_6) {
clearPositions(&(newPosition->board), index2bb((int)(position->epSquare - 8)));
}
}
if(isDoublePush(leavingSquare, arrivingSquare)) {
newPosition->epSquare = getEpSquare(leavingSquare);
}
if(arrivingBB & (RANK_1 | RANK_8)) {
clearPositions(&(newPosition->board), arrivingBB);
if(position->toMove == WHITE) {
newPosition->board.whiteQueens = newPosition->board.whiteQueens | arrivingBB;
} else {
newPosition->board.blackQueens = newPosition->board.blackQueens | arrivingBB;
}
}
}
// ===== CASTLING =====
if(leavingSquare == str2index("a1")) {
newPosition->castlingRights =
removeCastlingRights(newPosition->castlingRights, CASTLE_QUEENSIDE_WHITE);
} else if(leavingSquare == str2index("h1")) {
newPosition->castlingRights =
removeCastlingRights(newPosition->castlingRights, CASTLE_KINGSIDE_WHITE);
} else if(leavingSquare == str2index("a8")) {
newPosition->castlingRights =
removeCastlingRights(newPosition->castlingRights, CASTLE_QUEENSIDE_BLACK);
} else if(leavingSquare == str2index("h8")) {
newPosition->castlingRights =
removeCastlingRights(newPosition->castlingRights, CASTLE_KINGSIDE_BLACK);
}
if(leavingBB & position->board.whiteKing) {
newPosition->castlingRights = removeCastlingRights(
newPosition->castlingRights, (CASTLE_KINGSIDE_WHITE | CASTLE_QUEENSIDE_WHITE));
if(leavingSquare == str2index("e1")) {
if(arrivingSquare == str2index("g1"))
movePiece(&(newPosition->board), generateMove(str2index("h1"), str2index("f1")));
if(arrivingSquare == str2index("c1"))
movePiece(&(newPosition->board), generateMove(str2index("a1"), str2index("d1")));
}
} else if(leavingBB & position->board.blackKing) {
newPosition->castlingRights = removeCastlingRights(
newPosition->castlingRights, CASTLE_KINGSIDE_BLACK | CASTLE_QUEENSIDE_BLACK);
if(leavingSquare == str2index("e8")) {
if(arrivingSquare == str2index("g8"))
movePiece(&(newPosition->board), generateMove(str2index("h8"), str2index("f8")));
if(arrivingSquare == str2index("c8"))
movePiece(&(newPosition->board), generateMove(str2index("a8"), str2index("d8")));
}
}
}
void makeMove(Game* game, Move move) {
Position newPosition;
updatePosition(&newPosition, &(game->position), move);
memcpy(&(game->position), &newPosition, sizeof(Position));
game->moveListLen += 1;
// ===== MOVE LIST =====
game->moveList[game->moveListLen - 1] = move;
// ===== POSITION HISTORY =====
toFen(game->positionHistory[game->moveListLen], &game->position);
}
void unmakeMove(Game* game) {
Position newPosition;
if(game->moveListLen >= 1) {
loadFen(&newPosition, game->positionHistory[game->moveListLen - 1]);
memcpy(&(game->position), &newPosition, sizeof(Position));
game->moveList[game->moveListLen - 1] = 0;
memset(game->positionHistory[game->moveListLen], 0, MAX_FEN_LEN * sizeof(char));
game->moveListLen -= 1;
} else { // return to initial game
loadFen(&newPosition, game->positionHistory[0]);
memcpy(&(game->position), &newPosition, sizeof(Position));
game->moveListLen = 0;
memset(game->moveList, 0, MAX_PLYS_PER_GAME * sizeof(int));
memset(&game->positionHistory[1], 0, (MAX_PLYS_PER_GAME - 1) * MAX_FEN_LEN * sizeof(char));
}
}
// ======== MOVE GEN =========
Bitboard getMoves(Bitboard movingPiece, Position* position, char color) {
if((movingPiece & position->board.whitePawns) | (movingPiece & position->board.blackPawns))
return pawnMoves(movingPiece, position, color);
if((movingPiece & position->board.whiteKnights) | (movingPiece & position->board.blackKnights))
return knightMoves(movingPiece, &(position->board), color);
if((movingPiece & position->board.whiteBishops) | (movingPiece & position->board.blackBishops))
return bishopMoves(movingPiece, &(position->board), color);
if((movingPiece & position->board.whiteRooks) | (movingPiece & position->board.blackRooks))
return rookMoves(movingPiece, &(position->board), color);
if((movingPiece & position->board.whiteQueens) | (movingPiece & position->board.blackQueens))
return queenMoves(movingPiece, &(position->board), color);
if((movingPiece & position->board.whiteKing) | (movingPiece & position->board.blackKing))
return kingMoves(movingPiece, &(position->board), color);
return 0;
}
int pseudoLegalMoves(Move* moves, Position* position, char color) {
int leavingSquare, arrivingSquare, moveCount = 0;
Bitboard leavingBB = 1;
Bitboard attackers = getColoredPieces(&(position->board), color);
for(leavingSquare = 0; leavingSquare < NUM_SQUARES; leavingSquare++) {
if(leavingBB & attackers) {
Bitboard targets = getMoves(leavingBB, position, color);
for(arrivingSquare = 0; arrivingSquare < NUM_SQUARES; arrivingSquare++) {
if(isSet(targets, arrivingSquare)) {
moves[moveCount++] = generateMove(leavingSquare, arrivingSquare);
}
}
if((leavingBB & position->board.whiteKing) | (leavingBB & position->board.blackKing)) {
if(canCastleKingside(position, color)) {
moves[moveCount++] = generateMove(leavingSquare, leavingSquare + 2);
}
if(canCastleQueenside(position, color)) {
moves[moveCount++] = generateMove(leavingSquare, leavingSquare - 2);
}
}
}
leavingBB <<= 1;
}
return moveCount;
}
Bitboard getAttacks(Bitboard movingPiece, Board* board, char color) {
if((movingPiece & board->whitePawns) | (movingPiece & board->blackPawns))
return pawnAttacks(movingPiece, board, color);
if((movingPiece & board->whiteKnights) | (movingPiece & board->blackKnights))
return knightAttacks(movingPiece);
if((movingPiece & board->whiteBishops) | (movingPiece & board->blackBishops))
return bishopAttacks(movingPiece, board, color);
if((movingPiece & board->whiteRooks) | (movingPiece & board->blackRooks))
return rookAttacks(movingPiece, board, color);
if((movingPiece & board->whiteQueens) | (movingPiece & board->blackQueens))
return queenAttacks(movingPiece, board, color);
if((movingPiece & board->whiteKing) | (movingPiece & board->blackKing))
return kingAttacks(movingPiece);
return 0;
}
int countAttacks(Bitboard target, Board* board, char color) {
int i, attackCount = 0;
Bitboard attackers = getColoredPieces(board, color);
Bitboard position = 1;
for(i = 0; i < NUM_SQUARES; i++) {
if(position & attackers) {
if(getAttacks(position, board, color) & target) {
attackCount += 1;
}
}
position = position << 1;
}
return attackCount;
}
BOOL isAttacked(Bitboard target, Board* board, char color) {
int i = 0;
Bitboard attackers = getColoredPieces(board, color);
Bitboard position = 1;
for(i = 0; i < NUM_SQUARES; i++) {
if(position & attackers) {
if(getAttacks(position, board, color) & target) {
return TRUE;
}
}
position = position << 1;
}
return FALSE;
}
BOOL isCheck(Board* board, char color) {
return isAttacked(getKing(board, color), board, opponent(color));
}
BOOL isLegalMove(Position* position, Move move) {
Position newPosition;
updatePosition(&newPosition, position, move);
if(isCheck(&(newPosition.board), position->toMove)) return FALSE;
return TRUE;
}
int legalMoves(Move* legalMoves, Position* position, char color) {
int i, legalCount = 0;
Move pseudoMoves[MAX_BRANCHING_FACTOR];
int pseudoCount = pseudoLegalMoves(pseudoMoves, position, color);
for(i = 0; i < pseudoCount; i++) {
if(isLegalMove(position, pseudoMoves[i])) {
legalMoves[legalCount++] = pseudoMoves[i];
}
}
return legalCount;
}
int legalMovesCount(Position* position, char color) {
int i, legalCount = 0;
Move pseudoMoves[MAX_BRANCHING_FACTOR];
int pseudoCount = pseudoLegalMoves(pseudoMoves, position, color);
for(i = 0; i < pseudoCount; i++) {
if(isLegalMove(position, pseudoMoves[i])) {
legalCount++;
}
}
return legalCount;
}
int staticOrderLegalMoves(Move* orderedLegalMoves, Position* position, char color) {
Move moves[MAX_BRANCHING_FACTOR];
int legalCount = legalMoves(moves, position, color);
Position newPosition;
Node nodes[legalCount], orderedNodes[legalCount];
int i;
for(i = 0; i < legalCount; i++) {
updatePosition(&newPosition, position, moves[i]);
nodes[i] = (Node){.move = moves[i], .score = staticEvaluation(&newPosition)};
}
sortNodes(orderedNodes, nodes, legalCount, color);
for(i = 0; i < legalCount; i++) {
orderedLegalMoves[i] = orderedNodes[i].move;
}
return legalCount;
}
int legalCaptures(Move* legalCaptures, Position* position, char color) {
int i, captureCount = 0;
Move moves[MAX_BRANCHING_FACTOR];
int legalCount = legalMoves(moves, position, color);
for(i = 0; i < legalCount; i++) {
int arrivingSquare = getTo(moves[i]);
if(index2bb(arrivingSquare) & getColoredPieces(&(position->board), opponent(color))) {
legalCaptures[captureCount++] = moves[i];
}
}
return captureCount;
}
// ====== GAME CONTROL =======
BOOL isCheckmate(Position* position) {
if(isCheck(&(position->board), position->toMove) &&
legalMovesCount(position, position->toMove) == 0)
return TRUE;
else
return FALSE;
}
BOOL isStalemate(Position* position) {
if(!isCheck(&(position->board), position->toMove) &&
legalMovesCount(position, position->toMove) == 0)
return TRUE;
else
return FALSE;
}
BOOL hasInsufficientMaterial(Board* board) {
int pieceCount = countBits(getOccupiedSquares(board));
if(pieceCount <= 3) {
if(pieceCount == 2 || getKnights(board) != 0 || getBishops(board) != 0) return TRUE;
}
return FALSE;
}
BOOL isEndgame(Board* board) {
if(countBits(getOccupiedSquares(board)) <= ENDGAME_PIECE_COUNT) return TRUE;
return FALSE;
}
BOOL isOver75MovesRule(Position* position) {
if(position->halfmoveClock >= 150)
return TRUE;
else
return FALSE;
}
BOOL hasGameEnded(Position* position) {
if(isCheckmate(position) || isStalemate(position) ||
hasInsufficientMaterial(&(position->board)) || isOver75MovesRule(position))
return TRUE;
else
return FALSE;
}
void printOutcome(Position* position) {
if(isCheckmate(position) && position->toMove == BLACK) printf("WHITE wins!\n");
if(isCheckmate(position) && position->toMove == WHITE) printf("BLACK wins!\n");
if(isStalemate(position)) printf("Draw by stalemate!\n");
if(hasInsufficientMaterial(&(position->board))) printf("Draw by insufficient material!\n");
if(isOver75MovesRule(position)) printf("Draw by 75 move rule!\n");
fflush(stdout);
}
// ========== EVAL ===========
int winScore(char color) {
if(color == WHITE) return 10 * PIECE_VALUES[KING];
if(color == BLACK) return -10 * PIECE_VALUES[KING];
return 0;
}
int materialSum(Board* board, char color) {
if(color == WHITE) {
return PIECE_VALUES[PAWN] * countBits(board->whitePawns) +
PIECE_VALUES[KNIGHT] * countBits(board->whiteKnights) +
PIECE_VALUES[BISHOP] * countBits(board->whiteBishops) +
PIECE_VALUES[ROOK] * countBits(board->whiteRooks) +
PIECE_VALUES[QUEEN] * countBits(board->whiteQueens);
} else {
return PIECE_VALUES[PAWN] * countBits(board->blackPawns) +
PIECE_VALUES[KNIGHT] * countBits(board->blackKnights) +
PIECE_VALUES[BISHOP] * countBits(board->blackBishops) +
PIECE_VALUES[ROOK] * countBits(board->blackRooks) +
PIECE_VALUES[QUEEN] * countBits(board->blackQueens);
}
}
int materialBalance(Board* board) {
return materialSum(board, WHITE) - materialSum(board, BLACK);
}
int positionalBonus(Board* board, char color) {
int bonus = 0;
Bitboard positionBB;
Bitboard coloredPieces = getColoredPieces(board, color);
int i;
for(i = 0; i < NUM_SQUARES; i++) {
positionBB = index2bb(i);
if(positionBB & coloredPieces) {
if((positionBB & board->whitePawns) | (positionBB & board->blackPawns)) {
if(color == WHITE) {
bonus += PAWN_BONUS[i];
} else {
bonus += PAWN_BONUS[FLIP_VERTICAL[i]];
}
if(isDoubledPawn(positionBB, board, color)) {
bonus -= DOUBLED_PAWN_PENALTY / 2;
}
if(isPassedPawn(positionBB, board, color)) {
bonus += PASSED_PAWN_BONUS;
}
if(isIsolatedPawn(positionBB, board, color)) {
bonus -= ISOLATED_PAWN_PENALTY;
} else if(isBackwardsPawn(positionBB, board, color)) {
bonus -= BACKWARDS_PAWN_PENALTY;
}
} else if((positionBB & board->whiteKnights) | (positionBB & board->blackKnights)) {
if(color == WHITE) {
bonus += KNIGHT_BONUS[i];
} else {
bonus += KNIGHT_BONUS[FLIP_VERTICAL[i]];
}
} else if((positionBB & board->whiteBishops) | (positionBB & board->blackBishops)) {
if(color == WHITE) {
bonus += BISHOP_BONUS[i];
} else {
bonus += BISHOP_BONUS[FLIP_VERTICAL[i]];
}
} else if((positionBB & board->whiteRooks) | (positionBB & board->blackRooks)) {
if(isOpenFile(positionBB, board)) {
bonus += ROOK_OPEN_FILE_BONUS;
} else if(isSemiOpenFile(positionBB, board)) {
bonus += ROOK_SEMI_OPEN_FILE_BONUS;
}
if(color == WHITE) {
if(positionBB & RANK_7) {
bonus += ROOK_ON_SEVENTH_BONUS;
}
} else {
if(positionBB & RANK_2) {
bonus += ROOK_ON_SEVENTH_BONUS;
}
}
} else if((positionBB & board->whiteKing) | (positionBB & board->blackKing)) {
if(isEndgame(board)) {
if(color == WHITE) {
bonus += KING_ENDGAME_BONUS[i];
} else {
bonus += KING_ENDGAME_BONUS[FLIP_VERTICAL[i]];
}
} else {
if(color == WHITE) {
bonus += KING_BONUS[i];
} else {
bonus += KING_BONUS[FLIP_VERTICAL[i]];
}
}
}
}
}
return bonus;
}
int positionalBalance(Board* board) {
return positionalBonus(board, WHITE) - positionalBonus(board, BLACK);
}
int endNodeEvaluation(Position* position) {
if(isCheckmate(position)) {
return winScore(opponent(position->toMove));
}
if(isStalemate(position) || hasInsufficientMaterial(&(position->board)) ||
isOver75MovesRule(position)) {
return 0;
}
return 0;
}
int staticEvaluation(Position* position) {
if(hasGameEnded(position))
return endNodeEvaluation(position);
else
return materialBalance(&(position->board)) + positionalBalance(&(position->board));
}
int getPieceValue(Bitboard position, Board* board) {
if((position & board->whitePawns) | (position & board->blackPawns)) return PIECE_VALUES[PAWN];
if((position & board->whiteKnights) | (position & board->blackKnights))
return PIECE_VALUES[KNIGHT];
if((position & board->whiteBishops) | (position & board->blackBishops))
return PIECE_VALUES[BISHOP];
if((position & board->whiteRooks) | (position & board->blackRooks)) return PIECE_VALUES[ROOK];
if((position & board->whiteQueens) | (position & board->blackQueens))
return PIECE_VALUES[QUEEN];
if((position & board->whiteKing) | (position & board->blackKing)) return PIECE_VALUES[KING];
return 0;
}
int getCaptureSequence(Move* captures, Position* position, int targetSquare) {
Move allCaptures[MAX_BRANCHING_FACTOR], targetCaptures[MAX_ATTACKING_PIECES];
int captureCount = legalCaptures(allCaptures, position, position->toMove);
int i, j, targetCount = 0;
for(i = 0; i < captureCount; i++) {
if(getTo(allCaptures[i]) == targetSquare) {
targetCaptures[targetCount++] = allCaptures[i];
}
}
Move captureBuffer[targetCount];
BOOL sorted;
for(i = 0; i < targetCount; i++) {
sorted = FALSE;
int pieceValue = getPieceValue(index2bb(getFrom(targetCaptures[i])), &(position->board));
for(j = 0; j < i; j++) {
int sortedPieceValue =
getPieceValue(index2bb(getFrom(captures[j])), &(position->board));
if(pieceValue < sortedPieceValue) {
sorted = TRUE;
memcpy(captureBuffer, &captures[j], (i - j) * sizeof(Move));
memcpy(&captures[j + 1], captureBuffer, (i - j) * sizeof(Move));
captures[j] = targetCaptures[i];
break;
}
}
if(sorted == FALSE) {
captures[i] = targetCaptures[i];
}
}
return targetCount;
}
int staticExchangeEvaluation(Position* position, int targetSquare) {
Move captures[MAX_ATTACKING_PIECES];
int attackCount = getCaptureSequence(captures, position, targetSquare);
int value = 0;
if(attackCount > 0) {
Position newPosition;
updatePosition(&newPosition, position, captures[0]);
int pieceValue = getPieceValue(index2bb(targetSquare), &(position->board));
value = pieceValue - staticExchangeEvaluation(&newPosition, targetSquare);
}
return value > 0 ? value : 0;
}
int quiescenceEvaluation(Position* position) {
int staticScore = staticEvaluation(position);
if(hasGameEnded(position)) return staticScore;
Move captures[MAX_BRANCHING_FACTOR];
int captureCount = legalCaptures(captures, position, position->toMove);
if(captureCount == 0) {
return staticScore;
} else {
Position newPosition;
int i, bestScore = staticScore;
for(i = 0; i < captureCount; i++) {
if(staticExchangeEvaluation(position, getTo(captures[i])) <= 0) continue;
updatePosition(&newPosition, position, captures[i]);
int score = quiescenceEvaluation(&newPosition);
if((position->toMove == WHITE && score > bestScore) ||
(position->toMove == BLACK && score < bestScore)) {
bestScore = score;
}
}
return bestScore;
}
}
// ========= SEARCH ==========
Node staticSearch(Position* position) {
int bestScore = position->toMove == WHITE ? INT32_MIN : INT32_MAX;
Move bestMove = 0;
Move moves[MAX_BRANCHING_FACTOR];
int moveCount = legalMoves(moves, position, position->toMove);
Position newPosition;
int i;
for(i = 0; i < moveCount; i++) {
updatePosition(&newPosition, position, moves[i]);
int score = staticEvaluation(&newPosition);
if(score == winScore(position->toMove)) {
return (Node){.move = moves[i], .score = score};
}
if((position->toMove == WHITE && score > bestScore) ||
(position->toMove == BLACK && score < bestScore)) {
bestScore = score;
bestMove = moves[i];
}
}
return (Node){.move = bestMove, .score = bestScore};
}
Node quiescenceSearch(Position* position) {
int bestScore = position->toMove == WHITE ? INT32_MIN : INT32_MAX;
Move bestMove = 0;
Move moves[MAX_BRANCHING_FACTOR];
int moveCount = legalMoves(moves, position, position->toMove);
Position newPosition;
int i;
for(i = 0; i < moveCount; i++) {
updatePosition(&newPosition, position, moves[i]);
int score = quiescenceEvaluation(&newPosition);
if(score == winScore(position->toMove)) {
return (Node){.move = moves[i], .score = score};
}
if((position->toMove == WHITE && score > bestScore) ||
(position->toMove == BLACK && score < bestScore)) {
bestScore = score;
bestMove = moves[i];
}
}
return (Node){.move = bestMove, .score = bestScore};
}
Node alphaBeta(Position* position, char depth, int alpha, int beta) {
if(hasGameEnded(position)) return (Node){.score = endNodeEvaluation(position)};
if(depth == 1) return staticSearch(position);
// Mate in 1
Node staticNode = staticSearch(position);
if(staticNode.score == winScore(position->toMove)) return staticNode;
Move bestMove = 0;
Move moves[MAX_BRANCHING_FACTOR];
int moveCount = staticOrderLegalMoves(moves, position, position->toMove);
Position newPosition;
int i;
for(i = 0; i < moveCount; i++) {
updatePosition(&newPosition, position, moves[i]);
int score = alphaBeta(&newPosition, depth - 1, alpha, beta).score;
if(score == winScore(position->toMove)) {
return (Node){.move = moves[i], .score = score};
}
if(position->toMove == WHITE && score > alpha) {
alpha = score;
bestMove = moves[i];
} else if(position->toMove == BLACK && score < beta) {
beta = score;
bestMove = moves[i];
}
if(alpha > beta) {
break;
}
}
return (Node){.move = bestMove, .score = position->toMove == WHITE ? alpha : beta};
}
int alphaBetaNodes(Node* sortedNodes, Position* position, char depth) {
Node nodes[MAX_BRANCHING_FACTOR];
Move moves[MAX_BRANCHING_FACTOR];
int moveCount = legalMoves(moves, position, position->toMove);
Position newPosition;
int i;
for(i = 0; i < moveCount; i++) {
updatePosition(&newPosition, position, moves[i]);
nodes[i].move = moves[i];
nodes[i].score = depth > 1 ?
alphaBeta(&newPosition, depth - 1, INT32_MIN, INT32_MAX).score :
staticEvaluation(&newPosition);
}
sortNodes(sortedNodes, nodes, moveCount, position->toMove);
return moveCount;
}
Node iterativeDeepeningAlphaBeta(Position* position, char depth, int alpha, int beta, BOOL verbose) {
if(hasGameEnded(position)) return (Node){.score = endNodeEvaluation(position)};
if(depth == 1) return quiescenceSearch(position);
// return staticSearch(position);
// Mate in 1
Node staticNode = staticSearch(position);
if(staticNode.score == winScore(position->toMove)) return staticNode;
Move bestMove = 0;
if(verbose) {
printf("Ordering moves...\n");
fflush(stdout);
}
Node nodes[MAX_BRANCHING_FACTOR];
int moveCount = alphaBetaNodes(nodes, position, depth - 1);
Position newPosition;
int i;
for(i = 0; i < moveCount; i++) {
updatePosition(&newPosition, position, nodes[i].move);
if(verbose) {
printf("(Move %2d/%d) ", i + 1, moveCount);
printFullMove(nodes[i].move, &(position->board));
printf(" = ");
fflush(stdout);
}
int score = iterativeDeepeningAlphaBeta(&newPosition, depth - 1, alpha, beta, FALSE).score;
if(verbose) {
printf("%.2f\n", (double)score / (double)100.00);
fflush(stdout);
}
if(score == winScore(position->toMove)) {
return (Node){.move = nodes[i].move, .score = score};
}
if(position->toMove == WHITE && score > alpha) {
alpha = score;
bestMove = nodes[i].move;
} else if(position->toMove == BLACK && score < beta) {
beta = score;
bestMove = nodes[i].move;
}
if(alpha > beta) {
break;
}
}
return (Node){.move = bestMove, .score = position->toMove == WHITE ? alpha : beta};
}
Node pIDAB(Position* position, char depth, int* p_alpha, int* p_beta) {
if(hasGameEnded(position)) return (Node){.score = endNodeEvaluation(position)};
if(depth == 1) return quiescenceSearch(position);
// Mate in 1
Node staticNode = staticSearch(position);
if(staticNode.score == winScore(position->toMove)) return staticNode;
Move bestMove = 0;
Node nodes[MAX_BRANCHING_FACTOR];
int moveCount = alphaBetaNodes(nodes, position, depth - 1);
Position newPosition;
int i;
int alpha = *p_alpha;
int beta = *p_beta;
for(i = 0; i < moveCount; i++) {
updatePosition(&newPosition, position, nodes[i].move);
int score = iterativeDeepeningAlphaBeta(&newPosition, depth - 1, alpha, beta, FALSE).score;
if(score == winScore(position->toMove)) {
return (Node){.move = nodes[i].move, .score = score};
}
if(position->toMove == WHITE && score > alpha) {
alpha = score;
bestMove = nodes[i].move;
} else if(position->toMove == BLACK && score < beta) {
beta = score;
bestMove = nodes[i].move;
}
if(alpha > beta || alpha > *p_beta || *p_alpha > beta) {
break;
}
}
return (Node){.move = bestMove, .score = position->toMove == WHITE ? alpha : beta};
}
Node pIDABhashed(Position* position, char depth, int* p_alpha, int* p_beta) {
if(hasGameEnded(position)) {
int score = endNodeEvaluation(position);
writeToHashFile(position, score, 0);
return (Node){.score = score};
}
if(depth <= 1) {
Node quie = quiescenceSearch(position);
writeToHashFile(position, quie.score, depth);
return quie;
}
// Mate in 1
Node staticNode = staticSearch(position);
if(staticNode.score == winScore(position->toMove)) {
writeToHashFile(position, staticNode.score, 1);
return staticNode;
}
Move bestMove = 0;
Node nodes[MAX_BRANCHING_FACTOR];
int moveCount = alphaBetaNodes(nodes, position, depth - 1);
Position newPosition;
int i;
int alpha = *p_alpha;
int beta = *p_beta;
for(i = 0; i < moveCount; i++) {
updatePosition(&newPosition, position, nodes[i].move);
int score = iterativeDeepeningAlphaBeta(&newPosition, depth - 1, alpha, beta, FALSE).score;
writeToHashFile(&newPosition, score, depth - 1);
if(score == winScore(position->toMove)) {
return (Node){.move = nodes[i].move, .score = score};
}
if(position->toMove == WHITE && score > alpha) {
alpha = score;
bestMove = nodes[i].move;
} else if(position->toMove == BLACK && score < beta) {
beta = score;
bestMove = nodes[i].move;
}
if(alpha > beta || alpha > *p_beta || *p_alpha > beta) {
break;
}
}
writeToHashFile(position, position->toMove == WHITE ? alpha : beta, depth);
return (Node){.move = bestMove, .score = position->toMove == WHITE ? alpha : beta};
}
// Parallel processing currently only implemented for Windows
#ifdef _WIN32
DWORD WINAPI evaluatePositionThreadFunction(LPVOID lpParam) {
ThreadInfo* tInfo = (ThreadInfo*)lpParam;
Position* pos = &tInfo->pos;
Node node = pIDAB(pos, tInfo->depth, tInfo->alpha, tInfo->beta);
if(pos->toMove == BLACK && node.score > *tInfo->alpha) {
*tInfo->alpha = node.score;
} else if(pos->toMove == WHITE && node.score < *tInfo->beta) {
*tInfo->beta = node.score;
}
if(tInfo->verbose) {
printf("-");
fflush(stdout);
}
return node.score;
}
DWORD WINAPI evaluatePositionThreadFunctionHashed(LPVOID lpParam) {
ThreadInfo* tInfo = (ThreadInfo*)lpParam;
Position* pos = &tInfo->pos;
Node node = pIDABhashed(pos, tInfo->depth, tInfo->alpha, tInfo->beta);
if(pos->toMove == BLACK && node.score > *tInfo->alpha) {
*tInfo->alpha = node.score;
} else if(pos->toMove == WHITE && node.score < *tInfo->beta) {
*tInfo->beta = node.score;
}
if(tInfo->verbose) {
printf("-");
fflush(stdout);
}
return node.score;
}
Node idabThreaded(Position* position, int depth, BOOL verbose) {
if(hasGameEnded(position)) return (Node){.score = endNodeEvaluation(position)};
if(depth <= 1) return quiescenceSearch(position);
int i;
Node nodes[MAX_BRANCHING_FACTOR];
int moveCount = alphaBetaNodes(nodes, position, depth - 1);
if(moveCount == 1) {
return nodes[0];
}
if(verbose) {
printf("Analyzing %d possible moves with base depth %d:\n[", moveCount, depth);
for(i = 0; i < moveCount; i++) printf(" ");
printf("]\r[");
fflush(stdout);
}
HANDLE threadHandles[MAX_BRANCHING_FACTOR];
ThreadInfo threadInfo[MAX_BRANCHING_FACTOR];
int alpha = INT32_MIN;
int beta = INT32_MAX;
for(i = 0; i < moveCount; i++) {
threadInfo[i].depth = depth - 1;
updatePosition(&threadInfo[i].pos, position, nodes[i].move);
threadInfo[i].alpha = &alpha;
threadInfo[i].beta = &beta;
threadInfo[i].verbose = verbose;
threadHandles[i] =
CreateThread(NULL, 0, evaluatePositionThreadFunction, (LPVOID)&threadInfo[i], 0, NULL);
if(threadHandles[i] == NULL) {
// printf("Error launching process on move #%d!\n", i);
printf("!");
fflush(stdout);
}
}
WaitForMultipleObjects((DWORD)moveCount, threadHandles, TRUE, INFINITE);
if(verbose) {
printf("]\n");
fflush(stdout);
}
Move bestMove = 0;
int bestMoveScore = position->toMove == WHITE ? INT32_MIN : INT32_MAX;
long unsigned int retVal;
int score;
for(i = 0; i < moveCount; i++) {
GetExitCodeThread(threadHandles[i], &retVal);
score = (int)retVal;
if((position->toMove == WHITE && score > bestMoveScore) ||
(position->toMove == BLACK && score < bestMoveScore)) {
bestMove = nodes[i].move;
bestMoveScore = score;
}
if(CloseHandle(threadHandles[i]) == 0) {
// printf("Error on closing thread #%d!\n", i);
printf("x");
fflush(stdout);
}
}
return (Node){.move = bestMove, .score = bestMoveScore};
}
Node idabThreadedBestFirst(Position* position, int depth, BOOL verbose) {
if(hasGameEnded(position)) return (Node){.score = endNodeEvaluation(position)};
if(depth <= 1) return quiescenceSearch(position);
int i;
Node nodes[MAX_BRANCHING_FACTOR];
int moveCount = alphaBetaNodes(nodes, position, depth - 1);
if(moveCount == 1) {
return nodes[0];
}
Position firstPos;
updatePosition(&firstPos, position, nodes[0].move);
Node firstReply = idabThreaded(&firstPos, depth - 1, FALSE);
if(firstReply.score == winScore(position->toMove)) {
if(verbose) {
printf("Playing checkmate move: ");
printFullMove(nodes[0].move, position->board);
printf(".\n");
}
return (Node){.move = nodes[0].move, .score = firstReply.score};
}
if(verbose) {
printf("Move ");
printFullMove(nodes[0].move, position->board);
printf(" had score of %+.2f.\n", firstReply.score / 100.0);
printf(
"Analyzing other %d possible moves with minimum depth of %d plies:\n[",
moveCount - 1,
depth);
for(i = 0; i < moveCount - 1; i++) printf(" ");
printf("]\r[");
fflush(stdout);
}
HANDLE threadHandles[MAX_BRANCHING_FACTOR];
ThreadInfo threadInfo[MAX_BRANCHING_FACTOR];
int alpha = INT32_MIN;
int beta = INT32_MAX;
if(position->toMove == WHITE) {
alpha = firstReply.score;
} else {
beta = firstReply.score;
}
for(i = 0; i < moveCount - 1; i++) {
threadInfo[i].depth = depth - 1;
updatePosition(&threadInfo[i].pos, position, nodes[i + 1].move);
threadInfo[i].alpha = &alpha;
threadInfo[i].beta = &beta;
threadInfo[i].verbose = verbose;
threadHandles[i] =
CreateThread(NULL, 0, evaluatePositionThreadFunction, (LPVOID)&threadInfo[i], 0, NULL);
if(threadHandles[i] == NULL) {
// printf("Error launching process on move #%d!\n", i);
printf("!");
fflush(stdout);
}
}
WaitForMultipleObjects((DWORD)moveCount - 1, threadHandles, TRUE, INFINITE);
if(verbose) {
printf("] Done!\n");
fflush(stdout);
}
Move bestMove = nodes[0].move;
int bestMoveScore = firstReply.score;
long unsigned int retVal;
int score;
for(i = 0; i < moveCount - 1; i++) {
GetExitCodeThread(threadHandles[i], &retVal);
score = (int)retVal;
if((position->toMove == WHITE && score > bestMoveScore) ||
(position->toMove == BLACK && score < bestMoveScore)) {
bestMove = nodes[i + 1].move;
bestMoveScore = score;
}
if(CloseHandle(threadHandles[i]) == 0) {
// printf("Error on closing thread #%d!\n", i);
printf("x");
fflush(stdout);
}
}
return (Node){.move = bestMove, .score = bestMoveScore};
}
Node idabThreadedBestFirstHashed(Position* position, int depth, BOOL verbose) {
if(hasGameEnded(position)) {
int score = endNodeEvaluation(position);
writeToHashFile(position, score, 0);
return (Node){.score = score};
}
if(depth <= 1) {
Node quie = quiescenceSearch(position);
writeToHashFile(position, quie.score, depth);
return quie;
}
int i;
Node nodes[MAX_BRANCHING_FACTOR];
int moveCount = alphaBetaNodes(nodes, position, depth - 1);
if(moveCount == 1) {
return nodes[0];
}
Position firstPos;
updatePosition(&firstPos, position, nodes[0].move);
Node firstReply = idabThreaded(&firstPos, depth - 1, FALSE);
if(firstReply.score == winScore(position->toMove)) {
if(verbose) {
printf("Playing checkmate move: ");
printFullMove(nodes[0].move, position->board);
printf(".\n");
}
writeToHashFile(position, firstReply.score, depth);
return (Node){.move = nodes[0].move, .score = firstReply.score};
}
if(verbose) {
printf("Move ");
printFullMove(nodes[0].move, position->board);
printf(" had score of %+.2f.\n", firstReply.score / 100.0);
printf(
"Analyzing other %d possible moves with minimum depth of %d plies:\n[",
moveCount - 1,
depth);
for(i = 0; i < moveCount - 1; i++) printf(" ");
printf("]\r[");
fflush(stdout);
}
HANDLE threadHandles[MAX_BRANCHING_FACTOR];
ThreadInfo threadInfo[MAX_BRANCHING_FACTOR];
int alpha = INT32_MIN;
int beta = INT32_MAX;
if(position->toMove == WHITE) {
alpha = firstReply.score;
} else {
beta = firstReply.score;
}
for(i = 0; i < moveCount - 1; i++) {
threadInfo[i].depth = depth - 1;
updatePosition(&threadInfo[i].pos, position, nodes[i + 1].move);
threadInfo[i].alpha = &alpha;
threadInfo[i].beta = &beta;
threadInfo[i].verbose = verbose;
threadHandles[i] = CreateThread(
NULL, 0, evaluatePositionThreadFunctionHashed, (LPVOID)&threadInfo[i], 0, NULL);
if(threadHandles[i] == NULL) {
// printf("Error launching process on move #%d!\n", i);
printf("!");
fflush(stdout);
}
}
WaitForMultipleObjects((DWORD)moveCount - 1, threadHandles, TRUE, INFINITE);
if(verbose) {
printf("] Done!\n");
fflush(stdout);
}
Move bestMove = nodes[0].move;
int bestMoveScore = firstReply.score;
long unsigned int retVal;
int score;
for(i = 0; i < moveCount - 1; i++) {
GetExitCodeThread(threadHandles[i], &retVal);
score = (int)retVal;
writeToHashFile(&threadInfo[i].pos, score, depth - 1);
if((position->toMove == WHITE && score > bestMoveScore) ||
(position->toMove == BLACK && score < bestMoveScore)) {
bestMove = nodes[i + 1].move;
bestMoveScore = score;
}
if(CloseHandle(threadHandles[i]) == 0) {
// printf("Error on closing thread #%d!\n", i);
printf("x");
fflush(stdout);
}
}
writeToHashFile(position, bestMoveScore, depth);
return (Node){.move = bestMove, .score = bestMoveScore};
}
#endif /* _WIN32 */
Move getRandomMove(Position* position) {
Move moves[MAX_BRANCHING_FACTOR];
int totalMoves = legalMoves(moves, position, position->toMove);
int chosenMove = rand() % totalMoves;
return moves[chosenMove];
}
Move getAIMove(Game* game, int depth) {
printf("--- AI ---\n");
fflush(stdout);
if(fromInitial(game) && countBookOccurrences(game) > 0) {
printf("There are %d available book continuations.\n", countBookOccurrences(game));
fflush(stdout);
Move bookMove = getBookMove(game);
printf("CHOSEN book move: ");
printFullMove(bookMove, &(game->position.board));
printf(".\n");
fflush(stdout);
return bookMove;
}
time_t startTime, endTime;
startTime = time(NULL);
// Move move = getRandomMove(&game->position);
// Move move = simpleEvaluation(&game->position).move;
// Move move = minimax(&game->position, AI_DEPTH).move;
// Node node = alphaBeta(&game->position, depth, INT32_MIN, INT32_MAX, TRUE);
// Node node = iterativeDeepeningAlphaBeta(&game->position, depth, INT32_MIN, INT32_MAX, TRUE);
// Node node = idabThreaded(&game->position, depth, TRUE);
// Node node = idabThreadedBestFirst(&game->position, depth, TRUE);
// Node node = idabThreadedBestFirstHashed(&game->position, depth, TRUE);
#ifdef _WIN32
Node node = idabThreadedBestFirst(&game->position, depth, TRUE);
#else
Node node = iterativeDeepeningAlphaBeta(&game->position, depth, INT32_MIN, INT32_MAX, TRUE);
#endif
endTime = time(NULL);
printf("CHOSEN move: ");
printFullMove(node.move, &(game->position.board));
printf(
" in %d seconds [%+.2f, %+.2f]\n",
(int)(endTime - startTime),
(double)staticEvaluation(&game->position) / (double)100.0,
(double)node.score / (double)100.0);
fflush(stdout);
return node.move;
}
Move parseMove(char* move) {
int pos1 = str2index(&move[0]);
int pos2 = str2index(&move[2]);
return generateMove(pos1, pos2);
}
Move getPlayerMove() {
char input[100];
fgets(input, 100, stdin);
return parseMove(input);
}
Move suggestMove(char fen[], int depth) {
Game game;
getFenGame(&game, fen);
return getAIMove(&game, depth);
}
// ===== PLAY LOOP (TEXT) ====
void playTextWhite(int depth) {
printf("Playing as WHITE!\n");
fflush(stdout);
Game game;
getInitialGame(&game);
while(TRUE) {
printBoard(&(game.position.board));
if(hasGameEnded(&game.position)) break;
makeMove(&game, getPlayerMove());
printBoard(&(game.position.board));
if(hasGameEnded(&game.position)) break;
makeMove(&game, getAIMove(&game, depth));
}
printOutcome(&game.position);
}
void playTextBlack(int depth) {
printf("Playing as BLACK!\n");
fflush(stdout);
Game game;
getInitialGame(&game);
while(TRUE) {
printBoard(&(game.position.board));
if(hasGameEnded(&game.position)) break;
makeMove(&game, getAIMove(&game, depth));
printBoard(&(game.position.board));
if(hasGameEnded(&game.position)) break;
makeMove(&game, getPlayerMove());
}
printOutcome(&game.position);
}
void playTextAs(char color, int depth) {
if(color == WHITE) playTextWhite(depth);
if(color == BLACK) playTextBlack(depth);
}
void playTextRandomColor(int depth) {
char colors[] = {WHITE, BLACK};
char color = colors[rand() % 2];
playTextAs(color, depth);
}
// ===========================
/*
int main(int argc, char *argv[]) {
srand(time(NULL));
int opt;
int FEN_MODE = 0, MOVES_MODE = 1, mode = FEN_MODE;
int depth = DEFAULT_AI_DEPTH;
while ((opt = getopt(argc, argv, "fmd:v")) != -1) {
switch (opt) {
case 'f': mode = FEN_MODE; break;
case 'm': mode = MOVES_MODE; break;
case 'd': depth = atoi(optarg); break;
case 'v': printf(ENGINE_VERSION); exit(0);
default:
fprintf(stderr, "Usage: %s [-d depth] [-f fen] [-m move_list]\n", argv[0]);
exit(EXIT_FAILURE);
}
}
Game game;
if (argc > optind) {
if (mode == FEN_MODE) {
getFenGame(&game, argv[optind]);
} else if (mode == MOVES_MODE) {
getMovelistGame(&game, argv[optind]);
}
} else {
getInitialGame(&game);
}
Move move;
if ( mode == MOVES_MODE && countBookOccurrences(&game) > 0 ) {
move = getBookMove(&game);
} else {
Node node = iterativeDeepeningAlphaBeta(&(game.position), (char) depth, INT32_MIN, INT32_MAX, FALSE);
move = node.move;
}
printf("%c%c%c%c", getFile(getFrom(move)), getRank(getFrom(move)), getFile(getTo(move)), getRank(getTo(move)));
return EXIT_SUCCESS;
}
// */