#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" 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; from.file = static_cast(uci[0] - 'a'); from.rank = static_cast('8' - uci[1]); to.file = static_cast(uci[2] - 'a'); to.rank = static_cast('8' - uci[3]); return {from, to}; } Game initBoard( 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; Piece EmptyPiece = { false, NONEPIECE, }; for (int i = 0; i < 64; i++) { b.pieces[i] = EmptyPiece; }; 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::printf("%d | ", 8 - rank); for (int file = 0; file < 8; file++) { int sq = rank * 8 + file; std::cout << (((bb >> sq) & 1ULL) ? "1 " : ". "); } std::printf(" | %d\n", 8 - rank); } std::cout << " a b c d e f g h\n"; 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 (int color = 0; color < 2; ++color) { std::cout << (color ? "White\n" : "Black\n"); for (int type = PAWN; type <= KING; ++type) { PrintBitboard(g->PieceBitboards[color][type], pieceNames[type]); } } } void Uci() { unordered_map transpositions = {}; Game game = initBoard("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", &transpositions); string line; while (getline(cin, line)) { string cmd = line; LogUci("GUI -> ENGINE: " + line); transform(cmd.begin(), cmd.end(), cmd.begin(), ::tolower); if (cmd == "quit") { break; } if (cmd == "board") { for (int rank = 7; rank >= 0; --rank) { std::printf("%d |", 8 - rank); for (int file = 0; file < 8; file++) { Piece piece = game.pieces[rank * 8 + file]; std::printf(" %d%d", piece.type, piece.color); } std::printf(" | %d\n", 8 - rank); } } if (cmd == "bits") { DebugBitboards(&game); } if (cmd == "uci") { cout << "id name " << engine_version() << "\n"; cout << "uciok\n"; cout.flush(); } else if (cmd == "ucinewgame") { game = initBoard("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", &transpositions); } else if (cmd == "isready") { cout << "readyok\n"; cout.flush(); } else if (cmd.starts_with("go")) { stringstream ss(line); string token; int depth = -1; ss >> token; // go while (ss >> token) { if (token == "depth") { ss >> depth; break; } } if (depth != -1 && depth <= 0) { cout << "Fatal: Depth must be positive integer"; exit(1); } Move best = GetBestMove(&game, depth); cout << "bestmove "; cout << static_cast(best.From.file + 'a') << 8 - best.From.rank << static_cast(best.To.file + 'a') << 8 - best.To.rank; if (best.promotion != NONEPIECE) { switch (best.promotion) { 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"); } } cout << "\n"; cout.flush(); } else if (cmd.starts_with("position")) { stringstream ss(line); string token; ss >> token; // position ss >> token; if (token == "startpos") { game = initBoard( "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1", &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) 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); } } else if (cmd.starts_with("perft")) { std::istringstream iss(cmd); std::string word; int depth = -1; if (!(iss >> word >> depth) || word != "perft" || depth < 0) { cout << "info string usage: perft \n"; cout.flush(); continue; } auto start = std::chrono::steady_clock::now(); uint64_t count = MoveGenTest(depth, &game); auto end = std::chrono::steady_clock::now(); auto ms = std::chrono::duration_cast(end - start) .count(); std::cout << ms << " ms\n"; cout << count << "\n"; cout.flush(); } } }