Пример #1
0
        public SearchResult Search(IDeterministicState startState, int maxDepth, CancellationToken cancellationToken)
        {
            Assert.AreEqual(expectedDepths[index], maxDepth, "Didn't get expected depth");
            index++;

            return(new SearchResult(startState.Evaluate(1, new List <IState>()), startState));
        }
Пример #2
0
        /// <summary>
        /// Runs a search.
        /// </summary>
        /// <param name="startState">The state that the search will start from</param>
        /// <param name="maxDepth">The search will be terminated after maxDepth</param>
        /// <param name="cancellationToken">Used to cancel the search</param>
        public SearchResult Search(IDeterministicState startState, int maxDepth, CancellationToken cancellationToken)
        {
            if (StateDefinesDepth && CacheMode != CacheMode.NoCache && CacheKeyType != CacheKeyType.StateOnly)
            {
                throw new MinMaxSearchException($"If {nameof(StateDefinesDepth)} the cache key should be of type {CacheKeyType.StateOnly}");
            }

            if (!startState.GetNeighbors().Any())
            {
                throw new NoNeighborsException("start state has no neighbors " + startState);
            }

            if (maxDepth < 1)
            {
                throw new ArgumentException($"{nameof(maxDepth)} must be at least 1. Was {maxDepth}");
            }

            if (SkipEvaluationForFirstNodeSingleNeighbor && startState.GetNeighbors().Count() == 1)
            {
                return(new SearchResult(0, startState, true, true, false));
            }

            var searchContext = new SearchContext(maxDepth, 0, cancellationToken);
            var searchWorker  = new SearchWorker(CreateSearchOptions(), GetThreadManager(maxDepth), cacheManagerFactory());
            var stopwatch     = new Stopwatch();

            stopwatch.Start();
            var result = searchWorker.Evaluate(startState, searchContext);

            stopwatch.Stop();
            result.StateSequence.Reverse();
            result.StateSequence.RemoveAt(0); // Removing the top node will make the result "nicer"
            return(new SearchResult(result, stopwatch.Elapsed, maxDepth, !cancellationToken.IsCancellationRequested));
        }
Пример #3
0
        public SearchResult EvaluateChildren(IDeterministicState startState, SearchContext searchContext,
                                             IDictionary <IState, double> storedStates = null)
        {
            var neighbors = startState.GetNeighbors().ToArray();

            if (!neighbors.Any())
            {
                var evaluation = startState.Evaluate(searchContext.CurrentDepth, searchContext.StatesUpTillNow, searchOptions);
                return(new SearchResult(evaluation, startState));
            }

            var pruned             = false;
            var player             = startState.Turn;
            var results            = new List <Task <SearchResult> >();
            var cancellationSource = CancellationTokenSource.CreateLinkedTokenSource(searchContext.CancellationToken);

            searchContext.CancellationToken = cancellationSource.Token;
            foreach (var state in neighbors)
            {
                var taskResult = Evaluate(startState, searchContext, storedStates, state);
                results.Add(taskResult);

                if (ProcessResultAndReturnWhetherWeShouldBreak(taskResult, searchContext, storedStates, state, player))
                {
                    pruned = true;
                    cancellationSource.Cancel();
                    break;
                }
            }

            return(Reduce(results, player, startState, pruned));
        }
Пример #4
0
        /// <summary>
        /// With this method you can simulate a complete game and compare different engines, search-depths or evaluation-strategies.
        /// </summary>
        /// <param name="maxEngine"> An engine to use for max</param>
        /// <param name="minEngine"> An engine to use for min</param>
        /// <param name="startState"> The starting sate</param>
        /// <param name="playerMaxSearchDepth"> How deep should max search</param>
        /// <param name="playerMinSearchDepth"> How deep should min search</param>
        /// <param name="maxPlayDepth"> After how many moves should we terminate the game if no one won</param>
        /// <param name="maxAlternateEvaluation"> Will be used to evaluate the board on max's turn in stead of the state's regaler Evaluate method (if null, will use the default state's evaluation method)</param>
        /// <param name="minAlternateEvaluation"> Will be used to evaluate the board on min's turn in stead of the state's regaler Evaluate method (if null, will use the default state's evaluation method)</param>
        public static CompetitionResult Compete(SearchEngine maxEngine, SearchEngine minEngine,
                                                IDeterministicState startState, int playerMaxSearchDepth, int playerMinSearchDepth,
                                                int maxPlayDepth = int.MaxValue, Func <IState, int, List <IState>, double> maxAlternateEvaluation            = null,
                                                Func <IState, int, List <IState>, double> minAlternateEvaluation = null, CancellationToken?cancellationToken = null)
        {
            maxEngine.AlternateEvaluation = maxAlternateEvaluation;
            minEngine.AlternateEvaluation = minAlternateEvaluation;

            var currentState  = startState;
            var resultFactory = new CompetitionResultFactory();

            for (int i = 0; ContainsNeigbors(currentState) && i < maxPlayDepth; i++)
            {
                if (cancellationToken.HasValue && cancellationToken.Value.IsCancellationRequested)
                {
                    break;
                }

                var searchResult = currentState.Turn == Player.Max
                    ? maxEngine.Search(currentState, playerMaxSearchDepth, cancellationToken ?? CancellationToken.None)
                    : minEngine.Search(currentState, playerMinSearchDepth, cancellationToken ?? CancellationToken.None);

                resultFactory.AddState(searchResult.NextMove);
                resultFactory.AddTime(searchResult.SearchTime, currentState.Turn);

                currentState = GetNextState(searchResult.NextMove);
            }

            return(resultFactory.GetCompetitionResult());
        }
Пример #5
0
 /// <summary>
 /// With this method you can simulate a complete game and compare different search-depth or evaluation-strategies.
 /// </summary>
 /// <param name="engine"> The engine to use</param>
 /// <param name="startState"> The starting sate</param>
 /// <param name="playerMaxSearchDepth"> How deep should max search</param>
 /// <param name="playerMinSearchDepth"> How deep should min search</param>
 /// <param name="maxPlayDepth"> After how many moves should we terminate the game if no one won</param>
 /// <param name="maxAlternateEvaluation"> Will be used to evaluate the board on max's turn in stead of the state's regaler Evaluate method (if null, will use the default state's evaluation method)</param>
 /// <param name="minAlternateEvaluation"> Will be used to evaluate the board on min's turn in stead of the state's regaler Evaluate method (if null, will use the default state's evaluation method)</param>
 public static CompetitionResult Compete(this SearchEngine engine, IDeterministicState startState,
                                         int playerMaxSearchDepth, int playerMinSearchDepth, Func <IState, int, List <IState>, double> maxAlternateEvaluation = null,
                                         Func <IState, int, List <IState>, double> minAlternateEvaluation = null, int maxPlayDepth = int.MaxValue,
                                         CancellationToken?cancellationToken = null)
 {
     return(Compete(engine, engine.Clone(), startState, playerMaxSearchDepth, playerMinSearchDepth, maxPlayDepth, maxAlternateEvaluation, minAlternateEvaluation, cancellationToken));
 }
        public SearchResult Example1()
        {
            IDeterministicState startState = Utils.GetEmptyTicTacToeState();
            int          searchDepth       = 5;
            SearchEngine engine            = new SearchEngine();
            SearchResult searchResult      = engine.Search(startState, searchDepth);

            return(searchResult);
        }
Пример #7
0
        /// <summary>
        /// Runs an iterative deepening search.
        /// In Iterative search, a depth-limited version of depth-first search is run repeatedly with increasing depth limits till some condition is met.
        /// </summary>
        /// <param name="startState"> The state that the search will start from</param>
        /// <param name="startDepth"> The first search's depth</param>
        /// <param name="maxDepth"> The max search depth</param>
        /// <param name="cancellationToken"> Terminates the search immediately. We will return the best solution found in the deepest completed search (or, if the first search wasn't completed, the results found so far in the first search).</param>
        /// <param name="cancellationTokenAfterFirstSearch">This token will terminate the search once the first search was complete (or immediately if the first search has already been completed).</param>
        public SearchResult IterativeSearch(IDeterministicState startState, int startDepth, int maxDepth,
                                            CancellationToken cancellationToken, CancellationToken cancellationTokenAfterFirstSearch)
        {
            if (startDepth >= maxDepth)
            {
                throw new Exception($"{nameof(startDepth)} (== {startDepth}) must be bigger than {nameof(maxDepth)} ( == {maxDepth})");
            }

            return(IterativeSearch(startState, new RangeEnumerable(startDepth, maxDepth), cancellationToken, cancellationTokenAfterFirstSearch));
        }
Пример #8
0
        /// <summary>
        /// With this method you can simulate a complete game and compare different evaluation-strategies.
        /// </summary>
        /// <param name="engine"> The engine to use</param>
        /// <param name="startState"> The starting sate</param>
        /// <param name="searchDepth"> How deep should we search</param>
        /// <param name="maxPlayDepth"> After how many moves should we terminate the game if no one won</param>
        /// <param name="maxAlternateEvaluation"> Will be used to evaluate the board on max's turn in stead of the state's regaler Evaluate method (if null, will use the default state's evaluation method)</param>
        /// <param name="minAlternateEvaluation"> Will be used to evaluate the board on min's turn in stead of the state's regaler Evaluate method (if null, will use the default state's evaluation method)</param>
        public static CompetitionResult Compete(this SearchEngine engine, IDeterministicState startState,
                                                int searchDepth, Func <IState, int, List <IState>, double> maxAlternateEvaluation = null,
                                                Func <IState, int, List <IState>, double> minAlternateEvaluation = null, int maxPlayDepth = int.MaxValue,
                                                CancellationToken?cancellationToken = null)
        {
            if (maxAlternateEvaluation == null && minAlternateEvaluation == null)
            {
                throw new ArgumentException($"At least one of {nameof(maxAlternateEvaluation)} or {nameof(minAlternateEvaluation)} shouldn't be null");
            }

            return(Compete(engine, engine.Clone(), startState, searchDepth, searchDepth, maxPlayDepth, maxAlternateEvaluation, minAlternateEvaluation, cancellationToken));
        }
Пример #9
0
        public SearchResult RunIterativeSearch()
        {
            IDeterministicState startState = Utils.GetEmptyTicTacToeState();
            int startDepth = 2;
            int endDepth   = 5;
            CancellationTokenSource CancellationTokenSource = new CancellationTokenSource();
            ISearchEngine           engine          = new SearchEngine();
            IterativeSearchWrapper  iterativeEngine = new IterativeSearchWrapper(engine);
            // This will run an IterativeSearch beginning at depth 2, and ending with depth 5 (including)
            SearchResult searchResult = iterativeEngine.IterativeSearch(startState, startDepth, endDepth, CancellationTokenSource.Token);

            return(searchResult);
        }
Пример #10
0
        public void DifferentDepths()
        {
            IDeterministicState startState = Utils.GetEmptyTicTacToeState();
            int          minSearchDepth    = 2;
            int          maxSearchDepth    = 5;
            SearchEngine engine            = new SearchEngine();

            CompetitionResult competitionResult = engine.Compete(startState, maxSearchDepth, minSearchDepth);

            // Print some of the results
            Console.WriteLine("Max Search Time " + competitionResult.MaxTotalTime);
            Console.WriteLine("Min Search Time " + competitionResult.MinTotalTime);
            Console.WriteLine("Final Score " + competitionResult.FinalState.Evaluate(0, new List <IState>()));
        }
        /// <summary>
        /// This method will fill the cache.
        /// It can be useful to run while your opponent is considering their turn (when the program would otherwise be idle).
        /// It's important to remember to cancel this when you're ready to run a search (using the cancellation token).
        /// Note that this is only helpful if you're using the ReuseCache CasheMode.
        /// </summary>
        public static void FillCache(this SearchEngine searchEngine, IDeterministicState startState, CancellationToken cancellationToken, ParallelismMode parallelismMode = ParallelismMode.NonParallelism, int maxDegreeOfParallelism = 1)
        {
            if (searchEngine.CacheMode != CacheMode.ReuseCache)
            {
                throw new MinMaxSearchException($"{nameof(FillCache)} will only work if {nameof(searchEngine.CacheMode)} is set to {CacheMode.ReuseCache}");
            }

            var newEngine = searchEngine.CloneWithCacheManager(searchEngine.CacheManager);

            newEngine.ParallelismMode        = parallelismMode;
            newEngine.MaxDegreeOfParallelism = maxDegreeOfParallelism;

            // Running this will fill the cache
            new IterativeSearchWrapper(newEngine).IterativeSearch(startState, 1, 100, cancellationToken);
        }
Пример #12
0
        private Task <SearchResult> Evaluate(IDeterministicState startState, SearchContext searchContext,
                                             IDictionary <IState, double> storedStates, IState state)
        {
            var taskResult = storedStates != null && storedStates.ContainsKey(state)
                ? Task.FromResult(new SearchResult(storedStates[state], state))
                : threadManager.Invoke(() =>
            {
                var actualStartState = startState is ProbablisticStateWrapper wrapper
                        ? (IState)wrapper.InnerState
                        : startState;
                return(searchWorker.Evaluate(state, searchContext.CloneAndAddState(actualStartState)));
            }, searchContext.CurrentDepth);

            return(taskResult);
        }
Пример #13
0
        public void DifferentEngines()
        {
            IDeterministicState startState = Utils.GetEmptyTicTacToeState();
            int          searchDepth       = 5;
            int          playDepth         = 100;
            SearchEngine engine1           = new SearchEngine();
            SearchEngine engine2           = new SearchEngine();

            CompetitionResult competitionResult = CompetitionManager.Compete(engine1, engine2, startState, searchDepth, searchDepth, playDepth);

            // Print some of the results
            Console.WriteLine("Max Search Time " + competitionResult.MaxTotalTime);
            Console.WriteLine("Min Search Time " + competitionResult.MinTotalTime);
            Console.WriteLine("Final Score " + competitionResult.FinalState.Evaluate(0, new List <IState>()));
        }
Пример #14
0
        /// <summary>
        /// Runs an iterative deepening search.
        /// In Iterative search, a depth-limited version of depth-first search is run repeatedly with increasing depth limits till some condition is met.
        /// </summary>
        /// <param name="startState"> The state that the search will start from</param>
        /// <param name="depths"> A lists of the depths to check</param>
        /// <param name="cancellationToken"> Terminates the search immediately. We will return the best solution found in the deepest completed search (or, if the first search wasn't completed, the results found so far in the first search).</param>
        /// <param name="cancellationTokenAfterFirstSearch">This token will terminate the search once the first search was complete (or immediately if the first search has already been completed).</param>
        public SearchResult IterativeSearch(IDeterministicState startState, IEnumerable <int> depths, CancellationToken cancellationToken, CancellationToken cancellationTokenAfterFirstSearch)
        {
            var stopewatch = new Stopwatch();

            stopewatch.Start();
            var linkedCancellationToken = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, cancellationTokenAfterFirstSearch).Token;

            SearchResult bestResultSoFar = null;
            int          maxDepth        = -1;

            foreach (var depth in depths)
            {
                if (depth < maxDepth)
                {
                    continue;
                }
                else
                {
                    maxDepth = depth;
                }

                var result = searchEngine.Search(startState, depth, bestResultSoFar == null ? cancellationToken : linkedCancellationToken);
                if (linkedCancellationToken.IsCancellationRequested)
                {
                    if (bestResultSoFar == null)
                    {
                        bestResultSoFar = result;
                    }

                    break;
                }

                bestResultSoFar = result;

                if (result.FullTreeSearchedOrPruned)
                {
                    break; // No point searching any deeper
                }
            }
            stopewatch.Stop();

            if (bestResultSoFar == null)
            {
                throw new InternalException($"Code 1000 ({nameof(bestResultSoFar)} is null)");
            }

            return(new SearchResult(bestResultSoFar, stopewatch.Elapsed, maxDepth, bestResultSoFar.IsSearchCompleted));
        }
        public SearchResult Example2()
        {
            IDeterministicState startState = Utils.GetEmptyTicTacToeState();
            int searchDepth = 5;
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
            SearchEngine            engine = new SearchEngine()
            {
                FavorShortPaths = true,
                DieEarly        = true,
                MaxScore        = 99,
                MinScore        = -99
            };
            SearchResult searchResult = engine.Search(startState, searchDepth, cancellationTokenSource.Token);

            return(searchResult);
        }
Пример #16
0
        public void MinAlternateEvaluation()
        {
            IDeterministicState startState = Utils.GetEmptyTicTacToeState();
            int          searchDepth       = 6;
            SearchEngine engine            = new SearchEngine();

            CompetitionResult competitionResult = engine.Compete(startState, searchDepth, minAlternateEvaluation: (s, d, l) => {
                // Some alternate evaluation goes here - you probably don't really want to return 0
                return(0);
            });

            // Print some of the results
            Console.WriteLine("Max Search Time " + competitionResult.MaxTotalTime);
            Console.WriteLine("Min Search Time " + competitionResult.MinTotalTime);
            Console.WriteLine("Final Score " + competitionResult.FinalState.Evaluate(0, new List <IState>()));
        }
Пример #17
0
 /// <summary>
 /// Runs a search asynchronously.
 /// </summary>
 /// <param name="startState"> The state that the search will start from</param>
 /// <param name="maxDepth"> The search will be terminated after maxDepth</param>
 /// <param name="cancellationToken"> Used to cancel the search</param>
 public Task <SearchResult> SearchAsync(IDeterministicState startState, int maxDepth, CancellationToken cancellationToken) =>
 Task.Run(() => Search(startState, maxDepth, cancellationToken));
Пример #18
0
 public static void SetAsEndState(this IDeterministicState state) =>
 A.CallTo(() => state.GetNeighbors()).Returns(new IState[] {});
Пример #19
0
 public static void SetNeigbor(this IDeterministicState state, IState neigbor) =>
 A.CallTo(() => state.GetNeighbors()).Returns(new [] { neigbor });
Пример #20
0
 public static void SetNeigbors(this IDeterministicState state, params IState[] neigbors) =>
 A.CallTo(() => state.GetNeighbors()).Returns(neigbors);
Пример #21
0
 public static void SetNeigbors(this IDeterministicState state, IEnumerable <IState> neigbors) =>
 A.CallTo(() => state.GetNeighbors()).Returns(neigbors);
 /// <summary>
 /// This method will fill the cache.
 /// It can be useful to run while your opponent is considering their turn (when the program would otherwise be idle).
 /// It's important to remember to cancel this when you're ready to run a search (using the cancellation token).
 /// Note that this is only helpful if you're using the ReuseCache CasheMode.
 /// </summary>
 public static Task FillCacheAsync(this SearchEngine searchEngine, IDeterministicState startState, CancellationToken cancellationToken, ParallelismMode parallelismMode = ParallelismMode.NonParallelism, int maxDegreeOfParallelism = 1)
 {
     return(Task.Run(() => searchEngine.FillCache(startState, cancellationToken, parallelismMode, maxDegreeOfParallelism)));
 }
Пример #23
0
 /// <summary>
 /// Runs an iterative deepening search.
 /// In Iterative search, a depth-limited version of depth-first search is run repeatedly with increasing depth limits till some condition is met.
 /// </summary>
 /// <param name="startState"> The state that the search will start from</param>
 /// <param name="startDepth"> The first search's depth</param>
 /// <param name="maxDepth"> The max search depth</param>
 /// <param name="cancellationToken"> Terminates the search immediately. We will return the best solution found in the deepest completed search (or, if the first search wasn't completed, the results found so far in the first search).</param>
 public SearchResult IterativeSearch(IDeterministicState startState, int startDepth, int maxDepth,
                                     CancellationToken cancellationToken) =>
 IterativeSearch(startState, startDepth, maxDepth, cancellationToken, CancellationToken.None);
Пример #24
0
 public SearchResult IterativeSearch(IDeterministicState startState, int startDepth, int maxDepth, CancellationToken cancellationToken) =>
 new IterativeSearchWrapper(this).IterativeSearch(startState, startDepth, maxDepth, cancellationToken);
Пример #25
0
 public SearchResult IterativeSearch(IDeterministicState startState, int startDepth, int maxDepth, TimeSpan timeout) =>
 IterativeSearch(startState, startDepth, maxDepth, new CancellationTokenSource(timeout).Token);
Пример #26
0
 /// <summary>
 /// Runs an iterative deepening search.
 /// In Iterative search, a depth-limited version of depth-first search is run repeatedly with increasing depth limits till some condition is met.
 /// </summary>
 /// <param name="startState"> The state that the search will start from</param>
 /// <param name="depths"> A lists of the depths to check</param>
 /// <param name="cancellationToken"> Terminates the search immediately. We will return the best solution found in the deepest completed search (or, if the first search wasn't completed, the results found so far in the first search).</param>
 public SearchResult IterativeSearch(IDeterministicState startState, IEnumerable <int> depths, CancellationToken cancellationToken) =>
 IterativeSearch(startState, depths, cancellationToken, CancellationToken.None);
Пример #27
0
 /// <summary>
 /// Runs a search.
 /// </summary>
 /// <param name="startState"> The state that the search will start from</param>
 /// <param name="maxDepth"> The search will be terminated after maxDepth</param>
 public SearchResult Search(IDeterministicState startState, int maxDepth) =>
 Search(startState, maxDepth, CancellationToken.None);