Bitmap xperft(int depth) { Bitmap x, k, desde, hasta, r; desde = (Bitmap) board.ply_moves[board.ply - 1]; hasta = (Bitmap) board.ply_moves[board.ply]; if (depth > 1) { x = 0; // xfen();xl(); for (k = desde; k < hasta; k++) { // b0 = board; //DBG make_move(board.moves[k]); movegen(); r = xperft(depth - 1); x += r; unmake_move(); } return x; } else { return hasta - desde; } }
int main(int argc, char* argv[]) { if (argc != 4) { std::cerr << "Expect arguments: pn1/pn2 <max nodes> <move seq>\n" << "Eg: ./pns_analyze pn1 100000 \"e3 b6\"" << std::endl; return 0; } const auto pns_type = GetPNSType(argv[1]); const int max_nodes = atoi(argv[2]); const std::string position = GetPosition(argv[3]); Board board(Variant::SUICIDE, position); MoveGeneratorSuicide movegen(board); std::vector<std::string> egtb_filenames; assert(GlobFiles("egtb/*.egtb", &egtb_filenames)); EGTB egtb(egtb_filenames, board); egtb.Initialize(); EvalSuicide eval(&board, &movegen, &egtb); PNSParams pns_params; pns_params.max_nodes = max_nodes; pns_params.pns_type = pns_type; pns_params.quiet = false; pns_params.log_progress = 10; PNSearch pn_search(&board, &movegen, &eval, &egtb, nullptr); PNSResult pns_result; pn_search.Search(pns_params, &pns_result); std::cout << "tree_size: " << pns_result.pns_tree->tree_size << "\n" << "proof: " << pns_result.pns_tree->proof << "\n" << "disproof: " << pns_result.pns_tree->disproof << std::endl; return 0; }
std::string GetPosition(const char* arg) { Board board(Variant::SUICIDE); MoveGeneratorSuicide movegen(board); const auto move_str_vec = SplitString(arg, ' '); for (const auto& move_str : move_str_vec) { const Move move = SANToMove(move_str, board, &movegen); if (!move.is_valid()) { throw std::invalid_argument("Invalid move " + move_str); } board.MakeMove(move); } return board.ParseIntoFEN(); }
void perft(int depth) { Bitmap ms, ds; Bitmap rs; ms = get_ms(); board_reset(); movegen(); rs = xperft(depth); ds = get_ms() - ms; printf("Total:%lu ", (long unsigned int) rs); if( ds ) { printf( " (%lu positions/second)", (long unsigned int) (rs * 1000 / ds)); } printf( "\n"); }
int Board::alphabeta(int ply, int depth, int alpha, int beta) { // Negascout int i, j, val; triangularLength[ply] = ply; if (depth == 0) return board.eval(); moveBufLen[ply+1] = movegen(moveBufLen[ply]); for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++) { makeMove(moveBuffer[i]); { if (!isOtherKingAttacked()) { inodes++; if (!ply) displaySearchStats(3, ply, i); val = -alphabeta(ply+1, depth-1, -beta, -alpha); unmakeMove(moveBuffer[i]); if (val >= beta) { return beta; } if (val > alpha) { alpha = val; // both sides want to maximize from *their* perspective triangularArray[ply][ply] = moveBuffer[i]; // save this move for (j = ply + 1; j < triangularLength[ply + 1]; j++) { triangularArray[ply][j] = triangularArray[ply+1][j]; // and append the latest best PV from deeper plies } triangularLength[ply] = triangularLength[ply + 1]; if (!ply) { msStop = timer.getms(); displaySearchStats(2, depth, val); } } } else unmakeMove(moveBuffer[i]); } } return alpha; }
int Board::minimax(int ply, int depth) { // Negamax int i, j, val, best; best = -LARGE_NUMBER; triangularLength[ply] = ply; if (depth == 0) return board.eval(); moveBufLen[ply+1] = movegen(moveBufLen[ply]); for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++) { makeMove(moveBuffer[i]); { if (!isOtherKingAttacked()) { inodes++; if (!ply) displaySearchStats(3, ply, i); val = -minimax(ply+1, depth-1); // note the minus sign unmakeMove(moveBuffer[i]); if (val > best) // both sides want to maximize from *their* perspective { best = val; triangularArray[ply][ply] = moveBuffer[i]; // save this move for (j = ply + 1; j < triangularLength[ply + 1]; j++) { triangularArray[ply][j] = triangularArray[ply+1][j]; // and append the latest best PV from deeper plies } triangularLength[ply] = triangularLength[ply + 1]; if (!ply) { msStop = timer.getms(); displaySearchStats(2, depth, val); } } } else unmakeMove(moveBuffer[i]); } } return best; }
void do_move(char *ini_moves, int from, int sz) { char pv[6], str_move[6]; int i, to; for (i = 0; i < sz; i++) { pv[i] = ini_moves[from + i]; } pv[i] = 0; from = board.idx_moves; movegen(); to = board.idx_moves; for (i = from; i < to; i++) { if (!strcmp(move2str(board.moves[i], str_move), pv)) { make_move(board.moves[i]); return; } } }
Bitmap calc_perft(char *fen, int depth) { fen_board(fen); movegen(); return xperft(depth); }
BOOLTYPE toSan(Move &move, char *sanMove) { // =========================================================================== // toSan will convert a move into non-ambiguous SAN-notation, returned in char sanMove[]. // "move" must belong to the current "board". Returns true if successful. // The move is compared with other moves from the current board position. // Ambiguities can arise if two (or more) identical pieces can move to the same square. // In such cases, the piece's initial is followed by (in this priority): // - the from file, if it's unique, // - or else the from rank, if it's unique // - or else the from file and rank (this can happen after pawn promotions, // e.g. with 4 rooks on c3, c7, a5 and e5; they all can move to c5, and then move notation would be: R3c5 // 'e.p.' is added for an en-passant capture // '+'is added for check, '#' is added for mate. // =========================================================================== int i, j, k, ibuf, from, to, piece, capt, prom, ambigfile, ambigrank; int asciiShift; BOOLTYPE legal, check, mate, ambig; asciiShift = (int)'a'; piece = move.getPiec(); from = move.getFrom(); to = move.getTosq(); capt = move.getCapt(); prom = move.getProm(); ibuf = 0; ambig = false; ambigfile = 0; ambigrank = 0; legal = false; check = false; mate = false; sprintf(sanMove, ""); // Generate all pseudo-legal moves to be able to remove any ambiguities // and check legality. Take the next free location in moveBufLen: while (board.moveBufLen[ibuf+1]) ibuf++; board.moveBufLen[ibuf+1] = movegen(board.moveBufLen[ibuf]); // Loop over the moves to see what kind(s) of ambiguities exist, if any: for (i = board.moveBufLen[ibuf]; i < board.moveBufLen[ibuf+1]; i++) { makeMove(board.moveBuffer[i]); if (!isOtherKingAttacked()) { if (board.moveBuffer[i].moveInt == move.moveInt) { legal = true; // it is check: if (isOwnKingAttacked()) { check = true; // is it checkmate? k = 0; board.moveBufLen[ibuf+2] = movegen(board.moveBufLen[ibuf+1]); for (j = board.moveBufLen[ibuf+1]; j < board.moveBufLen[ibuf+2]; j++) { makeMove(board.moveBuffer[j]); if (!isOtherKingAttacked()) k++; unmakeMove(board.moveBuffer[j]); } if (!k) mate = true; } } // two same pieces can move to the same square: if ((board.moveBuffer[i].moveInt != move.moveInt) && (board.moveBuffer[i].getPiec() == piece) && (board.moveBuffer[i].getTosq() == to)) { ambig = true; if (FILES[from] == FILES[board.moveBuffer[i].getFrom()]) ambigfile++; if (RANKS[from] == RANKS[board.moveBuffer[i].getFrom()]) ambigrank++; } } unmakeMove(board.moveBuffer[i]); } // cleanup: board.moveBufLen[ibuf+1] = 0; board.moveBufLen[ibuf+2] = 0; // construct the SAN string: if (!legal) { strcpy(sanMove, "unknown"); return false; } else { if (move.isCastleOO()) { strcpy(sanMove, "O-O"); return true; } if (move.isCastleOOO()) { strcpy(sanMove, "O-O-O"); return true; } // start building the string if (!move.isPawnmove()) { sprintf(sanMove, "%s", PIECECHARS[piece]); if (ambig) { if (ambigfile) { if (ambigrank) sprintf(sanMove, "%s%c%d", sanMove, FILES[from] + asciiShift - 1,RANKS[from]); else sprintf(sanMove, "%s%d", sanMove, RANKS[from]); } else { sprintf(sanMove, "%s%c", sanMove, FILES[from] + asciiShift - 1); } } } else { if (move.isCapture()) { sprintf(sanMove, "%s%c", sanMove, FILES[from] + asciiShift - 1); } } if (move.isCapture()) sprintf(sanMove, "%sx", sanMove); sprintf(sanMove, "%s%c%d", sanMove, FILES[to] + asciiShift - 1, RANKS[to]); if (move.isEnpassant()) sprintf(sanMove, "%s e.p.", sanMove); if (move.isPromotion()) sprintf(sanMove, "%s=%s", sanMove, PIECECHARS[prom]); if (check) { if (mate) sprintf(sanMove, "%s#", sanMove); else sprintf(sanMove, "%s+", sanMove); } return true; } }
int Board::alphabetapvs(int ply, int depth, int alpha, int beta) { // PV search int i, j, movesfound, pvmovesfound, val; triangularLength[ply] = ply; if (depth == 0) { followpv = false; return qsearch(ply, alpha, beta); } // repetition check: if (repetitionCount() >= 3) return DRAWSCORE; movesfound = 0; pvmovesfound = 0; moveBufLen[ply+1] = movegen(moveBufLen[ply]); for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++) { selectmove(ply, i, depth, followpv); makeMove(moveBuffer[i]); { if (!isOtherKingAttacked()) { inodes++; movesfound++; if (!ply) displaySearchStats(3, ply, i); if (pvmovesfound) { val = -alphabetapvs(ply+1, depth-1, -alpha-1, -alpha); if ((val > alpha) && (val < beta)) { // in case of failure, proceed with normal alphabeta val = -alphabetapvs(ply+1, depth - 1, -beta, -alpha); } } // normal alphabeta else val = -alphabetapvs(ply+1, depth-1, -beta, -alpha); unmakeMove(moveBuffer[i]); if (val >= beta) { // update the history heuristic if (nextMove) blackHeuristics[moveBuffer[i].getFrom()][moveBuffer[i].getTosq()] += depth*depth; else whiteHeuristics[moveBuffer[i].getFrom()][moveBuffer[i].getTosq()] += depth*depth; return beta; } if (val > alpha) { alpha = val; // both sides want to maximize from *their* perspective pvmovesfound++; triangularArray[ply][ply] = moveBuffer[i]; // save this move for (j = ply + 1; j < triangularLength[ply+1]; j++) { triangularArray[ply][j] = triangularArray[ply+1][j]; // and append the latest best PV from deeper plies } triangularLength[ply] = triangularLength[ply+1]; if (!ply) { msStop = timer.getms(); displaySearchStats(2, depth, val); } } } else unmakeMove(moveBuffer[i]); } } // update the history heuristic if (pvmovesfound) { if (nextMove) blackHeuristics[triangularArray[ply][ply].getFrom()][triangularArray[ply][ply].getTosq()] += depth*depth; else whiteHeuristics[triangularArray[ply][ply].getFrom()][triangularArray[ply][ply].getTosq()] += depth*depth; } // 50-move rule: if (fiftyMove >= 100) return DRAWSCORE; // Checkmate/stalemate detection: if (!movesfound) { if (isOwnKingAttacked()) return (-CHECKMATESCORE+ply-1); else return (STALEMATESCORE); } return alpha; }
BOOLTYPE Board::isEndOfgame(int &legalmoves, Move &singlemove) { // Checks if the current position is end-of-game due to: // checkmate, stalemate, 50-move rule, or insufficient material int whiteknights, whitebishops, whiterooks, whitequeens, whitetotalmat; int blackknights, blackbishops, blackrooks, blackqueens, blacktotalmat; // are we checkmating the other side? int i; if (isOtherKingAttacked()) { if (nextMove) std::cout << "1-0 {Black mates}" << std::endl; else std::cout << "1-0 {White mates}" << std::endl; return true; } // how many legal moves do we have? legalmoves = 0; moveBufLen[0] = 0; moveBufLen[1] = movegen(moveBufLen[0]); for (i = moveBufLen[0]; i < moveBufLen[1]; i++) { makeMove(moveBuffer[i]); if (!isOtherKingAttacked()) { legalmoves++; singlemove = moveBuffer[i]; } unmakeMove(moveBuffer[i]); } // checkmate or stalemate? if (!legalmoves) { if (isOwnKingAttacked()) { if (nextMove) std::cout << "1-0 {White mates}" << std::endl; else std::cout << "1-0 {Black mates}" << std::endl; } else std::cout << "1/2-1/2 {stalemate}" << std::endl; return true; } // draw due to insufficient material: if (!whitePawns && !blackPawns) { whiteknights = bitCnt(whiteKnights); whitebishops = bitCnt(whiteBishops); whiterooks = bitCnt(whiteRooks); whitequeens = bitCnt(whiteQueens); whitetotalmat = 3 * whiteknights + 3 * whitebishops + 5 * whiterooks + 10 * whitequeens; blackknights = bitCnt(blackKnights); blackbishops = bitCnt(blackBishops); blackrooks = bitCnt(blackRooks); blackqueens = bitCnt(blackQueens); blacktotalmat = 3 * blackknights + 3 * blackbishops + 5 * blackrooks + 10 * blackqueens; // king versus king: if ((whitetotalmat == 0) && (blacktotalmat == 0)) { std::cout << "1/2-1/2 {material}" << std::endl; return true; } // king and knight versus king: if (((whitetotalmat == 3) && (whiteknights == 1) && (blacktotalmat == 0)) || ((blacktotalmat == 3) && (blackknights == 1) && (whitetotalmat == 0))) { std::cout << "1/2-1/2 {material}" << std::endl; return true; } // 2 kings with one or more bishops, all bishops on the same colour: if ((whitebishops + blackbishops) > 0) { if ((whiteknights == 0) && (whiterooks == 0) && (whitequeens == 0) && (blackknights == 0) && (blackrooks == 0) && (blackqueens == 0)) { if (!((whiteBishops | blackBishops) & WHITE_SQUARES) || !((whiteBishops | blackBishops) & BLACK_SQUARES)) { std::cout << "1/2-1/2 {material}" << std::endl; return true; } } } } // draw due to repetition: if (repetitionCount() >= 3) { std::cout << "1/2-1/2 {repetition}" << std::endl; return true; } // draw due to 50 move rule: if (fiftyMove >= 100) { std::cout << "1/2-1/2 {50-move rule}" << std::endl; return true; } return false; }
int Board::alphabetapvs(int ply, int depth, int alpha, int beta) { // PV search int i, j, movesfound, pvmovesfound, val; triangularLength[ply] = ply; if (depth <= 0) { followpv = false; return qsearch(ply, alpha, beta); } // repetition check: if (repetitionCount() >= 3) return DRAWSCORE; // now try a null move to get an early beta cut-off: if (!followpv && allownull) { if ((nextMove && (board.totalBlackPieces > NULLMOVE_LIMIT)) || (!nextMove && (board.totalWhitePieces > NULLMOVE_LIMIT))) { if (!isOwnKingAttacked()) { allownull = false; inodes++; if (--countdown <=0) readClockAndInput(); nextMove = !nextMove; hashkey ^= KEY.side; val = -alphabetapvs(ply, depth - NULLMOVE_REDUCTION, -beta, -beta+1); nextMove = !nextMove; hashkey ^= KEY.side; if (timedout) return 0; allownull = true; if (val >= beta) return val; } } } allownull = true; movesfound = 0; pvmovesfound = 0; moveBufLen[ply+1] = movegen(moveBufLen[ply]); for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++) { selectmove(ply, i, depth, followpv); makeMove(moveBuffer[i]); { if (!isOtherKingAttacked()) { inodes++; if (--countdown <=0) readClockAndInput(); movesfound++; if (!ply) displaySearchStats(3, ply, i); if (pvmovesfound) { val = -alphabetapvs(ply+1, depth-1, -alpha-1, -alpha); if ((val > alpha) && (val < beta)) { // in case of failure, proceed with normal alphabeta val = -alphabetapvs(ply+1, depth - 1, -beta, -alpha); } } // normal alphabeta else val = -alphabetapvs(ply+1, depth-1, -beta, -alpha); unmakeMove(moveBuffer[i]); if (timedout) return 0; if (val >= beta) { // update the history heuristic if (nextMove) blackHeuristics[moveBuffer[i].getFrom()][moveBuffer[i].getTosq()] += depth*depth; else whiteHeuristics[moveBuffer[i].getFrom()][moveBuffer[i].getTosq()] += depth*depth; return beta; } if (val > alpha) { alpha = val; // both sides want to maximize from *their* perspective pvmovesfound++; triangularArray[ply][ply] = moveBuffer[i]; // save this move for (j = ply + 1; j < triangularLength[ply+1]; j++) { triangularArray[ply][j] = triangularArray[ply+1][j]; // and append the latest best PV from deeper plies } triangularLength[ply] = triangularLength[ply+1]; if (!ply) displaySearchStats(2, depth, val); } } else unmakeMove(moveBuffer[i]); } } // update the history heuristic if (pvmovesfound) { if (nextMove) blackHeuristics[triangularArray[ply][ply].getFrom()][triangularArray[ply][ply].getTosq()] += depth*depth; else whiteHeuristics[triangularArray[ply][ply].getFrom()][triangularArray[ply][ply].getTosq()] += depth*depth; } // 50-move rule: if (fiftyMove >= 100) return DRAWSCORE; // Checkmate/stalemate detection: if (!movesfound) { if (isOwnKingAttacked()) return (-CHECKMATESCORE+ply-1); else return (STALEMATESCORE); } return alpha; }
void commands() { // ================================================================ // commands is used to read console input and execute the commands // It also serves as winboard driver. // The code is based on H.G. Muller's model WinBoard protocol driver: // http://www.open-aurec.com/wbforum/viewtopic.php?f=24&t=51739 // ================================================================= int i, j, number; int fenhalfmoveclock; int fenfullmovenumber; char fen[100]; char fencolor[1]; char fencastling[4]; char fenenpassant[2]; char sanMove[12]; char command[80]; char userinput[80]; U64 msStart,msStop, perftcount; Timer timer; Move move, dummy; // ================================================================= // infinite loop: // ================================================================= while (1) { fflush(stdout); // ================================================================= // think & move // ================================================================= if (XB_MODE) { if (XB_COMPUTER_SIDE == board.nextMove) { #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : start think" << std::endl; #endif move = board.think(); #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : exit think" << std::endl; std::cout << "#<KENNY : move " << SQUARENAME[move.getFrom()] << SQUARENAME[move.getTosq()] << std::endl; #endif if (move.moveInt) { printf("move "); printf("%s",SQUARENAME[move.getFrom()]); printf("%s",SQUARENAME[move.getTosq()]); if (move.isPromotion()) printf("%s",PIECECHARS[move.getProm()]); printf("\n"); makeMove(move); board.endOfGame++; board.endOfSearch = board.endOfGame; } } fflush(stdout); // ================================================================= // ponder // ================================================================= if (XB_COMPUTER_SIDE != XB_NONE && XB_COMPUTER_SIDE != XB_ANALYZE && XB_PONDER && board.endOfGame) { XB_NO_TIME_LIMIT = true; #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : start ponder" << std::endl; #endif move = board.think(); #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : exit ponder" << std::endl; #endif XB_NO_TIME_LIMIT = false; } // ================================================================= // analyze // ================================================================= if (XB_COMPUTER_SIDE == XB_ANALYZE) { XB_NO_TIME_LIMIT = true; #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : start analyze" << std::endl; #endif move = board.think(); #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : exit analyze" << std::endl; #endif XB_NO_TIME_LIMIT = false; } } noPonder: // ================================================================= // display the command prompt // ================================================================= if (!XB_MODE) { if (board.nextMove == WHITE_MOVE) std::cout << "wt> "; else std::cout << "bl> "; fflush(stdout); } // ================================================================= // read input, but only after attending a pending command received during // search/ponder/analyze: // ================================================================= if (!XB_DO_PENDING) { #ifdef KENNY_DEBUG_WINBOARD if (XB_MODE) std::cout << "#-KENNY : COMPUTER_SIDE=" << (int)XB_COMPUTER_SIDE << " PONDER=" << XB_PONDER << " nextMove=" << (int)board.nextMove << std::endl; #endif for (CMD_BUFF_COUNT = 0; (CMD_BUFF[CMD_BUFF_COUNT] = getchar()) != '\n'; CMD_BUFF_COUNT++); CMD_BUFF[CMD_BUFF_COUNT+1] = '\0'; #ifdef KENNY_DEBUG_WINBOARD if (XB_MODE) std::cout << "#>KENNY : " << CMD_BUFF << std::endl; #endif } #ifdef KENNY_DEBUG_WINBOARD else { if (XB_MODE) std::cout << "#>KENNY : " << CMD_BUFF << " (from peek)" << std::endl; } #endif XB_DO_PENDING = false; // ================================================================= // ignore empty lines // ================================================================= if (!CMD_BUFF_COUNT) continue; // ================================================================= // extract the first word // ================================================================= sscanf(CMD_BUFF, "%s", command); // ================================================================= // help, h or ?: show this help - list of CONSOLE-ONLY COMMANDS // ================================================================= if ((!XB_MODE) && ((!strcmp(command, "help")) || (!strcmp(command, "h")) || (!strcmp(command, "?")))) { std::cout << std::endl << "help:" << std::endl; std::cout << "black : BLACK to move" << std::endl; std::cout << "cc : play computer-to-computer " << std::endl; std::cout << "d : display board " << std::endl; std::cout << "eval : show static evaluation of this position" << std::endl; std::cout << "exit : exit program " << std::endl; std::cout << "game : show game moves " << std::endl; std::cout << "go : computer next move " << std::endl; std::cout << "help, h, or ? : show this help " << std::endl; std::cout << "info : display variables (for testing purposes)" << std::endl; std::cout << "ini : read the initialization file" << std::endl; std::cout << "memory n : max memory to use (in MB)" << std::endl; std::cout << "move e2e4, or h7h8q : enter a move (use this format)" << std::endl; std::cout << "moves : show all legal moves" << std::endl; std::cout << "new : start new game" << std::endl; std::cout << "perft n : calculate raw number of nodes from here, depth n " << std::endl; #ifdef KENNY_VERBOSE_SEE std::cout << "qsearch : shows sorted capture movelist" << std::endl; #endif std::cout << "quit : exit program " << std::endl; std::cout << "r : rotate board " << std::endl; std::cout << "readfen filename n : reads #-th FEN position from filename" << std::endl; std::cout << "sd n : set the search depth to n" << std::endl; std::cout << "setup : setup board... " << std::endl; std::cout << "test filename : starts search on all FEN position in 'filename'" << std::endl; std::cout << " using current time & search depth parameters" << std::endl; std::cout << " output is written in test.log" << std::endl; std::cout << "time s : time per move in seconds" << std::endl; std::cout << "undo : take back last move" << std::endl; std::cout << "white : WHITE to move" << std::endl; std::cout << std::endl; continue; } // ================================================================= // accepted: in reply to the "feature" command // ================================================================= if (XB_MODE && !strcmp(command, "accepted")) continue; // ================================================================= // analyze: enter analyze mode // ================================================================= if (XB_MODE && !strcmp(command, "analyze")) { XB_COMPUTER_SIDE = XB_ANALYZE; continue; } // ================================================================= // black: BLACK to move // ================================================================= if (!XB_MODE && !strcmp(command, "black") && board.nextMove == WHITE_MOVE) { board.hashkey ^= KEY.side; board.endOfSearch = 0; board.endOfGame = 0; board.nextMove = BLACK_MOVE; continue; } // ================================================================= // bk: show book moves from this position, if any // ================================================================= if (XB_MODE && !strcmp(command, "bk")) continue; // ================================================================= // cc: play computer-to-computer // ================================================================= if (!XB_MODE && !strcmp(command, "cc")) { while (!_kbhit() && !board.isEndOfgame(i, dummy)) { move = board.think(); if (move.moveInt) { makeMove(move); board.endOfGame++; board.endOfSearch = board.endOfGame; board.display(); } } continue; } // ================================================================= // computer: the opponent is also a computer chess engine // ================================================================= if (XB_MODE && !strcmp(command, "computer")) continue; // ================================================================= // cores n: informs the engine on how many CPU cores it is allowed to use maximally // ================================================================= if (XB_MODE && !strcmp(command, "cores")) continue; // ================================================================= // d: display board // ================================================================= if (!XB_MODE && !strcmp(command, "d")) { board.display(); continue; } // ================================================================= // easy: turn off pondering // ================================================================= if (XB_MODE && !strcmp(command, "easy")) { XB_PONDER = false; continue; } // ================================================================= // egtpath type path: informs the engine in which directory it can find end-game tables // ================================================================= if (XB_MODE && !strcmp(command, "egtpath")) continue; // ================================================================= // eval: show static evaluation of this position // ================================================================= if (!XB_MODE && !strcmp(command, "eval")) { number = board.eval(); std::cout << "eval score = " << number << std::endl; #ifdef KENNY_DEBUG_EVAL board.mirror(); board.display(); i = board.eval(); std::cout << "eval score = " << i << std::endl; board.mirror(); if (number != i) std::cout << "evaluation is not symmetrical! " << number << std::endl; else std::cout << "evaluation is symmetrical" << std::endl; #endif continue; } // ================================================================= // exit: leave analyze mode / exit program (if not in WB) // ================================================================= if (!strcmp(command, "exit")) { if (XB_MODE) { XB_COMPUTER_SIDE = XB_NONE; continue; } else break; } // ================================================================= // force: Set the engine to play neither color // ================================================================= if (XB_MODE && !strcmp(command, "force")) { XB_COMPUTER_SIDE = XB_NONE; continue; } // ================================================================= // game: show game moves // ================================================================= if (!XB_MODE && !strcmp(command, "game")) { if (board.endOfGame) { // make a temporary copy of board.gameLine[]; number = board.endOfGame; GameLineRecord *tmp = new GameLineRecord[number]; memcpy(tmp, board.gameLine, number * sizeof(GameLineRecord)); // unmake all moves: for (i = number-1 ; i >= 0 ; i--) { unmakeMove(tmp[i].move); board.endOfSearch = --board.endOfGame; } // redo all moves: j = board.nextMove; for (i = 0 ; i < number; i++) { // move numbering: if (!((i+j+2)%2)) std::cout << (i+2*j+2)/2 << ". "; else if (!i) std::cout << "1. ... "; // construct the move string toSan(tmp[i].move, sanMove); std::cout << sanMove; // output CRLF, or space: if (!((i+j+1)%2)) std::cout << std::endl; else std::cout << " "; // make the move: makeMove(tmp[i].move); board.endOfSearch = ++board.endOfGame; } std::cout << std::endl; // delete the temporary copy: delete[] tmp; } else { std::cout << "there are no game moves" << std::endl; } continue; } // ================================================================= // go: leave force mode and set the engine to play the color that is on move // ================================================================= if (!strcmp(command, "go")) { if (XB_MODE) { XB_COMPUTER_SIDE = board.nextMove; continue; } else { if (!board.isEndOfgame(i, dummy)) { move = board.think(); if (move.moveInt) { makeMove(move); board.endOfGame++; board.endOfSearch = board.endOfGame; } board.display(); board.isEndOfgame(i, dummy); CMD_BUFF_COUNT = '\0'; } else { board.display(); CMD_BUFF_COUNT = '\0'; } } continue; } // ================================================================= // hard: turn on pondering // ================================================================= if (XB_MODE && !strcmp(command, "hard")) { XB_PONDER = true; continue; } // ================================================================= // hint: respond with "Hint: xxx", where xxx is a suggested move // ================================================================= if (XB_MODE && !strcmp(command, "hint")) { continue; } // ================================================================= // ics hostname: the engine is playing on an Internet Chess Server (ICS) with the given hostname // ================================================================= if (XB_MODE && !strcmp(command, "ics")) { continue; } // ================================================================= // info: display variables (for testing purposes) // ================================================================= if (!XB_MODE && !strcmp(command, "info")) { info(); continue; } // ================================================================= // ini: read the initialization file // ================================================================= if (!XB_MODE && !strcmp(command, "ini")) { readIniFile(); continue; } // ================================================================= // level mps base inc: set time controls // ================================================================= if (XB_MODE && !strcmp(command, "level")) { sscanf(CMD_BUFF, "level %d %d %d", &XB_MPS, &XB_MIN, &XB_INC) == 3 || sscanf(CMD_BUFF, "level %d %d:%d %d", &XB_MPS, &XB_MIN, &XB_SEC, &XB_INC); XB_INC *= 1000; continue; } // ================================================================= // memory n: informs the engine on how much memory it is allowed to use maximally, in MB // ================================================================= if (XB_MODE && !strcmp(command, "memory")) continue; // ================================================================= // moves: show all legal moves // ================================================================= if (!XB_MODE && !strcmp(command, "moves")) { board.moveBufLen[0] = 0; board.moveBufLen[1] = movegen(board.moveBufLen[0]); std::cout << std::endl << "moves from this position:" << std::endl; number = 0; for (i = board.moveBufLen[0]; i < board.moveBufLen[1]; i++) { makeMove(board.moveBuffer[i]); if (isOtherKingAttacked()) { unmakeMove(board.moveBuffer[i]); } else { unmakeMove(board.moveBuffer[i]); toSan(board.moveBuffer[i], sanMove); std::cout << ++number << ". " << sanMove << std::endl; } } continue; } // ================================================================= // move: enter a move (use this format: move e2e4, or h7h8q) // ================================================================= if (!XB_MODE && !strcmp(command, "")) { sscanf(CMD_BUFF,"%s",userinput); // generate the pseudo-legal move list board.moveBufLen[0] = 0; board.moveBufLen[1] = movegen(board.moveBufLen[0]); if (isValidTextMove(userinput, move)) // check to see if the user move is also found in the pseudo-legal move list { makeMove(move); if (isOtherKingAttacked()) // post-move check to see if we are leaving our king in check { unmakeMove(move); std::cout << " invalid move, leaving king in check: " << userinput << std::endl; } else { board.endOfGame++; board.endOfSearch = board.endOfGame; board.display(); } } else { std::cout << " move is invalid or not recognized: " << userinput << std::endl; } continue; } // ================================================================= // name <something>: informs the engine of its opponent's name // ================================================================= if (XB_MODE && !strcmp(command, "name")) continue; // ================================================================= // new: reset the board to the standard chess starting position // ================================================================= if (!strcmp(command, "new")) { board.init(); if (XB_MODE) { XB_COMPUTER_SIDE = BLACK_MOVE; board.searchDepth = MAX_PLY; } continue; } // ================================================================= // nopost: turn off thinking/pondering output // ================================================================= if (XB_MODE && !strcmp(command, "nopost")) { XB_POST = false; continue; } // ================================================================= // otim n: set a clock that belongs to the opponent, in centiseconds // ================================================================= if (XB_MODE && !strcmp(command, "otim")) { // do not start pondering after receiving time commands, as a move will follow immediately sscanf(CMD_BUFF, "otim %d", &XB_OTIM); XB_OTIM *= 10; // convert to miliseconds; goto noPonder; } // ================================================================= // option name[=value]: setting of an engine-define option // ================================================================= if (XB_MODE && !strcmp(command, "option")) continue; // ================================================================= // perft: calculate raw number of nodes from here, depth n // ================================================================= if (!XB_MODE && !strcmp(command, "perft")) { sscanf(CMD_BUFF,"perft %d", &number); std::cout << " starting perft " << number << "..." << std::endl; timer.init(); board.moveBufLen[0] = 0; #ifdef KENNY_DEBUG_PERFT ICAPT = 0; IEP = 0; IPROM = 0; ICASTLOO = 0; ICASTLOOO = 0; ICHECK = 0; #endif msStart = timer.getms(); perftcount = perft(0, number); msStop = timer.getms(); std::cout << "nodes = " << perftcount << ", " << msStop - msStart << " ms, "; if ((msStop - msStart) > 0) std::cout << (perftcount/(msStop - msStart)) << " knods/s"; std::cout << std::endl; CMD_BUFF_COUNT = '\0'; #ifdef KENNY_DEBUG_PERFT std::cout << "captures = " << ICAPT << std::endl; std::cout << "en-passant = " << IEP << std::endl; std::cout << "castlings = " << ICASTLOO + ICASTLOOO << std::endl; std::cout << "promotions = " << IPROM << std::endl; std::cout << "checks = " << ICHECK << std::endl; #endif continue; } // ================================================================= // ping n: reply by sending the string pong n // ================================================================= if (XB_MODE && !strcmp(command, "ping")) { sscanf(CMD_BUFF,"ping %d", &number); std::cout << "pong " << number << std::endl; continue; } // ================================================================= // post: turn on thinking/pondering output // ================================================================= if (XB_MODE && !strcmp(command, "post")) { XB_POST = true; continue; } // ================================================================= // protover n: protocol version // ================================================================= if (XB_MODE && !strcmp(command, "protover")) { std::cout << "feature ping=1" << std::endl; std::cout << "feature setboard=1" << std::endl; std::cout << "feature colors=0" << std::endl; std::cout << "feature usermove=1" << std::endl; std::cout << "feature memory=1" << std::endl; std::cout << "feature debug=1" << std::endl; std::cout << "feature done=1" << std::endl; continue; } #ifdef KENNY_VERBOSE_SEE // ================================================================= // qsearch: shows sorted capture movelist // ================================================================= if (!XB_MODE && !strcmp(command, "qsearch")) { board.moveBufLen[0] = 0; board.moveBufLen[1] = captgen(board.moveBufLen[0]); std::cout << std::endl << "sorted capturing moves from this position:" << std::endl; std::cout << std::endl << " score:" << std::endl; number = 0; for (i = board.moveBufLen[0]; i < board.moveBufLen[1]; i++) { makeMove(board.moveBuffer[i]); if (isOtherKingAttacked()) { unmakeMove(board.moveBuffer[i]); } else { unmakeMove(board.moveBuffer[i]); std::cout << ++number << ". "; displayMove(board.moveBuffer[i]); std::cout << " " << board.moveBuffer[i + OFFSET].moveInt << std::endl; } } continue; } #endif // ================================================================= // quit: exit program // ================================================================= if (!strcmp(command, "quit")) break; // ================================================================= // r: rotate board // ================================================================= if (!XB_MODE && !strcmp(command, "r")) { board.viewRotated = !board.viewRotated; continue; } // ================================================================= // random: ignored // ================================================================= if (XB_MODE && !strcmp(command, "random")) continue; // ================================================================= // rating: ICS opponent's rating // ================================================================= if (XB_MODE && !strcmp(command, "rating")) continue; // ================================================================= // readfen filename n: reads #-th FEN position from filename // ================================================================= if (!XB_MODE && !strcmp(command, "readfen")) { sscanf(CMD_BUFF,"readfen %s %d", userinput, &number); board.init(); readFen(userinput, number); board.display(); continue; } // ================================================================= // rejected: feature is rejected // ================================================================= if (XB_MODE && !strcmp(command, "rejected")) continue; // ================================================================= // remove: undo the last two moves (one for each player) and continue playing the same color. // ================================================================= if (XB_MODE && !strcmp(command, "remove")) { if (board.endOfGame) { unmakeMove(board.gameLine[--board.endOfGame].move); board.endOfSearch = board.endOfGame; } if (board.endOfGame) { unmakeMove(board.gameLine[--board.endOfGame].move); board.endOfSearch = board.endOfGame; } continue; } // ================================================================= // result string {comment}: end the each game, e.g.: result 1-0 {White mates} // ================================================================= if (XB_MODE && !strcmp(command, "result")) { XB_COMPUTER_SIDE = XB_NONE; continue; } // ================================================================= // sd n: set the search depth to n // ================================================================= if (!strcmp(command, "sd")) { sscanf(CMD_BUFF,"sd %d", &board.searchDepth); if (board.searchDepth < 1) board.searchDepth = 1; if (board.searchDepth > MAX_PLY) board.searchDepth = MAX_PLY; std::cout << "KENNY> search depth " << board.searchDepth << std::endl; continue; } // ================================================================= // setboard fen: set up the board/position // ================================================================= if (XB_MODE && !strcmp(command, "setboard")) { XB_COMPUTER_SIDE = XB_NONE; sscanf(CMD_BUFF, "setboard %s %s %s %s %d %d", fen, fencolor, fencastling, fenenpassant, &fenhalfmoveclock, &fenfullmovenumber); setupFen(fen, fencolor, fencastling, fenenpassant, fenhalfmoveclock, fenfullmovenumber); continue; } // ================================================================= // setup: setup board... // ================================================================= if (!XB_MODE && !strcmp(command, "setup")) { setup(); continue; } // ================================================================= // stopfrac (0-100%): undocumented command to interactively change this // parameter (e.g. for running testsuites), default value is 60 // Don't start a new iteration if STOPFRAC fraction of the max search time // has passed // ================================================================= if (!XB_MODE && !strcmp(command, "stopfrac")) { number = (int)(STOPFRAC * 100); sscanf(CMD_BUFF, "stopfrac %d", &number); if (number < 1) number = 1; if (number > 100) number = 100; STOPFRAC = (float)(number/100.0); std::cout << "KENNY> stopfrac " << 100*STOPFRAC << std::endl; continue; } // ================================================================= // st time: set time controls // ================================================================= if (XB_MODE && !strcmp(command, "st")) { sscanf(CMD_BUFF, "st %d", &board.maxTime); board.maxTime *= board.maxTime; // convert to ms continue; } // ================================================================= // test filename: starts search on all FEN position in 'filename // ================================================================= if (!XB_MODE && !strcmp(command, "test")) { sscanf(CMD_BUFF,"test %s", userinput); board.init(); test(userinput); continue; } // ================================================================= // time: set a clock that belongs to the engine // ================================================================= if (!strcmp(command, "time")) { number = (int)board.maxTime / 1000; sscanf(CMD_BUFF,"time %d", &number); if (number < 1) number = 1; if (!XB_MODE) std::cout << "KENNY> search time " << number << " seconds" << std::endl; if (XB_MODE) { XB_CTIM = number * 10; board.maxTime = number * 10; // conversion to ms } else { board.maxTime = number * 1000; // conversion to ms } goto noPonder; } // ================================================================= // undo: take back last move // ================================================================= if (!strcmp(command, "undo")) { if (board.endOfGame) { unmakeMove(board.gameLine[--board.endOfGame].move); board.endOfSearch = board.endOfGame; if (!XB_MODE) board.display(); } else if (!XB_MODE) std::cout << "already at start of game" << std::endl; continue; } // ================================================================= // usermove move: do a move // ================================================================= if (XB_MODE && !strcmp(command, "usermove")) { sscanf(CMD_BUFF,"usermove %s",userinput); // generate the pseudo-legal move list board.moveBufLen[0] = 0; board.moveBufLen[1] = movegen(board.moveBufLen[0]); if (isValidTextMove(userinput, move)) // check to see if the user move is also found in the pseudo-legal move list { makeMove(move); if (isOtherKingAttacked()) // post-move check to see if we are leaving our king in check { #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : usermove illegal" << std::endl; #endif unmakeMove(move); } else { #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : usermove " << userinput << " made" << std::endl; #endif board.endOfGame++; board.endOfSearch = board.endOfGame; } } else { #ifdef KENNY_DEBUG_WINBOARD std::cout << "#-KENNY : usermove illegal" << std::endl; #endif } continue; } // ================================================================= // variant: the game is not standard chess // ================================================================= if(XB_MODE && !strcmp(command, "variant")) continue; // ================================================================= // white: WHITE to move // ================================================================= if (!XB_MODE && !strcmp(command, "white") && board.nextMove == BLACK_MOVE) { board.hashkey ^= KEY.side; board.endOfSearch = 0; board.endOfGame = 0; board.nextMove = WHITE_MOVE; continue; } // ================================================================= // xboard: put the engine into "xboard mode", stop all unsolicited output // ================================================================= if (!XB_MODE && !strcmp(command, "xboard")) { #ifdef KENNY_DEBUG_WINBOARD if (XB_MODE) std::cout << "#>KENNY : xboard" << std::endl; #endif std::cout << std::endl; XB_COMPUTER_SIDE = XB_NONE; XB_MODE = true; XB_POST = false; board.init(); continue; } // ================================================================= // unknown command: // ================================================================= printf("Error: unknown command: %s\n", command); #ifdef KENNY_DEBUG_WINBOARD if (XB_MODE) std::cout << "#<KENNY : Error: unknown command: " << command << std::endl; #endif } }