Esempio n. 1
0
        private void OnBestMoveFound(BestMoveParams bestMoveParams, EvaluatedMove evaluatedMove)
        {
            if (null == evaluatedMove)
            {
                throw new ArgumentNullException(nameof(evaluatedMove));
            }

            if (evaluatedMove != bestMoveParams.BestMove)
            {
                BestMoveFound?.Invoke(this, new BestMoveFoundEventArgs(evaluatedMove.Move, evaluatedMove.Depth, evaluatedMove.ScoreAfterMove));
                bestMoveParams.BestMove = evaluatedMove;
            }
        }
Esempio n. 2
0
        private async Task <Move> GetBestMoveAsync(GameBoard gameBoard, int maxDepth, TimeSpan maxTime, int maxHelperThreads, CancellationToken token)
        {
            if (null == gameBoard)
            {
                throw new ArgumentNullException(nameof(gameBoard));
            }

            if (maxDepth < 0)
            {
                throw new ArgumentOutOfRangeException("maxDepth");
            }

            if (maxTime < TimeSpan.Zero)
            {
                throw new ArgumentOutOfRangeException("maxTime");
            }

            if (maxHelperThreads < 0)
            {
                throw new ArgumentOutOfRangeException("maxHelperThreads");
            }

            if (gameBoard.GameIsOver)
            {
                throw new Exception("Game is over.");
            }

            BestMoveParams bestMoveParams = new BestMoveParams()
            {
                MaxSearchDepth   = maxDepth,
                MaxSearchTime    = maxTime,
                MaxHelperThreads = maxHelperThreads,
            };

            EvaluatedMoveCollection evaluatedMoves = await EvaluateMovesAsync(gameBoard, bestMoveParams, token);

            if (evaluatedMoves.Count == 0)
            {
                throw new Exception("No moves after evaluation!");
            }

            // Make sure at least one move is reported
            OnBestMoveFound(bestMoveParams, evaluatedMoves.BestMove);

            return(bestMoveParams.BestMove.Move);
        }
Esempio n. 3
0
        private async Task <EvaluatedMoveCollection> EvaluateMovesAsync(GameBoard gameBoard, BestMoveParams bestMoveParams, CancellationToken token)
        {
            EvaluatedMoveCollection movesToEvaluate = new EvaluatedMoveCollection();

            EvaluatedMove bestMove = null;

            // Try to get cached best move if available
            ulong key = gameBoard.ZobristKey;

            if (TranspositionTable.TryLookup(key, out TranspositionTableEntry tEntry) && null != tEntry.BestMove)
            {
                bestMove = new EvaluatedMove(tEntry.BestMove, tEntry.Value, tEntry.Depth);
                OnBestMoveFound(bestMoveParams, bestMove);
            }

            if (null != bestMove && double.IsPositiveInfinity(bestMove.ScoreAfterMove))
            {
                // Winning move, don't search
                movesToEvaluate.Add(bestMove);
                return(movesToEvaluate);
            }

            List <EvaluatedMove> validMoves = GetPreSortedValidMoves(gameBoard, bestMove);

            movesToEvaluate.Add(validMoves, false);

            if (movesToEvaluate.Count <= 1 || bestMoveParams.MaxSearchDepth == 0)
            {
                // No need to search
                return(movesToEvaluate);
            }

            // Iterative search
            int depth = 1 + Math.Max(0, movesToEvaluate.BestMove.Depth);

            while (depth <= bestMoveParams.MaxSearchDepth)
            {
                // Start LazySMP helper threads
                CancellationTokenSource helperCTS = new CancellationTokenSource();
                Task[] helperThreads = StartHelperThreads(gameBoard, depth, bestMoveParams.MaxHelperThreads, helperCTS);

                // "Re-sort" moves to evaluate based on the next iteration
                movesToEvaluate = await EvaluateMovesToDepthAsync(gameBoard, depth, movesToEvaluate, token);

                // End LazySMP helper threads
                EndHelperThreads(helperThreads, helperCTS);

                // Fire BestMoveFound for current depth
                OnBestMoveFound(bestMoveParams, movesToEvaluate.BestMove);

                if (double.IsInfinity(movesToEvaluate.BestMove.ScoreAfterMove))
                {
                    // The best move ends the game, stop searching
                    break;
                }

                // Prune game-losing moves if possible
                movesToEvaluate.PruneGameLosingMoves();

                if (movesToEvaluate.Count <= 1)
                {
                    // Only one move, no reason to keep looking
                    break;
                }

                if (token.IsCancellationRequested)
                {
                    // Cancelled, stop searching
                    break;
                }

                depth = 1 + Math.Max(depth, movesToEvaluate.BestMove.Depth);
            }

            return(movesToEvaluate);
        }