int Board::qsearch(int ply, int alpha, int beta) { // quiescence search int i, j, val; if (timedout) return 0; triangularLength[ply] = ply; if (isOwnKingAttacked()) return alphabetapvs(ply, 1, alpha, beta); if(EVAL_FUNC == 0){ val = board.eval(); }else { val = board.evalJL(PARAM_EVAL_MATERIAL, PARAM_EVAL_ESPACIAL, PARAM_EVAL_DINAMICA, PARAM_EVAL_POS_TABLERO, ply); } if (val >= beta) return val; if (val > alpha) alpha = val; // generate captures & promotions: // captgen returns a sorted move list moveBufLen[ply+1] = captgen(moveBufLen[ply]); for (i = moveBufLen[ply]; i < moveBufLen[ply+1]; i++) { makeMove(moveBuffer[i]); { if (!isOtherKingAttacked()) { inodes++; if (--countdown <=0) readClockAndInput(); val = -qsearch(ply+1, -beta, -alpha); unmakeMove(moveBuffer[i]); if (val >= beta) return val; if (val > alpha) { alpha = val; triangularArray[ply][ply] = moveBuffer[i]; for (j = ply + 1; j < triangularLength[ply+1]; j++) { triangularArray[ply][j] = triangularArray[ply+1][j]; } triangularLength[ply] = triangularLength[ply+1]; } } else unmakeMove(moveBuffer[i]); } } return alpha; }
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; }
// This function should not be called by engines, it would be greatly inefficient // It exists simply to find the legal moves for a particular // square, such as is needed when running in a GUI uchar Board0x88::generateMoves(uchar row, uchar col, MoveList & moveList) { MoveList allMoves; uchar totalMoves = generateMoves(allMoves); for (uchar i = 0; i < totalMoves; i++) { if (allMoves[i].sourceRow == row && allMoves[i].sourceCol == col) { makeMove(allMoves[i]); if (!isCellAttacked(mKingIndex[!mSideToMove], mSideToMove) ) moveList.addMove(allMoves[i]); unmakeMove(allMoves[i]); } } return moveList.size(); }
int BOARD::getPvLine(int depth) { int c=0; int move=getPvMove(); while(move != NOMOVE && c<depth) { PVARRAY[c++]=move; makeMove(move); //Not Checked if the move was legal move=getPvMove(); } while(ply>0) unmakeMove(); return c; }
void displayPV() { int i; char sanMove[12]; for (i = 0; i < board.triangularLength[0]; i++) { toSan(board.triangularArray[0][i], sanMove); std::cout << sanMove << " "; makeMove(board.triangularArray[0][i]); } for (i = board.triangularLength[0]-1; i >= 0; i--) { unmakeMove(board.triangularArray[0][i]); } if ((!XB_MODE) && (i < 3)) std::cout << " "; // make sure to overwrite any remaining output of mode 3 std::cout << std::endl; std::cout.flush(); }
int BOARD::quiescence(int alpha,int beta) { if((info.nodes & 2047) ==0) check(); info.nodes++; if(repeat() || fifty >=100) return 0; if(ply >MAXDEPTH -1) return evaluate(); int value=evaluate(); if(value >= beta) return beta; if(value >alpha) alpha=value; int legal=0; int oldalpha=alpha; int bestmove=NOMOVE; int score=-INFINITE; MOVE_LIST moveList; moveList.count=0; generateAllCap(moveList); std::sort(moveList.l,moveList.l+moveList.count,so); for(int i=0;i<moveList.count;i++) { //pickBest(i,moveList); if(!makeMove(moveList.l[i].move)) continue; score=-quiescence(-beta,-alpha); unmakeMove(); if(info.stopped) return 0; legal++; if(score >alpha) { if(score >=beta) { if(legal==1) info.fhf++; info.fh++; return beta; //beta - cutoff } alpha=score; bestmove=moveList.l[i].move; } } if(alpha != oldalpha) storePv(bestmove); return alpha; }
int BOARD::alphabeta(int depth,int alpha,int beta,bool donull) { if(depth==0) { //info.nodes++; return quiescence(alpha,beta); //return evaluate(); } if((info.nodes & 2047) == 0) check(); info.nodes++; if((repeat() || fifty >=100) & ply) return 0; if(ply >MAXDEPTH -1) return evaluate(); bool kingsafe; int pos=(int)log2(board[side+4] & -board[side+4]); kingsafe=isSafe(pos,side); if(!kingsafe) depth++; int score = -INFINITE; /* if(donull && ply && kingsafe && depth >4 && bigPiece[side] >0) { makeNullMove(); score=-alphabeta(depth-4,-beta,-beta+1,false); unmakeNullMove(); if(info.stopped) return 0; if(score>=beta) return beta; }*/ int legal=0; int oldalpha=alpha; int bestmove=NOMOVE; score=-INFINITE; MOVE_LIST moveList; moveList.count=0; generateAllMoves(moveList); int pvMove=getPvMove(); if(pvMove != NOMOVE) { for(int i=0;i<moveList.count;i++) { if(moveList.l[i].move == pvMove) { moveList.l[i].score=2000000; break; } } } std::sort(moveList.l,moveList.l+moveList.count,so); for(int i=0;i<moveList.count;i++) { //pickBest(i,moveList); if(!makeMove(moveList.l[i].move)) continue; legal++; score=-alphabeta(depth-1,-beta,-alpha,donull); unmakeMove(); if(info.stopped) return 0; if(score >alpha) { if(score >=beta) { if(legal==1) info.fhf++; info.fh++; if(CAP(moveList.l[i].move) == EMPTY) { searchKillers[1][depth]=searchKillers[0][depth]; searchKillers[0][depth]=moveList.l[i].move; } return beta; //beta - cutoff } alpha=score; bestmove=moveList.l[i].move; if(CAP(moveList.l[i].move) == EMPTY) searchHistory[cboard[FROM(bestmove)]][TO(bestmove)]+=depth; } } if(legal == 0) { if(!kingsafe) return -MATE + ply; else return 0; } if(alpha != oldalpha) storePv(bestmove); return alpha; }
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; }
void Board::displaySearchStats(int mode, int depth, int score) { // displays various search statistics // only to be used when ply = 0 // mode = 1 : display header // mode = 2 : display full stats, including score and latest PV // mode = 3 : display current root move that is being searched // depth = ply, score = loop counter in the search move list char sanMove[12]; U64 dt, hh, mm, ss; switch (mode) { case 1: std::cout << "depth score nodes time knps PV" << std::endl; break; case 2: dt = msStop - msStart; // depth printf("%5d ", depth); // score printf("%+6.2f ", float(score/100.0)); // nodes searched if (inodes > 100000000) printf("%6.0f%c ", float(inodes/1000000.0), 'M'); else if (inodes > 10000000) printf("%6.2f%c ", float(inodes/1000000.0), 'M'); else if (inodes > 1000000) printf("%6.0f%c ", float(inodes/1000.0), 'K'); else if (inodes > 100000) printf("%6.1f%c ", float(inodes/1000.0), 'K'); else if (inodes > 10000) printf("%6.2f%c ", float(inodes/1000.0), 'K'); else printf("%7d ", inodes); // search time if (dt > 3600000) { hh = dt/3600000; mm = (dt - 3600000*hh)/60000; ss = (dt - 3600000*hh - 60000*mm)/1000; printf("%02d%c", hh, ':'); printf("%02d%c", mm, ':'); printf("%02d ", ss); } else if (dt > 60000) { mm = dt/60000; ss = (dt - 60000*mm)/1000; printf(" %02d%c", mm, ':'); printf("%02d ", ss); } else if (dt > 10000) printf(" %6.1f%c ", float(dt/1000.0), 's'); else if (dt > 1000) printf(" %6.2f%c ", float(dt/1000.0), 's'); else if (dt > 0) printf(" %5dms ", dt); else printf(" 0ms "); // search speed if (dt > 0) std::cout << std::setw(5) << (inodes/dt) << " "; else std::cout << " - "; // store this PV: rememberPV(); // display the PV displayPV(); break; case 3: // Note that the numbers refer to pseudo-legal moves: printf("%12s (%2d/%2d)%16s", " ", score+1, moveBufLen[depth+1]-moveBufLen[depth], " "); unmakeMove(moveBuffer[score]); toSan(moveBuffer[score], sanMove); std::cout << sanMove; makeMove(moveBuffer[score]); // printf("... \n"); printf("... \r"); std::cout.flush(); break; default: break; } return; }
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 } }