/// <summary> /// Recursively carries out a full width alpha beta search untill the bottom is reached. /// From then a Quiescent search is carried out to avoid horrison effects. /// </summary> /// <param name="alpha">This is the best score that can be forced by some means. Anything worth less than this is of no use, because there is a strategy that is known to result in a score of alpha.</param> /// <param name="beta">Beta is the worst thing that the opponent has to endure. If the search finds something that returns a score of beta or better, it's too good, so the side to move is not going to get a chance to use this strategy.</param> /// <param name="remainingDepth">Remaining depth to search before bottom is reached.</param> /// <param name="allowNullMove">Is a null move allowed at this search depth.</param> /// <param name="allowTranspotitionTable">Is it allowed to use the transpotition table to probe for scores.</param> /// <returns>The score of a board.</returns> private int AlphaBetaSearch(int alpha, int beta, int remainingDepth, bool allowNullMove, bool allowTranspotitionTable) { if (m_searchRollback) { return(beta); } //At interval check if time is up and rollback if so. if (m_nodesVisited == m_nextRollbackCheck) { if (AbortingSearch()) { m_alphaBetaTable.Close(); m_quiescentTable.Close(); m_searchRollback = true; return(beta); } m_nextRollbackCheck += ROLLBACK_INTERVAL_COUNT; } ++m_nodesVisited; int score = 0; Move pvMove = null; AlphaBetaFlag alphaBetaFlag = AlphaBetaFlag.AlphaScore; if (m_board.State.NonHitAndPawnMovesPlayed >= 100) { return(0); } if (m_board.BoardHistoryFrequency() >= 3) { return(0); } //don't allow transposition table before both players has passed the above repetetion draw checks. if (allowTranspotitionTable && (m_searchDepth - remainingDepth) > 0) { if (m_alphaBetaTable.ProbeScore(m_board.BoardHash(false), alpha, beta, remainingDepth, ref score)) { if (Math.Abs(score) != Math.Abs(m_evaluator.MateValue)) //don't probe a mate value as this actuall might be a mate far down in the tree, and we want to find the mate at lowest depth. { return(score); } } } if (remainingDepth == 0) { score = QuiescentSearch(alpha, beta); if (allowTranspotitionTable) { m_alphaBetaTable.RecordHash(m_board.BoardHash(false), AlphaBetaFlag.ExactScore, remainingDepth, score, pvMove); } return(score); } if (allowNullMove) { //Carry out a null move which is a move where nothing is actually moved. This makes the opponent move //twice in a row. The idea is then to search to a reduced depth and if nothing good come out of this //(for the opponent) the move is considered garbage and we cut it off. NullMove nullMove = new NullMove(m_board, m_evaluator); if (nullMove.Execute()) { int remDepth = (remainingDepth > NULL_REDUCTION) ? remainingDepth - 1 - NULL_REDUCTION : 0; score = -AlphaBetaSearch(-beta, -beta + 1, remDepth, false, false); nullMove.UnExecute(); if (score >= beta) { return(beta); } } } MoveOrganizer currentMoves = new MoveOrganizer(m_alphaBetaTable.ProbePvMove(m_board.BoardHash(false))); m_board.GeneratePseudoLegalMoves(currentMoves); currentMoves.Sort(m_captureMoveCompare); bool validMoveFound = false; foreach (Move move in currentMoves) { if (move.Execute(m_board)) { validMoveFound = true; if (pvMove == null) { score = -AlphaBetaSearch(-beta, -alpha, remainingDepth - 1, true, allowTranspotitionTable); } else { score = -AlphaBetaSearch(-alpha - 1, -alpha, remainingDepth - 1, true, false); if ((score > alpha) && (score < beta)) { score = -AlphaBetaSearch(-beta, -alpha, remainingDepth - 1, true, allowTranspotitionTable); } } move.UnExecute(m_board); if (score >= beta) { if (allowTranspotitionTable) { m_alphaBetaTable.RecordHash(m_board.BoardHash(false), AlphaBetaFlag.BetaScore, remainingDepth, beta, pvMove); } return(beta); } if (score > alpha) { alpha = score; pvMove = move; alphaBetaFlag = AlphaBetaFlag.ExactScore; } } } if (!validMoveFound) { if (m_board.ColorToPlayIsCheck()) { alpha = m_evaluator.MateValue; //Checkmate } else { alpha = 0; //Stalemate } } if (allowTranspotitionTable) { m_alphaBetaTable.RecordHash(m_board.BoardHash(false), alphaBetaFlag, remainingDepth, alpha, pvMove); } return(alpha); }