std::string pgn::Ply::toStdString() const { std::string res; if (isLongCastle()) { res += "O-O-O"; if (isCheckMate()) res += "#"; else if (isCheck()) res += "+"; } else if (isShortCastle()) { res += "O-O"; if (isCheckMate()) res += "#"; else if (isCheck()) res += "+"; } else { res += hdata->piece.toStdString(); if (hdata->fromSquare != '-') res += hdata->fromSquare; if (isCapture()) res += "x"; res += hdata->toSquare.col(); res += hdata->toSquare.row(); if (hdata->promoted) { res += "="; res += hdata->promoted->toStdString(); } if (isCheckMate()) res += "#"; else if (isCheck()) res += "+"; } if (hdata->comment) res += hdata->comment->toStdString(); return res; }
int Engine::AlphaBeta(int depth, int alpha, int beta, vector<Move>* variation, bool cannull, bool dopv) { #ifdef BLITZKRIEG_DEBUG Bitset tablekey = pos.PawnKey; if (alpha > beta || alpha < CONS_NEGINF || beta > CONS_INF) { cout << "info string ERROR: alpha > beta" << alpha << " " << beta << " " << ply << endl; } #endif if (isDraw()) return 0; ///Quiescence if (depth == 0) { int value = QuiescenceSearch(alpha, beta); //go to quiescence /*if (value > alpha && value < beta) PvSize = ply - 1;*/ //Table.Save(pos.TTKey,0,value,TT_EXACT,CONS_NULLMOVE); return value; } nodes++; if (nodes%CheckupNodeCount == 0) { checkup(); //nodes = 0; } ///Repetition if (ply != 0 && pos.isRepetition()) //check for repetition { //Table.Save(pos.TTKey,depth,0,TT_EXACT,CONS_NULLMOVE); return 0; } ///Probe int probe = Table.Probe(pos.TTKey, depth, alpha, beta); Move ttbestmove = createNullMove(pos.epsquare); if (probe != CONS_TTUNKNOWN) { //cout << probe << " found " << pos.TTKey << endl; if (ply != 0) { tthitcount++; return probe; } /*else { ttbestmove = Table.getBestMove(pos.TTKey); if (!ttbestmove.isNullMove()) { variation->push_back(ttbestmove); tthitcount++; return probe; } }*/ } //if (probe.avoidnull) cannull = false; int leafevalprobe = Table.Probe(pos.TTKey, 0, alpha, beta); if (leafevalprobe!=CONS_TTUNKNOWN //&& leafevalprobe.entry->bound==TT_EXACT ) { Evaluation[ply] = leafevalprobe; //use TT probe as a better leafeval } else { if (ply > 0 && currentVariation[ply] == CONS_NULLMOVE) //if last move was a nullmove, just invert score { Evaluation[ply] = -Evaluation[ply-1]; assert(!cannull); } else { Evaluation[ply] = LeafEval<false>(); } } ///Razoring if (!dopv && ply != 0 && depth < 4 && !incheck[ply] && (((Evaluation[ply] + getRazorMargin(depth)) <= alpha))) { prunednodes++; if (depth <= 1 && (Evaluation[ply] + getRazorMargin(3)) <= alpha) return QuiescenceSearch(alpha, beta); int ralpha = alpha - getRazorMargin(depth); int v = QuiescenceSearch(ralpha, ralpha + 1); if (v <= ralpha) return v; } ///Futility if (depth < 5 && ply != 0 && !incheck[ply] && ((Evaluation[ply] - getFutilityMargin(depth)) >= beta)) { futilitynodes++; return (Evaluation[ply] - getFutilityMargin(depth)); } int bound = TT_ALPHA; /*vector<Move> dummyline; dummyline.reserve(128);*/ /*vector<Move> lineptr; lineptr.reserve(128);*/ vector<Move> line; line.reserve(128); Move m; int score = 0; ///Null Move bool madenullmove = false; Bitset Pieces = pos.OccupiedSq ^ pos.Pieces[COLOR_WHITE][PIECE_PAWN] ^ pos.Pieces[COLOR_BLACK][PIECE_PAWN]; int pieceCount = popcnt(Pieces); if (cannull && !dopv && depth >= 3 && incheck[ply] == false && (pieceCount>2) //side to move does not have only pawns(to avoid zugzwang) //&& Evaluation[ply] >= beta ) { madenullmove = true; //int R = depth > 5 ? 3 : 2; //dynamic depth-based reduction int R = ((823 + 67 * depth) / 256 + std::min(max(0, Evaluation[ply] - beta) / PieceMaterial[PIECE_PAWN], 3)); m = createNullMove(pos.epsquare); ply++; #ifdef BLITZKRIEG_DEBUG Bitset ttkeynull = pos.TTKey; #endif pos.forceMove(m); /*bool fullnullmovesearch = true; if (depth >= 7) { score = -QuiescenceSearchStandPat(-beta, -beta+1); if (score < beta) fullnullmovesearch = false; } if(fullnullmovesearch)*/ score = -AlphaBeta(max(0, depth - R), -beta, -beta+1, &line, false, false); //make a null-window search (we don't care by how much it fails high, if it does) ply--; pos.unmakeMove(m); if (line.empty()!=true) Threats[ply] = line.at(line.size() - 1); #ifdef BLITZKRIEG_DEBUG if (ttkeynull != pos.TTKey) { cout << "info string ERROR: Null TT fail" << endl; _getch(); } #endif if (score >= beta) { //cout << "Null move cutoff " << beta << endl; /*if (probe.avoidnull) badavoidnull++;*/ nullcutoffs++; return score; } //if (score < alpha - 100) //score is so bad, we are in danger, so increase depth //{ // depth++; //} } //futility pruning //bool futilityprune = false; //if (depth < 4 && !underCheck && // (((leafeval + FutilityMargin[depth]) <= alpha))) //futility pruning //{ // futilitynodes++; // futilityprune = true; //} //movesort(vec,depth); bool alpharaised = false; bool foundlegal = false; Move alphamove = CONS_NULLMOVE; int finalalpha = -1; int firstalpha = -1; //vec = pos.generateMoves(); vector<Move> vec; vec.reserve(128); //movegentime.Start(); //if (futilityprune) //{ // pos.generateCaptures(vec); //search only captures in futility pruning //} //else //{ pos.generateMoves(vec); //} //movegentime.Stop(); /*vector<Move> line; line.reserve(128);*/ /*vector<int> scores; scores.reserve(128); generateCaptureScores(vec, scores);*/ if (probe==CONS_TTUNKNOWN && (dopv || Evaluation[ply] + 256 >= beta) && depth >= 2) //internal iterative deepening { int score = AlphaBeta(depth-2, alpha, beta, &line, false, dopv); } int evaldiff = ply >= 2 ? Evaluation[ply] - Evaluation[ply - 2] : 0; vector<Move> quietmoves; quietmoves.reserve(128); int bestscore = CONS_NEGINF; //int oldsortphase = SORTPHASE_NONE; for (unsigned int i = 0;i < vec.size();i++) //search { line.clear(); //dummyline.clear(); //m = vec.at(i); //int tablekey2 = pos.TTKey; m = getHighestScoringMove(vec, i); #ifdef BLITZKRIEG_DEBUG if (SortPhase == SORTPHASE_NONE) cout << "info string Sort Phase error" << endl; #endif int capturedpiece = m.getCapturedPiece(); int special = m.getSpecial(); int movingpiece = m.getMovingPiece(); int moveto = m.getTo(); int movefrom = m.getFrom(); int iscapture = isCapture(m); /*int see = 0; int evade_see = 0; Move smallestattckr = pos.getSmallestAttacker(getOpponent(pos.turn), movefrom); if (iscapture) { see = StaticExchangeEvaluation(moveto, movefrom, movingpiece, capturedpiece); } if (!smallestattckr.isNullMove()) { evade_see = StaticExchangeEvaluation(movefrom, smallestattckr.getTo(), smallestattckr.getMovingPiece(), movingpiece); }*/ //if (iscapture && depth <= 1 && see < 0) //{ //prune bad captures at low depths // continue; //} //if (depth < 8 // && !alpharaised // && noMaterialGain(m) // //&& !incheck[ply] // && !incheck[ply - 1] // && movingpiece!=PIECE_PAWN // ) //{ // if (i >= 2+depth*depth) // { // continue; // } //} if (!pos.makeMove(m)) { continue; } //if (vec.size() == 1 || (foundlegal==false && i==vec.size()-1)) //singular extension, only 1 legal move, so extend //{ // depth++; //} foundlegal = true; ply++; currentVariation[ply] = m; score = 0; int reductiondepth = 1; ///Check Extension if (pos.underCheck(pos.turn)) { incheck[ply] = true; reductiondepth--; } else { incheck[ply] = false; } ///Recapture Extension /*if (ply > 1 && m.getTo() == currentVariation[ply - 1].getTo() && iscapture && isCapture(currentVariation[ply - 1])) { reductiondepth--; }*/ //extend when capturing the last piece /*if (isCapture(m) && getSquare2Piece(capturedpiece) != PIECE_PAWN && popcnt(pos.Pieces[COLOR_WHITE][PIECE_PAWN]) + popcnt(pos.Pieces[COLOR_BLACK][PIECE_PAWN]) + 2 == popcnt(pos.OccupiedSq)) { reductiondepth -= 3; }*/ /*if (depth < 16) { if (!incheck[ply - 1] && !incheck[ply] && movingpiece != PIECE_PAWN && noMaterialGain(m)) { if(i > 3 + (1 << (depth-1))) { pos.unmakeMove(m); incheck[ply] = false; ply--; continue; } } }*/ //if (!dopv && depth < 4 && ((Evaluation[ply] + getSmallRazorMargin(depth)) <= alpha)) //small forward razoring //{ // reductiondepth++; //} ///Latemove Reduction if (!alpharaised //&& i >= 4 && depth >= 4 //&& special!=PIECE_QUEEN //&& (see < 0 || !iscapture) && SortPhase >= SORTPHASE_HISTORY //&& noMaterialGain(m) //&& (KillerMoves[0][ply].getTo() != moveto || KillerMoves[0][ply].getFrom() != movefrom) //&& (KillerMoves[1][ply].getTo() != moveto || KillerMoves[1][ply].getFrom() != movefrom) && !incheck[ply] && !incheck[ply-1] //&& (movingpiece!=PIECE_PAWN || getRank(getColorMirror(getOpponent(pos.turn), moveto))<6) //dont reduce pawn moves past 6th rank //&& m!=Threats[ply] ) { if (evaldiff > 0) reductiondepth += min(depth - 4, min((int)i,4)); else reductiondepth += min(depth - 4, min((int)i+1,5)); assert((depth - reductiondepth) >= 3); if (!dopv && HistoryScores[movingpiece][moveto] < 0) //history reduction { reductiondepth++; } if (m == Threats[ply] && reductiondepth > 0) //decrease reduction if move is a threat { reductiondepth = max(reductiondepth - 1, 0); } //if (noMaterialGain(m) && !smallestattckr.isNullMove() && evade_see < 0) //decrease reduction if move evades a capture //{ // reductiondepth = max(reductiondepth - 1, 0); //} } //if (isCapture(m) && !dopv && see > 400 && depth>=5) //prune really good captures //{ // reductiondepth += 4; //} //if (alpha_counter != 0 && (depth-reductiondepth)>=3 && i>((double)alphalast_sum/alpha_counter) && capturedpiece == SQUARE_EMPTY && special == PIECE_NONE // && !pos.underCheck(pos.turn) // && (KillerMoves[0][ply].getTo() != m.getTo() || KillerMoves[0][ply].getFrom() != m.getFrom()) // && (KillerMoves[1][ply].getTo() != m.getTo() || KillerMoves[1][ply].getFrom() != m.getFrom())) //latemove reduction //{ // reductiondepth += 2; // /*reductiondepth ++; // if (i >= 8 && depth >= 6) // { // reductiondepth++; // if (i >= 12 && depth >= 9) // reductiondepth++; // }*/ //} ///Search if(dopv && i>0 && depth>=3) //principal variation search { score = -AlphaBeta(max(depth - reductiondepth, 0), -alpha - 1, -alpha, &line, true, false); if(score > alpha && score < beta) //check for failure { line.clear(); score = -AlphaBeta(depth - 1, -beta, -alpha, &line, true, true); //research alpharaised = false; pvresearch++; //cout << "pv research" << endl; } } else { score = -AlphaBeta(max(depth - reductiondepth,0), -beta, -alpha, &line, true, dopv); //cout << "latemove" << endl; if(score > alpha && score < beta && reductiondepth>1) { line.clear(); score = -AlphaBeta(depth - 1, -beta, -alpha, &line, true, dopv); latemoveresearch++; } } currentVariation[ply] = CONS_NULLMOVE; incheck[ply] = false; ply--; pos.unmakeMove(m); assert(score > CONS_NEGINF && score < CONS_INF); assert(score >= CONS_MATED && score <= -CONS_MATED); if(score>=beta) { if(noMaterialGain(m)) { //if(Table.getBestMove(pos.TTKey)!=m) //dont store hash move as a killer setKiller(m, depth, score); int bonus = depth*depth; HistoryScores[movingpiece][m.getTo()] += bonus; if (HistoryScores[movingpiece][m.getTo()] > 200000) //prevent overflow of history values { for (int i = 0;i < 6;i++) { for (int j = 0;j < 64;j++) { HistoryScores[i][j] /= 2; } } } for (int i = 0;i < quietmoves.size();i++) { /*if(HistoryScores[quietmoves.at(i).getMovingPiece()][quietmoves.at(i).getTo()] <= bonus) HistoryScores[quietmoves.at(i).getMovingPiece()][quietmoves.at(i).getTo()] = 0; else*/ HistoryScores[quietmoves.at(i).getMovingPiece()][quietmoves.at(i).getTo()] -= bonus; } } Table.Save(pos.TTKey, depth, score, TT_BETA, m); #ifdef BLITZKRIEG_STATS betacutoff_counter++; betacutoff_sum += i+1; if (i == 0) firstbetacutoffcount++; #endif return score; //fail soft beta cutoff } else if(score>bestscore) { bestscore = score; if (score > alpha) { bound = TT_EXACT; alpha = score; alpharaised = true; alphamove = m; *variation = line; if (noMaterialGain(m)) HistoryScores[movingpiece][m.getTo()] += depth; #ifdef BLITZKRIEG_STATS if (firstalpha == -1) { firstalpha = i; } finalalpha = i; #endif } } if (noMaterialGain(m)) { quietmoves.push_back(m); } } if(!foundlegal) { //if(futilityprune) //{ // //movegentime.Start(); // vec.clear(); // pos.generateMoves(vec); // //movegentime.Stop(); // int flag = 1; // for(int i = 0;i<vec.size();i++) // { // if(pos.makeMove(m)) // { // unmakeMove(m); // flag = 0; // break; // } // } // if(flag) // { // if(pos.underCheck(pos.turn)) // { // return CONS_MATED-ply; // } // else // { // return CONS_DRAW; // } // } //} //else { if(pos.underCheck(pos.turn)) { return CONS_MATED+ply; } else { return CONS_DRAW; } } } if(!alphamove.isNullMove()) { variation->push_back(alphamove); //if(dopv) // PrincipalVariation[ply] = alphamove; /*if (depth == 1) { PvSize = ply; PvPly = ply; }*/ /*else if (ply == PvPly - 1) { PrincipalVariation[ply] = alphamove; PvPly = ply; }*/ //HistoryScores[alphamove.getFrom()][alphamove.getTo()] += depth+finalalpha; #ifdef BLITZKRIEG_STATS alpha_counter++; alphalast_sum += (finalalpha + 1); alphafirst_sum += (firstalpha + 1); #endif } Table.Save(pos.TTKey, depth, bestscore, bound, alphamove); /*if (ply == 0) { cout << "info string Stored: " << alphamove.toString() << " " << bound << " " << depth << endl; }*/ #ifdef BLITZKRIEG_DEBUG if (pos.PawnKey != tablekey) { cout << "info string ERROR: Pawn TT key doesnt match" << endl; } #endif return bestscore; }