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; }
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; }