#include #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"; std::ofstream uciLog("cache/uci_log.txt", std::ios::app); void LogUci(const std::string &message) { if (!uciLog.is_open()) { return; } auto now = std::chrono::system_clock::now(); auto time = std::chrono::system_clock::to_time_t(now); uciLog << "[" << std::put_time(std::localtime(&time), "%H:%M:%S") << "] " << message << "\n"; uciLog.flush(); } using namespace std; Move UciToMove(const 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); } 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"); cout << "Unexpected promotion type"; exit(1); } } return {.From = from, .To = to, .promotion = 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 char *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[type]); } } } static void printPromotionLetter(PieceType p) { if (p == NONEPIECE) { return; } switch (p) { case QUEEN: cout << "q"; break; case ROOK: cout << "r"; break; case KNIGHT: cout << "n"; break; case BISHOP: 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) { print(" 00"); continue; }; std::print(" {0}{1}", static_cast(piece.type), piece.color ? 'W' : 'B'); } std::print(" | {0}\n", rank + 1); } } void Uci() { unordered_map transpositions = {}; Game game = initBoard(starting_fen, &transpositions); string cmd; while (getline(cin, cmd)) { LogUci("GUI -> ENGINE: " + cmd); if (cmd == "quit") { return; } if (cmd == "board") { printBoard(&game); } if (cmd == "bits") { DebugBitboards(&game); } if (cmd == "uci") { cout << "id name " << engine_version() << "\n"; cout << "uciok\n"; cout.flush(); } if (cmd == "ucinewgame") { game = initBoard("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", &transpositions); } if (cmd == "isready") { cout << "readyok\n"; cout.flush(); } if (cmd.starts_with("go")) { stringstream ss(cmd); 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 (depth != -1 && depth <= 0) { cout << "Fatal: Depth must be positive integer"; exit(1); } move_options options = { .wtime = wtime, .btime = btime, .wncr = wncr, .bncr = bncr, }; Move best = GetBestMove(&game, depth, options); cout << "bestmove "; cout << static_cast(best.From.file + 'a') << 1 + best.From.rank << static_cast(best.To.file + 'a') << 1 + best.To.rank; printPromotionLetter(best.promotion); cout << "\n"; cout.flush(); } if (cmd.starts_with("position")) { stringstream ss(cmd); string token; ss >> token; // position ss >> token; if (token == "startpos") { game = initBoard(starting_fen, &transpositions); } else if (token == "fen") { string fen; 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, &transpositions); } // check for moves while (ss >> token) { if (token == "moves") { break; } } while (ss >> token) { Move move = UciToMove(token); MakeMove(move, &game); } } } }