int Minimax::Quiescence(Board *board, GameInfo *gameInfo, int depth, int alpha, int beta) { // Evaluate the node in its current state. If it causes a beta-cutoff, assume that there will be no move further down the //game tree that will result in a better evaluation. Otherwise, set it as the lower bound, alpha. int nodeEvaluation = Eval(board, gameInfo); if (nodeEvaluation >= beta) return beta; if (nodeEvaluation > alpha) alpha = nodeEvaluation; MoveList moveList; // If the node is in check, consider every move. Otherwise, just consider captures/promotions. moveList.generate(gameInfo->turn, board, gameInfo, !MoveList::InCheck(gameInfo->turn, board)); moveList.prune((Piece::Color)!gameInfo->turn, board); for (MoveList::iterator moveItr = moveList.begin(); moveItr != moveList.end(); moveItr++) { Move move = *moveItr; board->executeMove(move); // Save any irreversible game info before making a move. GameInfo::Irreversible irreversible(gameInfo); gameInfo->executeMove(move); int childEval = 0; // If we are at depth 0, just get the score of the board (The score is negated as it is measured relative to the opposite color). if (depth == 0) childEval = -Eval(board, gameInfo); else childEval = -Quiescence(board, gameInfo, depth - 1, -beta, -alpha); board->reverseMove(move); gameInfo->reverseMove(irreversible); if (childEval >= beta) return beta; if (childEval > alpha) alpha = childEval; } return alpha; }
int Minimax::AlphaBeta(Board *board, GameInfo *gameInfo, Move &move, int depth, const int quiescenceDepth, int alpha, int beta) { // If we are at the end of the normal alpha beta search, perform a quiescence search with the given quiescence depth. if (depth == 0) return Quiescence(board, gameInfo, quiescenceDepth, alpha, beta); MoveList moveList; // Generate move list and check game state. GameInfo::State state = gameInfo->updateState(board, moveList); switch (state) { case GameInfo::STALEMATE: case GameInfo::FIFTY_MOVE_RULE: // Returns a score which will always be disregarded. return LARGEST_NUM + 1; case GameInfo::CHECKMATE: // Return arbitrarily large negative score (But not -LARGEST_NUM, as it would be cut off). return -LARGE_NUM; } MoveList::iterator bestMoveItr = moveList.begin(); for (MoveList::iterator moveItr = moveList.begin(); moveItr != moveList.end(); moveItr++) { Move childMove = *moveItr; board->executeMove(childMove); // Save any irreversible game info before making a move. GameInfo::Irreversible irreversible(gameInfo); gameInfo->executeMove(childMove); // Here, the child's evaluation is taken to be the negation of its return value, so that seperate if statements for maximising and minimising //aren't required. int childEval = -AlphaBeta(board, gameInfo, move, depth - 1, quiescenceDepth, -beta, -alpha); board->reverseMove(childMove); gameInfo->reverseMove(irreversible); // If the child evaluates to a score greater than or equal to beta, there is a beta cutoff. This means that there is no further point exploring this // node's moves, as it is known that this node will at least be as bad if not worse than another node elsewhere in the game tree. if (childEval >= beta) { // If a non-capture move caused a beta-cutoff, increase its history weighting. The depth squared is added to the heuristic so that moves near //the leaf nodes don't dominate the heuristic (Leaf node score would be 0 * 0). if (!move.isCapture()) Move::HistoryHeuristic[move.subject_from][move.subject_to] += depth * depth; move = *bestMoveItr; return beta; } // If the child's evaluation is greater than alpha, this is the best move at the moment. if (childEval > alpha) { alpha = childEval; bestMoveItr = moveItr; } } move = *bestMoveItr; return alpha; }
// Algorithme de recherche MinMax avec des coupes Alpha-Beta. // Ici on considere seulement les prises. int Quiescence(int ply, int wtm, int alpha, int beta ) { register int Valeur, AlphaInitiale; if ( ply >= MAXPLY-1 ) return beta; iNodes++; if ( wtm ) { Valeur = Eval(ply, wtm, alpha, beta); } else { Valeur = -Eval(ply, wtm, alpha, beta); } // Remplace si le score est egal au score pour la NULLE. if ( Valeur == DRAWSCORE ) Valeur = DRAWSCORE+1; AlphaInitiale = alpha; if ( Valeur > alpha ) { if ( Valeur >= beta ) return Valeur; alpha = Valeur; pv_length[ply] = ply-1; } // On genere toutes les prises. cb.MoveList[ply].nbmove = 0; GenMoveAttaque(ply, wtm, cb.MoveList[ply]); int keep = -1; // Le materiel. int iScoreMateriel = cb.ScoreMaterielBlanc-cb.ScoreMaterielNoir; iScoreMateriel = wtm?iScoreMateriel:-iScoreMateriel; int delta = alpha-100-iScoreMateriel; int iScoreGain; for( int i=0; i<cb.MoveList[ply].nbmove; i++ ) { // Garder le coup. bool bGarde = false; // On cherche seulement les coups qui ramene le materiel proche de alpha. // ex. Si celui qui jouent vient de se faire prendre une dame et que alpha // dit que le meilleur score est la perte d'un pion et bien ignorer tout // les captures qui ramene pas au moins la dame. if ( ValeurPiece[cb.MoveList[ply].moves[i].Capture] >= delta ) { if (cb.MoveList[ply].moves[i].Capture == ROI) return beta; #ifdef USE_SEE int iScoreCapture = ValeurPiece[cb.MoveList[ply].moves[i].Capture]; iScoreGain = iScoreCapture-ValeurPiece[cb.MoveList[ply].moves[i].Piece]; if ( iScoreGain > 0 || iScoreGain >= 0 && delta <= 0 ) { bGarde = true; } else { iScoreGain = Echange(cb.MoveList[ply].moves[i].From, cb.MoveList[ply].moves[i].To, wtm ); if ( iScoreGain >= 0 ) { bGarde = true; } } #else bGarde = true; #endif } if ( bGarde ) { keep++; cb.MoveList[ply].moves[i].Score = iScoreGain; memcpy( &cb.MoveList[ply].moves[keep], &cb.MoveList[ply].moves[i], sizeof( TMove ) ); } } cb.MoveList[ply].nbmove = keep+1; cb.MoveList[ply].currmove = -1; cb.MoveList[ply].Tri(); Phase[ply] = CAPTURE_MOVES; // Maintenant, evaluer chaque coup. while( NextMove( cb, ply, wtm ) ) { // On execute le coup. MakeMove(ply, cb.MoveList[ply].CurrentMove(), wtm); // Mettre le coup dans le chemin actuel. cb.CurrentPath.moves[ply] = cb.MoveList[ply].moves[cb.MoveList[ply].currmove]; if (!Check(wtm)) Valeur = -Quiescence(ply+1, !wtm, -beta, -alpha); cb.MoveList[ply].CurrentMove().Score = Valeur; // On defait le coup. UnmakeMove(ply, cb.MoveList[ply].CurrentMove(), wtm); // Est-il meileur que notre valeur actuelle? if ( Valeur > alpha ) { if ( Valeur >= beta ) return Valeur; pv_length[ply] = pv_length[ply+1]; pv[ply][ply] = cb.CurrentPath.moves[ply]; memcpy(&pv[ply][ply+1], &pv[ply+1][ply], sizeof(TMove)*(pv_length[ply]-ply)); alpha = Valeur; } #ifdef DEBUG Consistence( cb, cb.MoveList[ply].CurrentMove() ); #endif } return alpha; }