Exemplo n.º 1
0
        /// <summary>
        /// Gets the list pf PV nodes for the specified bitboard and color. Nodes count is limited by
        /// <see cref="AIConstants.MaxDepth"/> value to avoid infinite repetitions.
        /// </summary>
        /// <param name="bitboard">The initial bitboard.</param>
        /// <param name="color">The initial color.</param>
        /// <returns>The list of PV nodes.</returns>
        private PVNodesList GetPVNodes(Bitboard bitboard, Color color)
        {
            var pvNodes   = new PVNodesList();
            var boardHash = bitboard.GetHashForColor(color);

            while (_transpositionTable.Exists(boardHash) && pvNodes.Count < AIConstants.MaxDepth)
            {
                var pvNode = _transpositionTable.Get(boardHash);

                if (pvNode.BestMove == null)
                {
                    break;
                }

                pvNodes.Add(pvNode.BestMove);
                bitboard = bitboard.Move(pvNode.BestMove);

                color     = ColorOperations.Invert(color);
                boardHash = bitboard.GetHashForColor(color);
            }

            return(pvNodes);
        }
Exemplo n.º 2
0
        /// <summary>
        /// Temporary method to calculating best move.
        /// </summary>
        /// <param name="color">The player color.</param>
        /// <param name="bitboard">The bitboard.</param>
        /// <param name="depth">The current depth.</param>
        /// <param name="bestMove">The best possible move from nested nodes.</param>
        /// <param name="stats">The AI stats.</param>
        /// <returns>The evaluation score of best move.</returns>
        public int Do(Color color, Bitboard bitboard, int depth, int alpha, int beta, AIStats stats)
        {
            var bestValue     = AIConstants.InitialAlphaValue;
            var enemyColor    = ColorOperations.Invert(color);
            var boardHash     = bitboard.GetHashForColor(color);
            var originalAlpha = alpha;

            stats.TotalNodes++;

            if (bitboard.IsThreefoldRepetition())
            {
                stats.EndNodes++;
                return(0);
            }

            if (_transpositionTable.Exists(boardHash))
            {
                var transpositionNode = _transpositionTable.Get(boardHash);

                if (transpositionNode.Depth >= depth)
                {
                    stats.TranspositionTableHits++;
                    switch (transpositionNode.Type)
                    {
                    case ScoreType.Exact:
                    {
                        return(transpositionNode.Score);
                    }

                    case ScoreType.LowerBound:
                    {
                        alpha = Math.Max(alpha, transpositionNode.Score);
                        break;
                    }

                    case ScoreType.UpperBound:
                    {
                        beta = Math.Min(beta, transpositionNode.Score);
                        break;
                    }
                    }

                    if (alpha >= beta)
                    {
                        return(transpositionNode.Score);
                    }
                }
            }

            if (depth <= 0)
            {
                stats.EndNodes++;
                return(_quiescenceSearch.Do(color, bitboard, alpha, beta, stats));
            }

            var whiteGeneratorMode = GetGeneratorMode(color, Color.White);
            var blackGeneratorMode = GetGeneratorMode(color, Color.Black);

            bitboard.Calculate(whiteGeneratorMode, blackGeneratorMode, false);

            if (bitboard.IsCheck(enemyColor))
            {
                stats.EndNodes++;
                return(AIConstants.MateValue + depth);
            }

            Move bestMove = null;

            var availableMoves = SortMoves(color, bitboard, bitboard.Moves);
            var firstMove      = true;

            foreach (var move in availableMoves)
            {
                var bitboardAfterMove = bitboard.Move(move);
                var nodeValue         = 0;

                if (firstMove)
                {
                    nodeValue = -Do(enemyColor, bitboardAfterMove, depth - 1, -beta, -alpha, stats);
                    firstMove = false;
                }
                else
                {
                    nodeValue = -Do(enemyColor, bitboardAfterMove, depth - 1, -alpha - 1, -alpha, stats);

                    if (nodeValue > alpha && nodeValue < beta)
                    {
                        bitboardAfterMove = bitboard.Move(move);
                        nodeValue         = -Do(enemyColor, bitboardAfterMove, depth - 1, -beta, -alpha, stats);
                    }
                }

                if (nodeValue > bestValue)
                {
                    bestValue = nodeValue;
                    bestMove  = move;
                }

                alpha = Math.Max(nodeValue, alpha);

                if (alpha >= beta)
                {
                    stats.AlphaBetaCutoffs++;
                    break;
                }
            }

            if (bestValue == -(AIConstants.MateValue + depth - 1) && !bitboard.IsCheck(color))
            {
                stats.EndNodes++;
                return(0);
            }

            var updateTranspositionNode = new TranspositionNode();

            updateTranspositionNode.Score    = bestValue;
            updateTranspositionNode.Depth    = depth;
            updateTranspositionNode.BestMove = bestMove;

            if (bestValue <= originalAlpha)
            {
                updateTranspositionNode.Type = ScoreType.UpperBound;
            }
            else if (bestValue >= beta)
            {
                updateTranspositionNode.Type = ScoreType.LowerBound;
            }
            else
            {
                updateTranspositionNode.Type = ScoreType.Exact;
            }

            _transpositionTable.AddOrUpdate(boardHash, updateTranspositionNode);

            return(bestValue);
        }
        /// <summary>
        /// Regular search, the core of AI algorithms.
        /// </summary>
        /// <param name="color">The player color.</param>
        /// <param name="bitboard">The bitboard.</param>
        /// <param name="depth">The current depth.</param>
        /// <param name="alpha">The alpha value.</param>
        /// <param name="beta">The beta value.</param>
        /// <param name="deadline">The deadline (time after which search is immediately terminated).</param>
        /// <param name="helper">The flag indicating whether the search is an helper or not.</param>
        /// <param name="stats">The AI stats.</param>
        /// <returns>The evaluation score of best move.</returns>
        public int Do(Color color, Bitboard bitboard, int depth, int alpha, int beta, long deadline, bool helper, AIStats stats)
        {
            var root = stats.TotalNodes == 0;

            var bestValue     = AIConstants.InitialAlphaValue;
            var enemyColor    = ColorOperations.Invert(color);
            var boardHash     = bitboard.GetHashForColor(color);
            var originalAlpha = alpha;

            stats.TotalNodes++;

            if (bitboard.IsThreefoldRepetition())
            {
                stats.EndNodes++;
                return(0);
            }

#if TRANSPOSITION_TABLE
            if (_transpositionTable.Exists(boardHash))
            {
                var transpositionNode = _transpositionTable.Get(boardHash);

                if (transpositionNode.Depth >= depth)
                {
                    stats.TranspositionTableHits++;
                    switch (transpositionNode.Type)
                    {
                    case ScoreType.Exact:
                    {
                        return(transpositionNode.Score);
                    }

                    case ScoreType.LowerBound:
                    {
                        alpha = Math.Max(alpha, transpositionNode.Score);
                        break;
                    }

                    case ScoreType.UpperBound:
                    {
                        beta = Math.Min(beta, transpositionNode.Score);
                        break;
                    }
                    }

                    if (alpha >= beta)
                    {
                        return(transpositionNode.Score);
                    }
                }
            }
#endif

            if (depth <= 0)
            {
                stats.EndNodes++;
#if QUIESCENCE_SEARCH
                return(_quiescenceSearch.Do(color, bitboard, alpha, beta, stats));
#else
                bitboard.Calculate(GeneratorMode.CalculateAttacks, false);
                return(bitboard.GetEvaluation());
#endif
            }

            var whiteGeneratorMode = GetGeneratorMode(color, Color.White);
            var blackGeneratorMode = GetGeneratorMode(color, Color.Black);
            bitboard.Calculate(whiteGeneratorMode, blackGeneratorMode, false);

            if (bitboard.IsCheck(enemyColor))
            {
                stats.EndNodes++;
                return(AIConstants.MateValue + depth);
            }

            Move bestMove = null;

            var availableMoves = SortMoves(color, depth, bitboard, bitboard.Moves, helper);
            var firstMove      = true;

            foreach (var move in availableMoves)
            {
                if (DateTime.Now.Ticks >= deadline)
                {
                    break;
                }

                if (root)
                {
                    if (_patternsDetector.IsPattern(bitboard, move))
                    {
                        continue;
                    }
                }

                var bitboardAfterMove = bitboard.Move(move);
                var nodeValue         = 0;

                if (firstMove)
                {
                    nodeValue = -Do(enemyColor, bitboardAfterMove, depth - 1, -beta, -alpha, deadline, helper, stats);
#if NEGASCOUT
                    firstMove = false;
#endif
                }
                else
                {
                    nodeValue = -Do(enemyColor, bitboardAfterMove, depth - 1, -alpha - 1, -alpha, deadline, helper, stats);

                    if (nodeValue > alpha && nodeValue < beta)
                    {
                        bitboardAfterMove = bitboard.Move(move);
                        nodeValue         = -Do(enemyColor, bitboardAfterMove, depth - 1, -beta, -alpha, deadline, helper, stats);
                    }
                }

                if (nodeValue > bestValue)
                {
                    bestValue = nodeValue;
                    bestMove  = move;
                }

                alpha = Math.Max(nodeValue, alpha);

                if (alpha >= beta)
                {
                    if (move is QuietMove)
                    {
                        _historyTable.AddKiller(color, depth, bestMove);
                        _killerTable.AddKiller(depth, move);
                    }

#if ALPHABETA_PRUNNING
                    stats.AlphaBetaCutoffs++;
                    break;
#endif
                }
            }

            if (bestValue == -(AIConstants.MateValue + depth - 1) && !bitboard.IsCheck(color))
            {
                stats.EndNodes++;
                return(0);
            }

            var updateTranspositionNode = new TranspositionNode
            {
                Score    = bestValue,
                Depth    = depth,
                BestMove = bestMove,
                Type     = GetTranspositionNodeType(originalAlpha, beta, bestValue)
            };

            _transpositionTable.AddOrUpdate(boardHash, updateTranspositionNode);

            return(bestValue);
        }