示例#1
0
        /// <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);
        }
示例#2
0
        /// <summary>
        /// Carries out a narrow Quiescent search where only capure moves are searched.
        /// </summary>
        /// <param name="alpha">Current alpha value.</param>
        /// <param name="beta">Current beta value.</param>
        /// <returns>The score of a board.</returns>
        private int QuiescentSearch(int alpha, int beta)
        {
            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;
            QuiescentFlag quiescentFlag = QuiescentFlag.AlphaScore;

            if (m_quiescentTable.ProbeScore(m_board.BoardHash(false), alpha, beta, ref score))
            {
                return(score);
            }

            score = m_evaluator.EvaluatePosition(m_board);

            if (score >= beta)
            {
                m_quiescentTable.RecordHash(m_board.BoardHash(false), QuiescentFlag.BetaScore, beta);
                return(beta);
            }

            if (score > alpha)
            {
                alpha         = score;
                quiescentFlag = QuiescentFlag.ExactScore;
            }

            MoveOrganizer currentMoves = new MoveOrganizer();

            m_board.GeneratePseudoLegalMoves(currentMoves);
            currentMoves.Sort(m_captureMoveCompare);

            foreach (Move move in currentMoves)
            {
                if (!move.IsCaptureMove)
                {
                    break;
                }

                if (move.Execute(m_board))
                {
                    score = -QuiescentSearch(-beta, -alpha);
                    move.UnExecute(m_board);

                    if (score >= beta)
                    {
                        m_quiescentTable.RecordHash(m_board.BoardHash(false), QuiescentFlag.BetaScore, beta);
                        return(beta);
                    }

                    if (score > alpha)
                    {
                        alpha         = score;
                        quiescentFlag = QuiescentFlag.ExactScore;
                    }
                }
            }

            m_quiescentTable.RecordHash(m_board.BoardHash(false), quiescentFlag, alpha);
            return(alpha);
        }
示例#3
0
        /// <summary>
        /// Starts a iterative depending search, returning the move engine is to play.
        /// </summary>
        /// <param name="board">Board to find move for.</param>
        /// <param name="moveOrganizer">move organizer to where best move is to be found in.</param>
        /// <param name="aimSearchTime">The search time to aim for. Actual search time might be between this and maxSearchTime.</param>
        /// <param name="maxSearchTime">The maximum amount of seconds to seach for move.</param>
        /// <param name="maxSearchDepth">Maximum depth for full width search.</param>
        /// <returns>Move considered best.</returns>
        public Move FindBestMove(Board board, MoveOrganizer moveOrganizer, TimeSpan aimSearchTime, TimeSpan maxSearchTime, int maxSearchDepth)
        {
            moveOrganizer.Sort(m_captureMoveCompare);

            List <ScoreMove> scoredMoves = new List <ScoreMove>(moveOrganizer.Count);

            foreach (Move move in moveOrganizer)
            {
                scoredMoves.Add(new ScoreMove(move));
            }

            if (scoredMoves.Count == 1)
            {
                return(scoredMoves[0].Move);
            }

            m_alphaBetaTable.Open();
            m_quiescentTable.Open();
            m_searchRollback    = m_externalAbort = false;
            m_board             = board;
            m_searchStartTime   = DateTime.Now;
            m_searchMaxTime     = maxSearchTime;
            m_nodesVisited      = 0;
            m_nextRollbackCheck = ROLLBACK_INTERVAL_COUNT;

            bool continueSearch = true;

            for (m_searchDepth = 0; m_searchDepth <= maxSearchDepth && continueSearch; ++m_searchDepth)
            {
                int movesSearched = 0;
                int alpha         = m_evaluator.AlphaValue;
                int beta          = -m_evaluator.AlphaValue;

                foreach (ScoreMove scoreMove in scoredMoves)
                {
                    scoreMove.Move.Execute(m_board);

                    if (movesSearched == 0)
                    {
                        scoreMove.TemporaryScore = -AlphaBetaSearch(-beta, -alpha, m_searchDepth, false, true);
                    }
                    else //search in the same way as we do with pv moves by narrowing together alpha and beta
                    {
                        scoreMove.TemporaryScore = -AlphaBetaSearch(-alpha - 1, -alpha, m_searchDepth, false, false);
                        if ((scoreMove.TemporaryScore > alpha) && (scoreMove.TemporaryScore < beta))
                        {
                            scoreMove.TemporaryScore = -AlphaBetaSearch(-beta, -alpha, m_searchDepth, false, true);
                        }
                    }

                    scoreMove.Move.UnExecute(m_board);

                    if (scoreMove.TemporaryScore > alpha)
                    {
                        alpha = scoreMove.TemporaryScore - 1;
                    }

                    if (m_searchRollback || m_externalAbort || (scoreMove.ValidScore >= -m_evaluator.MateValue))
                    {
                        continueSearch = false;
                        break;
                    }

                    ++movesSearched;
                }

                if (movesSearched == scoredMoves.Count)
                {
                    foreach (ScoreMove scoreMove in scoredMoves)
                    {
                        scoreMove.ValidScore = scoreMove.TemporaryScore;
                    }
                }

                scoredMoves.Sort(m_scoreMoveCompare);
                OutputWriter.Write("Depth: " + m_searchDepth + ", Score: " + scoredMoves[0].ValidScore);

                if ((DateTime.Now - m_searchStartTime) >= aimSearchTime)
                {
                    continueSearch = false;
                }
            }

            OutputWriter.Write("Nodes: " + m_nodesVisited);
            OutputWriter.Write("Time: " + (DateTime.Now - m_searchStartTime).ToString());

            if (m_externalAbort)
            {
                OutputWriter.Write("Search aborted");
                return(null);
            }

            int canditates;

            for (canditates = 1; canditates < scoredMoves.Count; ++canditates)
            {
                if (m_scoreMoveCompare.Compare(scoredMoves[0], scoredMoves[canditates]) != 0)
                {
                    break;
                }
            }

            Random selector = new Random();
            int    idx      = selector.Next(canditates);

            OutputWriter.Write("Selecting: " + scoredMoves[idx].Move.From + " " + scoredMoves[idx].Move.To + " from " + scoredMoves.Count + " candidates");
            return(scoredMoves[idx].Move);
        }