コード例 #1
0
        // depth first search of longest walkable path, returns path length and the sequence of moves and boards in reverse order
        private Tuple <int, List <Tuple <Board, BoardSpace> > > LongestPathInReverseOrder(Board board, Func <Board, IEnumerable <BoardSpace> > moveGetter, Action <Board, BoardSpace> makeMove)
        {
            var longestPathLength = 0;
            var path = new List <Tuple <Board, BoardSpace> >();

            foreach (var move in moveGetter(board))
            {
                // bail if we're timing out
                if (_timer.Timeout() || _cancelToken.IsCancellationRequested)
                {
                    break;
                }

                var newBoard = board.Copy();
                makeMove(newBoard, move);

                var child      = LongestPathInReverseOrder(newBoard, moveGetter, makeMove);
                var pathLength = child.Item1 + 1;

                if (pathLength > longestPathLength)
                {
                    longestPathLength = pathLength;
                    path = new List <Tuple <Board, BoardSpace> >(child.Item2)
                    {
                        Tuple.Create(newBoard, move)
                    };
                }
            }

            return(Tuple.Create(longestPathLength, path));
        }
コード例 #2
0
ファイル: AlphaBetaWithStats.cs プロジェクト: spec2e/cs4701
        // recursive alpha beta
        private BestMoveResultWithStats BestMoveInternal(Board board, int depth, int alpha, int beta, CancellationToken cancelToken)
        {
            // if we reached the bottom, return
            if (depth == 0)
            {
                _numNodesAtDepthLimit++;
                return(new BestMoveResultWithStats(_config.Heuristic.Evaluate(board), null));
            }

            var isMaxTurn = board.MyPlayer == board.PlayerToMove;

            var validMoves = board.GetValidMoves();

            // if we hit game over before the depth limit, return infinity/-infinity if it's our/their turn
            if (!validMoves.Any())
            {
                return(new BestMoveResultWithStats(isMaxTurn ? int.MinValue : int.MaxValue, null));
            }

            BoardSpace bestMove = null;

            // generate new boards for each move and evaluate them so we can sort
            var validMovesWithBoard = validMoves.Select(x =>
            {
                var newBoard = board.Copy().Move(x);
                _nodesGeneratedByDepth[depth]++;
                var score = _config.Heuristic.Evaluate(newBoard);
                return(new { move = x, newBoard, score });
            });

            // if we're maxing, sort with largest first, otherwise sort with smallest first
            if (isMaxTurn)
            {
                validMovesWithBoard = validMovesWithBoard.OrderByDescending(x => x.score);
            }
            else
            {
                validMovesWithBoard = validMovesWithBoard.OrderBy(x => x.score);
            }

            // evaluate this board because we'll need to for quiessence search
            var boardScore = _config.Heuristic.Evaluate(board);

            foreach (var move in validMovesWithBoard)
            {
                BestMoveResultWithStats childResult;

                // if we're doing a quiessence search, check to see if heuristic score change is interesting
                if (IsInterestingMove(boardScore, move.score))
                {
                    // extend search depth because this move looks interesting
                    _numNodesQuiessenceSearched++;
                    childResult = BestMoveInternal(move.newBoard, depth, alpha, beta, cancelToken);
                }
                else
                {
                    // normal evaluation
                    childResult = BestMoveInternal(move.newBoard, depth - 1, alpha, beta, cancelToken);
                }

                // if we're near timeout or asked to cancel, just bail :(
                if (_timer.Timeout() || cancelToken.IsCancellationRequested)
                {
                    _timedOut = true;
                    break;
                }

                if (isMaxTurn) // if it's a max turn, we want to check alpha
                {
                    if (childResult.Score > alpha)
                    {
                        alpha    = childResult.Score;
                        bestMove = move.move;
                    }
                }
                else // else it's a min turn, so we want to check beta
                {
                    if (childResult.Score < beta)
                    {
                        beta     = childResult.Score;
                        bestMove = move.move;
                    }
                }

                // alpha-beta trim
                if (alpha >= beta)
                {
                    break;
                }
            }

            // if we didn't find anything good, just return the first one
            if (bestMove == null)
            {
                bestMove = validMoves.First();
            }

            return(new BestMoveResultWithStats(isMaxTurn ? alpha : beta, bestMove));
        }