コード例 #1
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
        /// <summary>Performs the iterative deepening search.</summary>
        SearchResult Search(StateType initialState, SearchLimiter limiter,
                            out Node <StateType, ActionType> solution, bool bidirectional)
        {
            if (limiter != null)
            {
                limiter.Start();
            }

            solution = new Node <StateType, ActionType>();

            for (DepthLimit = 1; limiter == null || !limiter.LimitReached; DepthLimit++)
            {
                SearchResult result = bidirectional ?
                                      base.FinishBidirectionalSearch(initialState, limiter, out solution) :
                                      base.FinishSearch(initialState, limiter, out solution);

                // if the search completed without reaching any limit, then we're done
                if (result != SearchResult.LimitReached)
                {
                    return(result);
                }

                // if the depth limit is about to wrap around, there's no point in continuing
                if (DepthLimit == int.MaxValue)
                {
                    return(SearchResult.LimitReached);
                }
            }

            return(SearchResult.LimitReached);
        }
コード例 #2
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
        /// <summary>This method does all of the work of running a forward-only search except for starting the limiter, if
        /// any.
        /// </summary>
        protected SearchResult FinishSearch(StateType initialState, SearchLimiter limiter,
                                            out Node <StateType, ActionType> solution)
        {
            solution = new Node <StateType, ActionType>();

            IQueue <Node <StateType, ActionType> > fringe = CreateQueue();
            Dictionary <StateType, Node <StateType, ActionType> > statesSeen =
                EliminateDuplicateStates ? new Dictionary <StateType, Node <StateType, ActionType> >() : null;
            bool limitHit = false;

            fringe.Enqueue(MakeNode(initialState));

            while (fringe.Count != 0)
            {
                if (limiter != null && limiter.LimitReached)
                {
                    limitHit = true;
                    break;
                }

                Node <StateType, ActionType> node = fringe.Dequeue();
                if (Problem.IsGoal(node.State))
                {
                    solution = node;
                    return(SearchResult.Success);
                }

                limitHit |= TryEnqueueNodes(fringe, node, statesSeen, false);
            }

            return(limitHit ? SearchResult.LimitReached : SearchResult.Failed);
        }
コード例 #3
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
 /// <include file="documentation.xml" path="/AI/Search/ISearch/Search_State/*"/>
 public override SearchResult Search(StateType initialState, SearchLimiter limiter,
                                     out Node <StateType, ActionType> solution)
 {
     if (limiter != null)
     {
         limiter.Start();
     }
     return(FinishSearch(initialState, limiter, out solution));
 }
コード例 #4
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
 /// <include file="documentation.xml" path="/AI/Search/IBidirectionalGraphSearch/BidirectionalSearch_State/*"/>
 public override SearchResult BidirectionalSearch(StateType initialState, SearchLimiter limiter,
                                                  out Node <StateType, ActionType> solution)
 {
     AssertBidirectionallySearchable();
     if (limiter != null)
     {
         limiter.Start();
     }
     return(FinishBidirectionalSearch(initialState, limiter, out solution));
 }
コード例 #5
0
        /// <include file="documentation.xml" path="/AI/Search/GameSearchBase/IterativeDeepeningSearch/*"/>
        /// <include file="documentation.xml" path="/AI/Search/ISearch/SearchCommon/param[@name='limiter']"/>
        /// <include file="documentation.xml" path="/AI/Search/ISearch/Search_State/param[@name='initialState']"/>
        public SearchResult IterativeDeepeningSearch(StateType initialState, SearchLimiter limiter,
                                                     out StateActionPair <StateType, ActionType> solution)
        {
            SearchResult result;
            int          userDepthLimit = DepthLimit; // save the original depth limit so we can restore it later

            if (limiter == null)                      // if we have an no limit, we might as well do a regular search
            {
                DepthLimit = Infinite;                // with unlimited depth because we have unlimited time
                result     = Search(initialState, limiter, out solution);
            }
            else
            {
                PrepareToStartSearch(initialState); // otherwise, verify that the search is valid

                if (limiter != null)
                {
                    limiter.Start();
                }
                BeginIterativeDeepeningSearch(initialState);

                result   = SearchResult.Failed; // assume that we couldn't complete a single iteration
                solution = new StateActionPair <StateType, ActionType>();

                // gradually increase the depth limit, starting from 1
                for (DepthLimit = 1; ; DepthLimit = DepthLimit == int.MaxValue ? Infinite : DepthLimit + 1)
                {
                    // start a new search with the given depth limit, and run it until it completes or the time expires
                    StateActionPair <StateType, ActionType> currentSolution;
                    SearchResult currentResult = PerformSearch(initialState, limiter, out currentSolution);

                    // Failed, in this case, means that the search couldn't complete because of the limiter, while LimitReached
                    // means that the search completed but was limited by the depth limit
                    if (currentResult == SearchResult.Failed)
                    {
                        break;
                    }

                    // the search completed, so store the result and solution
                    result   = currentResult;
                    solution = currentSolution;

                    // if the search was not limited by depth, increasing the depth won't help, so we're done
                    if (currentResult != SearchResult.LimitReached)
                    {
                        break;
                    }
                }
                EndIterativeDeepeningSearch();
            }

            DepthLimit = userDepthLimit; // restore the previous depth limit
            return(result);
        }
コード例 #6
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
        /// <include file="documentation.xml" path="/AI/Search/ISearch/Search_State/*"/>
        public override SearchResult Search(StateType initialState, SearchLimiter limiter, out Node <StateType, ActionType> solution)
        {
            if (limiter != null)
            {
                limiter.Start();
            }
            solution = new Node <StateType, ActionType>();

            float costLimit = Problem.GetHeuristic(initialState);

            while (true)
            {
                IQueue <Node <StateType, ActionType> > fringe = CreateQueue();
                Dictionary <StateType, Node <StateType, ActionType> > statesSeen =
                    EliminateDuplicateStates ? new Dictionary <StateType, Node <StateType, ActionType> >() : null;
                float newLimit      = float.PositiveInfinity;
                bool  depthLimitHit = false;

                fringe.Enqueue(MakeNode(initialState));

                while (fringe.Count != 0)
                {
                    if (limiter != null && limiter.LimitReached)
                    {
                        return(SearchResult.LimitReached);
                    }

                    Node <StateType, ActionType> node = fringe.Dequeue();
                    if (Problem.IsGoal(node.State))
                    {
                        solution = node;
                        return(SearchResult.Success);
                    }

                    float cost = node.PathCost + node.HeuristicCost;
                    if (cost <= costLimit)
                    {
                        depthLimitHit |= TryEnqueueNodes(fringe, node, statesSeen, false);
                    }
                    else if (cost < newLimit)
                    {
                        newLimit = cost;          // the new limit is the lowest of those that exceeded the old limit
                    }
                }

                if (float.IsInfinity(newLimit))
                {
                    return(depthLimitHit ? SearchResult.LimitReached : SearchResult.Failed);
                }
                costLimit = newLimit;
            }
        }
コード例 #7
0
        /// <include file="documentation.xml" path="/AI/Search/ISearch/Search_State/*"/>
        public sealed override SearchResult Search(StateType initialState, SearchLimiter limiter,
                                                   out StateActionPair <StateType, ActionType> solution)
        {
            PrepareToStartSearch(initialState);
            if (limiter != null)
            {
                limiter.Start();
            }
            // from PerformSearch(), Failed means that the limiter caused the search to abort, while LimitReached means that the
            // search completed, but was truncated by the depth limit. we'll convert Failed to LimitReached here.
            SearchResult result = PerformSearch(initialState, limiter, out solution);

            return(result == SearchResult.Failed ? SearchResult.LimitReached : result);
        }
コード例 #8
0
        /// <include file="documentation.xml" path="/AI/Search/GameSearchBase/PerformSearch/*"/>
        protected override SearchResult PerformSearch(StateType initialState, SearchLimiter limiter,
                                                      out StateActionPair <StateType, ActionType> solution)
        {
            solution = new StateActionPair <StateType, ActionType>();

            float bestUtility = float.NegativeInfinity;
            int   player      = Game.GetPlayerToMove(initialState);

            this.limiter  = limiter;
            depthLimitHit = searchAborted = false;

            foreach (Move <StateType, ActionType> move in Game.GetSuccessors(initialState)) // for each move available
            {
                // get the estimated utilities of the move for all players
                float[] utilities = GetExpectedUtilities(move.State, 1);
                if (searchAborted)
                {
                    return(SearchResult.Failed);    // if the limiter caused an abort, just return immediately
                }
                // if the estimated utility of this move for the player moving at the root is the best yet, save it
                if (utilities[player] > bestUtility)
                {
                    bestUtility = utilities[player];
                    solution    = new StateActionPair <StateType, ActionType>(move.State, move.Action);

                    if (bestUtility == MaxUtility) // if the given move is optimal, we don't need to search further
                    {
                        // if a terminal state was reached in the line of search that led to this maximal utility, then we don't
                        // need another round of iterative deepening search because there's no doubt about this particular move,
                        // and since this move is optimal, that's all that matters
                        if (wasTerminal)
                        {
                            depthLimitHit = false;
                        }
                        break;
                    }
                }
            }

            this.limiter = null; // release the limiter

            return(depthLimitHit ? SearchResult.LimitReached : SearchResult.Success);
        }
コード例 #9
0
        /// <include file="documentation.xml" path="/AI/Search/GameSearchBase/PerformSearch/*"/>
        protected override SearchResult PerformSearch(StateType initialState, SearchLimiter limiter,
                                                      out StateActionPair <StateType, ActionType> solution)
        {
            solution = new StateActionPair <StateType, ActionType>();

            // If we're performing an iterative deepening search, rootUtilities and rootSuccessors will be non-null. In that
            // case, we should use them to store utilities and retrieve successor nodes. The purpose is to allow some state,
            // in particular move ordering at the root, to be retained between iterations of the iterative deepening search.
            // The rootUtilities array is used to sort the rootSuccessors array so that the best moves from the previous
            // iteration can be tried first.
            //
            // TODO: should we extend this idea to store the best moves from each ply in the search?
            // TODO: we should probably also implement the killer heuristic and/or history heuristics (this requires some
            //       more complex communication between the game and the search)
            // TODO: we should implement the scout part of negascout
            // this url is quite good: http://www.fierz.ch/strategy.htm
            if (rootUtilities != null)
            {
                for (int i = 0; i < rootUtilities.Length; i++)
                {
                    rootUtilities[i] = float.PositiveInfinity;
                }
            }

            this.limiter  = limiter;
            depthLimitHit = wasTerminal = searchAborted = false;

            float bestUtility = float.NegativeInfinity, best0 = float.NegativeInfinity, best1 = float.NegativeInfinity;
            int   player = Game.GetPlayerToMove(initialState);
            int   index  = 0; // the index of the successor within rootSuccessor that we're currently examining

            foreach (Move <StateType, ActionType> move in
                     rootSuccessors != null ? rootSuccessors : Game.GetSuccessors(initialState))
            {
                // invoke the the alpha-beta search to estimate the utility value of the move's ending state
                float utility = GetUtilityEstimate(move.State, best0, best1, 1, player);

                // we'll use Failed to indicate that the limiter caused the search to abort
                if (searchAborted)
                {
                    return(SearchResult.Failed);
                }

                // the alpha-beta search returns the utility of the move from the perspective of the player whose turn it is in
                // the state resulting from the move. so find out which player that is.
                int otherPlayer = Game.GetPlayerToMove(move.State);

                // if the player moving at the successor is not the same as the player moving at the root, then we need to
                // reverse the utility. however, if the move led to a chance node, represented as a "player" of -1, the utility
                // is already from the correct viewpoint, so we don't need to reverse it.
                if (player != otherPlayer && otherPlayer != -1)
                {
                    utility = -utility;
                }

                // if we're doing an iterative deepening search, store the utility of this move
                if (rootUtilities != null)
                {
                    rootUtilities[index++] = utility;
                }

                // if the move is better than whatever we've got so far, store the move as the new best move
                if (utility > bestUtility)
                {
                    bestUtility = utility;
                    // we'll store the best move in the "solution" parameter
                    solution = new StateActionPair <StateType, ActionType>(move.State, move.Action);
                    // also, if the move was so good that it got the maximum utility, there's no point in searching further, so
                    // we'll end the search immediately
                    if (utility == MaxUtility)
                    {
                        // if a terminal state was reached in the line of search that led to this maximal utility, then we don't
                        // need another round of iterative deepening search because there's no doubt about this particular move,
                        // and since this move is optimal, that's all that matters
                        if (wasTerminal)
                        {
                            depthLimitHit = false;
                        }
                        break;
                    }

                    // update the alpha-beta values for the player at the root
                    if (player == 0)
                    {
                        if (utility > best0)
                        {
                            best0 = utility;
                        }
                    }
                    else
                    {
                        if (utility > best1)
                        {
                            best1 = utility;
                        }
                    }
                }
            }

            // if we're doing iterative deepening, sort the moves by utility
            if (rootSuccessors != null)
            {
                SortRootSuccessors();
            }

            this.limiter = null; // release the limiter

            return(depthLimitHit ? SearchResult.LimitReached : SearchResult.Success);
        }
コード例 #10
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
 /// <include file="documentation.xml" path="/AI/Search/IBidirectionalGraphSearch/BidirectionalSearch/*"/>
 public SearchResult BidirectionalSearch(SearchLimiter limiter, out Node <StateType, ActionType> solution)
 {
     return(BidirectionalSearch(Problem.GetInitialState(), limiter, out solution));
 }
コード例 #11
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
 /// <include file="documentation.xml" path="/AI/Search/ISearch/Search/*"/>
 public sealed override SearchResult Search(SearchLimiter limiter, out Node <StateType, ActionType> solution)
 {
     return(Search(Problem.GetInitialState(), limiter, out solution));
 }
コード例 #12
0
 /// <include file="documentation.xml" path="/AI/Search/GameSearchBase/PerformSearch/*"/>
 protected abstract SearchResult PerformSearch(StateType initialState, SearchLimiter limiter,
                                               out StateActionPair <StateType, ActionType> solution);
コード例 #13
0
 /// <include file="documentation.xml" path="/AI/Search/GameSearchBase/IterativeDeepeningSearch/*"/>
 /// <include file="documentation.xml" path="/AI/Search/ISearch/SearchCommon/param[@name='limiter']"/>
 public SearchResult IterativeDeepeningSearch(SearchLimiter limiter,
                                              out StateActionPair <StateType, ActionType> solution)
 {
     return(IterativeDeepeningSearch(game.GetInitialState(), limiter, out solution));
 }
コード例 #14
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
        /// <summary>This method does all of the work of running a bidirectional search except for ensuring that searching
        /// bidirectionally is safe and starting the limiter, if any.
        /// </summary>
        protected SearchResult FinishBidirectionalSearch(StateType initialState, SearchLimiter limiter,
                                                         out Node <StateType, ActionType> solution)
        {
            solution = new Node <StateType, ActionType>();

            // the queues into which nodes will be placed
            IQueue <Node <StateType, ActionType> > leftFringe, rightFringe;
            // dictionaries keeping track of states that have been visited before, and the best paths to them.
            Dictionary <StateType, Node <StateType, ActionType> > leftStates, rightStates;

            // initialize the search
            leftFringe  = CreateQueue();
            rightFringe = CreateQueue();
            leftStates  = new Dictionary <StateType, Node <StateType, ActionType> >();
            rightStates = EliminateDuplicateStates ? new Dictionary <StateType, Node <StateType, ActionType> >() : null;

            leftFringe.Enqueue(MakeNode(initialState));
            foreach (StateType goalState in BidiProblem.GetGoalStates())
            {
                rightFringe.Enqueue(MakeNode(goalState));
            }

            bool limitHit = false;                                  // keeps track of whether the search has hit some limit preventing it from exploring further

            while (leftFringe.Count != 0 && rightFringe.Count != 0) // while there are still nodes in both open lists
            {
                if (limiter != null && limiter.LimitReached)
                {
                    limitHit = true;
                    break;
                }

                // get a node from the forward search
                Node <StateType, ActionType> leftNode = leftFringe.Dequeue();
                if (Problem.IsGoal(leftNode.State)) // if it's a goal, we've found a forward-only solution, so return it
                {
                    solution = leftNode;
                    return(SearchResult.Success);
                }

                // this proceeds much like standard search...
                if (EliminateDuplicateStates)
                {
                    limitHit |= TryEnqueueNodes(leftFringe, leftNode, leftStates, false);
                }
                else if (leftNode.Depth >= DepthLimit)
                {
                    limitHit = true;
                }
                else
                {
                    foreach (Node <StateType, ActionType> child in GetNodes(leftNode, null, false))
                    {
                        leftFringe.Enqueue(child);

                        Node <StateType, ActionType> existingChild;
                        if (!leftStates.TryGetValue(child.State, out existingChild) || IsNodeBetter(child, existingChild))
                        {
                            leftStates[child.State] = child;
                        }
                    }
                }

                // now take a node from the reverse search
                Node <StateType, ActionType> rightNode = rightFringe.Dequeue();
                // if it has been visited by the forward search, we've found a link between the two
                if (leftStates.TryGetValue(rightNode.State, out leftNode))
                {
                    solution = BuildBidirectionalSolution(leftNode, rightNode); // convert it into a forward-only solution
                    return(SearchResult.Success);                               // and return it
                }

                limitHit |= TryEnqueueNodes(rightFringe, rightNode, rightStates, true);
            }

            // if it gets here, we couldn't find a solution
            return(limitHit ? SearchResult.LimitReached : SearchResult.Failed);
        }
コード例 #15
0
 /// <include file="documentation.xml" path="/AI/Search/ISearch/Search/*"/>
 public sealed override SearchResult Search(SearchLimiter limiter,
                                            out StateActionPair <StateType, ActionType> solution)
 {
     return(Search(game.GetInitialState(), limiter, out solution));
 }
コード例 #16
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
 /// <include file="documentation.xml" path="/AI/Search/ISearch/Search_State/*"/>
 public override SearchResult Search(StateType initialState, SearchLimiter limiter,
                                     out Node <StateType, ActionType> solution)
 {
     return(Search(initialState, limiter, out solution, false));
 }
コード例 #17
0
ファイル: GraphSearch.cs プロジェクト: Slyvester/AdamMil.net
 /// <include file="documentation.xml" path="/AI/Search/IBidirectionalGraphSearch/BidirectionalSearch_State/*"/>
 public abstract SearchResult BidirectionalSearch(StateType initialState, SearchLimiter limiter,
                                                  out Node <StateType, ActionType> solution);