#include #include #include #include #include #include #include #include #include #include #include #include #include "board/board.hpp" #include "board/fen.hpp" #include "bot.hpp" #include "misc.hpp" #include "moves.hpp" #include "uci.hpp" #include "zobrist.hpp" static const std::string starting_fen = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1"; uint16_t UciToMove(const std::string &uci) { Position from; Position to; PieceType promotion = NONEPIECE; if (uci.length() != 4 && uci.length() != 5) { assert(false && "expected valid uci string"); std::cout << "expected valid uci string\n"; exit(1); } // TODO: validate before using from.file = static_cast(uci[0] - 'a'); from.rank = static_cast(uci[1] - '1'); to.file = static_cast(uci[2] - 'a'); to.rank = static_cast(uci[3] - '1'); if (uci.length() == 5) { switch (toupper(static_cast(uci[4]))) { case 'Q': promotion = QUEEN; break; case 'N': promotion = KNIGHT; break; case 'R': promotion = ROOK; break; case 'B': promotion = BISHOP; break; default: assert(false && "Unexpected promotion type"); std::cout << "Unexpected promotion type"; exit(1); } } return CreateMove(static_cast(PositionToIndex(from)), static_cast(PositionToIndex(to)), promotion); } Game initBoard( const std::string &startingFEN, std::unordered_map *transPositions) { Game b{}; b.enPassant = IndexToPosition(0); // default value b.canEnpassant = false; b.state = TURN; b.turn = true; b.halfMoveClock = 0; b.MoveClock = 0; b.Transpositions = transPositions; setBoardFen(startingFEN, &b); uint64_t key = GenerateZobristKey(&b); b.ThreeFoldMap[key] = 1; return b; }; static void PrintBitboard(uint64_t bb, const std::string &name) { std::cout << name << "\n"; for (int rank = 7; rank >= 0; --rank) { std::print("{0} | ", rank + 1); for (int file = 0; file < 8; file++) { int sq = (rank * 8) + file; std::cout << (((bb >> sq) & 1) > 0 ? "1 " : ". "); } std::print("| {0}\n", rank + 1); } std::cout << "0x" << std::hex << bb << std::dec << "\n\n"; } static void DebugBitboards(const Game *g) { static constexpr const std::array pieceNames = { "None", "Pawn", "Knight", "Bishop", "Rook", "Queen", "King", }; std::cout << "========================\n"; std::cout << "Occupancy Bitboards\n"; std::cout << "========================\n"; PrintBitboard(g->WhitePieceBitboard, "White Occupancy"); PrintBitboard(g->BlackPieceBitboard, "Black Occupancy"); PrintBitboard(g->PieceBitboard, "All Pieces"); std::cout << "========================\n"; std::cout << "Piece Bitboards\n"; std::cout << "========================\n"; for (bool color : {false, true}) { std::cout << (color ? "White\n" : "Black\n"); for (int type = PAWN; type <= KING; ++type) { PrintBitboard(g->PieceBitboards[color][type], pieceNames[static_cast(type)]); } } } static void printPromotionLetter(PieceType p) { if (p == NONEPIECE) { return; } switch (p) { case QUEEN: std::cout << "q"; break; case ROOK: std::cout << "r"; break; case KNIGHT: std::cout << "n"; break; case BISHOP: std::cout << "b"; break; default: assert(false && "Unexpected promotion type"); } } static void printBoard(Game *g) { for (int rank = 7; rank >= 0; --rank) { std::print("{0} |", rank + 1); for (int file = 0; file < 8; file++) { Piece piece = g->pieces[(rank * 8) + file]; if (piece.type == NONEPIECE) { std::print(" 00"); continue; }; std::print(" {0}{1}", static_cast(piece.type), piece.color ? 'W' : 'B'); } std::print(" | {0}\n", rank + 1); } } static void GoCommand(Game *game, std::string &cmd) { std::stringstream ss(cmd); std::string token; int depth = -1; int wtime = -1; int btime = -1; int wncr = 0; int bncr = 0; ss >> token; // go while (ss >> token) { if (token == "depth") { ss >> depth; } if (token == "wtime") { ss >> wtime; } if (token == "btime") { ss >> btime; } if (token == "incr") { ss >> wncr; bncr = wncr; } if (token == "winc") { ss >> wncr; } if (token == "binc") { ss >> bncr; } } if (depth != -1 && depth <= 0) { std::cout << "Fatal: Depth must be positive integer"; exit(1); } move_options options = { .wtime = wtime, .btime = btime, .wncr = wncr, .bncr = bncr, }; uint16_t best = GetBestMove(game, depth, options); Piece piece = game->pieces[getFromValueFromMove(best)]; Position from = IndexToPosition(getFromValueFromMove(best)); Position to = IndexToPosition(getToValueFromMove(best)); PieceType promotion = getPromotionTypeFromMove(best); std::cout << "bestmove "; std::cout << static_cast(from.file + 'a') << 1 + from.rank << static_cast(to.file + 'a') << 1 + to.rank; if ((to.rank == 0 || to.rank == 7) && piece.type == PAWN) { printPromotionLetter(promotion); } std::cout << "\n"; std::cout.flush(); } static void positionCommnad(Game *game, std::string &cmd) { std::stringstream ss(cmd); std::string token; ss >> token; // position ss >> token; if (token == "startpos") { *game = initBoard(starting_fen, game->Transpositions); } else if (token == "fen") { std::string fen; std::string part; // FEN has spaces, need 6 parts for (int i = 0; i < 6; i++) { ss >> part; if (i != 0) { fen += ' '; } fen += part; } *game = initBoard(fen, game->Transpositions); } // check for moves while (ss >> token) { if (token == "moves") { break; } } while (ss >> token) { uint16_t move = UciToMove(token); MakeMove(move, game); } } void Uci() { std::unordered_map transpositions = {}; Game game = initBoard(starting_fen, &transpositions); std::string cmd; while (getline(std::cin, cmd)) { if (cmd == "quit") { return; } if (cmd == "board") { printBoard(&game); } if (cmd == "bits") { DebugBitboards(&game); } if (cmd == "uci") { std::cout << "id name " << engine_info(); std::cout << "uciok\n"; std::cout.flush(); } if (cmd == "ucinewgame") { game = initBoard("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", &transpositions); } if (cmd == "isready") { std::cout << "readyok\n"; std::cout.flush(); } if (cmd.starts_with("go")) { GoCommand(&game, cmd); } if (cmd.starts_with("position")) { positionCommnad(&game, cmd); } } }