Move Board::think() { // =========================================================================== // This is the entry point for search, it is intended to drive iterative deepening // The search stops if (whatever comes first): // - there is no legal move (checkmate or stalemate) // - there is only one legal move (in this case we don't need to search) // **later** - time is up // - the search is interrupted by the user, or by winboard // - the search depth is reached // =========================================================================== int score, legalmoves, currentdepth; Move singlemove; // =========================================================================== // Check if the game has ended, or if there is only one legal move, // because then we don't need to search: // =========================================================================== if (isEndOfgame(legalmoves, singlemove)) return NOMOVE; if (legalmoves == 1) { std::cout << "forced move: "; displayMove(singlemove); std::cout << std::endl; return singlemove; } // =========================================================================== // There is more than legal 1 move, so prepare to search: // =========================================================================== lastPVLength = 0; memset(lastPV, 0 , sizeof(lastPV)); memset(whiteHeuristics, 0, sizeof(whiteHeuristics)); memset(blackHeuristics, 0, sizeof(blackHeuristics)); inodes = 0; // display console header displaySearchStats(1, 0, 0); timer.init(); msStart = timer.getms(); // iterative deepening: for (currentdepth = 1; currentdepth <= searchDepth; currentdepth++) { // clear the buffers: memset(moveBufLen, 0, sizeof(moveBufLen)); memset(moveBuffer, 0, sizeof(moveBuffer)); memset(triangularLength, 0, sizeof(triangularLength)); memset(triangularArray, 0, sizeof(triangularArray)); followpv = true; score = alphabetapvs(0, currentdepth, -LARGE_NUMBER, LARGE_NUMBER); msStop = timer.getms(); displaySearchStats(2, currentdepth, score); // stop searching if the current depth leads to a forced mate: if ((score > (CHECKMATESCORE-currentdepth)) || (score < -(CHECKMATESCORE-currentdepth))) currentdepth = searchDepth; } return (lastPV[0]); }
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; }
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; }
Move Board::think() { // =========================================================================== // This is the entry point for search, it is intended to drive iterative deepening // The search stops if (whatever comes first): // - there is no legal move (checkmate or stalemate) // - there is only one legal move (in this case we don't need to search) // - time is up // - the search is interrupted by the user, or by winboard // - the search depth is reached // =========================================================================== int score, legalmoves, currentdepth; Move singlemove; lastScore = 0; // =========================================================================== // Check if the game has ended, or if there is only one legal move, // because then we don't need to search: // =========================================================================== if (isEndOfgame(legalmoves, singlemove)) return NOMOVE; if (legalmoves == 1) { //LC std::cout << "forced move: "; displayMove(singlemove); std::cout << std::endl; /*if (XB_MODE && XB_POST) { printf("0 0 0 0 "); displayMove(singlemove); std::cout << std::endl; }*/ return singlemove; } // =========================================================================== // There is more than legal 1 move, so prepare to search: // =========================================================================== if (XB_MODE) timeControl(); lastPVLength = 0; memset(lastPV, 0 , sizeof(lastPV)); memset(whiteHeuristics, 0, sizeof(whiteHeuristics)); memset(blackHeuristics, 0, sizeof(blackHeuristics)); inodes = 0; countdown = UPDATEINTERVAL; timedout = false; // display console header displaySearchStats(1, 0, 0); timer.init(); msStart = timer.getms(); // iterative deepening: for (currentdepth = 1; currentdepth <= searchDepth; currentdepth++) { // clear the buffers: memset(moveBufLen, 0, sizeof(moveBufLen)); memset(moveBuffer, 0, sizeof(moveBuffer)); memset(triangularLength, 0, sizeof(triangularLength)); memset(triangularArray, 0, sizeof(triangularArray)); followpv = true; allownull = true; score = alphabetapvs(0, currentdepth, -LARGE_NUMBER, LARGE_NUMBER); lastScore = score; // now check if time is up // if not decide if it makes sense to start another iteration: if (timedout) { //LC std::cout << std::endl; return (lastPV[0]); } else { if (!XB_NO_TIME_LIMIT) { msStop = timer.getms(); if ((msStop - msStart) > (STOPFRAC * maxTime)) { //if (!XB_MODE) std::cout << " ok" << std::endl; return (lastPV[0]); } } } displaySearchStats(2, currentdepth, score); // stop searching if the current depth leads to a forced mate: if ((score > (CHECKMATESCORE-currentdepth)) || (score < -(CHECKMATESCORE-currentdepth))) currentdepth = searchDepth; } return (lastPV[0]); }
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; }