private void IterativeDeepening_OnSearchUpdate(object sender, SearchStatistics statistics)
        {
            // Main search result
            _interactiveConsole.WriteLine($"  === Depth: {statistics.Depth}, Score: {statistics.Score}, Best: {statistics.PrincipalVariation[0]}, " +
                                          $"Time: {((float) statistics.SearchTime / 1000):F} s");

            // Normal search
            _interactiveConsole.WriteLine($"   Normal search: Nodes: {statistics.Nodes}, Leafs: {statistics.Leafs}, " +
                                          $"Branching factor: {statistics.BranchingFactor:F}, Beta cutoffs: {statistics.BetaCutoffs}");

            // Quiescence search
            _interactiveConsole.WriteLine($"   Q search: Nodes: {statistics.QNodes}, Leafs: {statistics.QLeafs}, " +
                                          $"Branching factor: {statistics.QBranchingFactor:F}, Beta cutoffs: {statistics.QBetaCutoffs}");

            // Total
            _interactiveConsole.WriteLine($"   Total: Nodes: {statistics.TotalNodes} ({((float)statistics.TotalNodesPerSecond / 1000000):F} MN/s), " +
                                          $"Leafs: {statistics.TotalLeafs}, Branching factor: {statistics.TotalBranchingFactor:F}, " +
                                          $"Beta cutoffs: {statistics.TotalBetaCutoffs}");

#if DEBUG
            // Beta cutoffs at first move
            _interactiveConsole.WriteLine($"   Beta cutoffs at first move: {statistics.BetaCutoffsAtFirstMove} ({statistics.BetaCutoffsAtFirstMovePercent:F} %), " +
                                          $"Q Beta cutoffs at first move: {statistics.QBetaCutoffsAtFirstMove} ({statistics.QBetaCutoffsAtFirstMovePercent:F} %)");

            // Transposition statistics
            _interactiveConsole.WriteLine($"   TT: " +
                                          $"Added: {statistics.TTAddedEntries}, " +
                                          $"Replacements: {statistics.TTReplacements} ({statistics.TTReplacesPercent:F} %), " +
                                          $"Hits: {statistics.TTHits} ({statistics.TTHitsPercent:F} %), " +
                                          $"Missed: {statistics.TTNonHits}, " +
                                          $"Filled: {TranspositionTable.GetFillLevel():F} %");

            // Pawn hash table statistics
            _interactiveConsole.WriteLine($"   PHT: " +
                                          $"Added: {statistics.EvaluationStatistics.PHTAddedEntries}, " +
                                          $"Replacements: {statistics.EvaluationStatistics.PHTReplacements} ({statistics.EvaluationStatistics.PHTReplacesPercent:F} %), " +
                                          $"Hits: {statistics.EvaluationStatistics.PHTHits} ({statistics.EvaluationStatistics.PHTHitsPercent:F} %), " +
                                          $"Missed: {statistics.EvaluationStatistics.PHTNonHits}, " +
                                          $"Filled: {PawnHashTable.GetFillLevel():F} %");

            // Evaluation hash table statistics
            _interactiveConsole.WriteLine($"   EHT: " +
                                          $"Added: {statistics.EvaluationStatistics.EHTAddedEntries}, " +
                                          $"Replacements: {statistics.EvaluationStatistics.EHTReplacements} ({statistics.EvaluationStatistics.EHTReplacesPercent:F} %), " +
                                          $"Hits: {statistics.EvaluationStatistics.EHTHits} ({statistics.EvaluationStatistics.EHTHitsPercent:F} %), " +
                                          $"Missed: {statistics.EvaluationStatistics.EHTNonHits}, " +
                                          $"Filled: {EvaluationHashTable.GetFillLevel():F} %");

            _interactiveConsole.WriteLine($"   Valid TT moves: {statistics.TTValidMoves}, Invalid TT moves: {statistics.TTInvalidMoves}, " +
                                          $"IID hits: {statistics.IIDHits}, Loud generations: {statistics.LoudMovesGenerated}, " +
                                          $"Quiet generations: {statistics.QuietMovesGenerated}");
#endif


            _interactiveConsole.WriteLine();
        }
Example #2
0
        public void Run(params string[] parameters)
        {
            TranspositionTable.Clear();
            PawnHashTable.Clear();
            EvaluationHashTable.Clear();
            KillerHeuristic.Clear();
            HistoryHeuristic.Clear();

            _uciClient.BoardState.SetDefaultState();
        }
        private void Test(BoardState boardState, string name, int depth)
        {
            _interactiveConsole.WriteLine($" == {name}:");

            TranspositionTable.Clear();
            PawnHashTable.Clear();
            EvaluationHashTable.Clear();
            KillerHeuristic.Clear();
            HistoryHeuristic.Clear();

            var context = new SearchContext(boardState)
            {
                MaxDepth = depth
            };

            IterativeDeepening.OnSearchUpdate += IterativeDeepening_OnSearchUpdate;
            IterativeDeepening.FindBestMove(context);
            IterativeDeepening.OnSearchUpdate -= IterativeDeepening_OnSearchUpdate;

            _interactiveConsole.WriteLine();
        }
Example #4
0
        public static int FindBestMove(SearchContext context, int depth, int ply, int alpha, int beta)
        {
            context.Statistics.QNodes++;

            if (ply > context.Statistics.SelectiveDepth)
            {
                context.Statistics.SelectiveDepth = ply;
            }

            if (context.BoardState.Pieces[context.BoardState.ColorToMove][Piece.King] == 0)
            {
                context.Statistics.QLeafs++;
                return(SearchConstants.NoKingValue);
            }

            if (context.BoardState.IsKingChecked(ColorOperations.Invert(context.BoardState.ColorToMove)))
            {
                context.Statistics.QLeafs++;
                return(-SearchConstants.NoKingValue);
            }

            var standPat = 0;

            var evaluationEntry = EvaluationHashTable.Get(context.BoardState.Hash);

            if (evaluationEntry.IsKeyValid(context.BoardState.Hash))
            {
                standPat = evaluationEntry.Score;

#if DEBUG
                context.Statistics.EvaluationStatistics.EHTHits++;
#endif
            }
            else
            {
                standPat = Evaluation.Evaluate(context.BoardState, true, context.Statistics.EvaluationStatistics);
                EvaluationHashTable.Add(context.BoardState.Hash, (short)standPat);

#if DEBUG
                context.Statistics.EvaluationStatistics.EHTNonHits++;
                context.Statistics.EvaluationStatistics.EHTAddedEntries++;

                if (evaluationEntry.Key != 0 || evaluationEntry.Score != 0)
                {
                    context.Statistics.EvaluationStatistics.EHTReplacements++;
                }
#endif
            }

            if (standPat >= beta)
            {
                context.Statistics.QLeafs++;
                return(standPat);
            }

            if (standPat > alpha)
            {
                alpha = standPat;
            }

            Span <Move>  moves      = stackalloc Move[SearchConstants.MaxMovesCount];
            Span <short> moveValues = stackalloc short[SearchConstants.MaxMovesCount];

            var movesCount = context.BoardState.GetAvailableCaptureMoves(moves);
            MoveOrdering.AssignQValues(context.BoardState, moves, moveValues, movesCount);

            for (var moveIndex = 0; moveIndex < movesCount; moveIndex++)
            {
                MoveOrdering.SortNextBestMove(moves, moveValues, movesCount, moveIndex);

                if (moveValues[moveIndex] < 0)
                {
#if DEBUG
                    context.Statistics.QSEEPrunes++;
#endif
                    break;
                }

                if (standPat + moveValues[moveIndex] + SearchConstants.QFutilityPruningMargin < alpha)
                {
#if DEBUG
                    context.Statistics.QFutilityPrunes++;
#endif
                    break;
                }

                context.BoardState.MakeMove(moves[moveIndex]);
                var score = -FindBestMove(context, depth - 1, ply + 1, -beta, -alpha);
                context.BoardState.UndoMove(moves[moveIndex]);

                if (score > alpha)
                {
                    alpha = score;

                    if (alpha >= beta)
                    {
#if DEBUG
                        if (moveIndex == 0)
                        {
                            context.Statistics.QBetaCutoffsAtFirstMove++;
                        }
                        else
                        {
                            context.Statistics.QBetaCutoffsNotAtFirstMove++;
                        }
#endif

                        context.Statistics.QBetaCutoffs++;
                        break;
                    }
                }
            }

            return(alpha);
        }