public void NewGame_NoOneCanWin(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            TicTacToeState startState = Utils.GetEmptyTicTacToeState();

            var engine     = GetSearchEngine(degreeOfParallelism, parallelismMode);
            var evaluation = engine.Search(startState, 10);

            Assert.AreEqual(0, evaluation.Evaluation);
            var lastMove = (IDeterministicState)evaluation.StateSequence.Last();

            Assert.AreEqual(0, lastMove.Evaluate(0, new List <IState>()), "Should have found a wining state");
            Assert.IsTrue(evaluation.FullTreeSearchedOrPruned);
            Assert.IsFalse(evaluation.AllChildrenAreDeadEnds);

            if (degreeOfParallelism == 1)
            {
                //Check that the our optimizations are working
                Assert.IsTrue(evaluation.Leaves < 63000, "Too many leaves in search.");
                Assert.IsTrue(evaluation.InternalNodes < 84000, "Too many internal nodes in search.");
            }

            // Too few leaves or internal nodes means that something went wrong
            Assert.IsTrue(evaluation.Leaves > 500, "Too few leaves in search.");
            Assert.IsTrue(evaluation.InternalNodes > 500, "Too few internal nodes in search.");
        }
        public void Search_SearchDepthIsRight(int depth, ParallelismMode parallelismMode)
        {
            var engine = TestUtils.GetBasicSearchEngine(parallelismMode, 8);
            var result = engine.Search(new IncreasingNumberState(8, Player.Max), depth);

            Assert.AreEqual(depth, result.SearchDepth, "Got wring depth");
        }
        public void Search_IsSearchCompletedTrue(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var tree = new UnaryDeterministicTree();

            var engine = TestUtils.GetBasicSearchEngine(parallelismMode, degreeOfParallelism);
            var result = engine.Search(tree.State3, 5);

            Assert.IsTrue(result.IsSearchCompleted, "Search should have been completed");
        }
Beispiel #4
0
 public static SearchEngine GetBasicSearchEngine(
     ParallelismMode parallelismMode = ParallelismMode.FirstLevelOnly, int maxDegreeOfParallelism = 1,
     int parallelismLevel            = 1) =>
 new SearchEngine(CacheMode.NewCache, CacheKeyType.StateOnly)
 {
     SkipEvaluationForFirstNodeSingleNeighbor = false,
     ParallelismMode        = parallelismMode,
     MaxLevelOfParallelism  = parallelismLevel,
     MaxDegreeOfParallelism = maxDegreeOfParallelism
 };
        public void Search_CheckPreventLoopPrunerWorks(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var searchEngine = new SearchEngine(CacheMode.NewCache, CacheKeyType.StateOnly)
            {
                PreventLoops           = true,
                MaxDegreeOfParallelism = degreeOfParallelism,
                ParallelismMode        = parallelismMode,
                SkipEvaluationForFirstNodeSingleNeighbor = false
            };

            searchEngine.Search(new ThrowExceptionAtDepthThreeState(0, Player.Max), 5);
        }
        private void BenchmarkWithDegreeOfParallelism(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            Console.WriteLine("Running with degreeOfParallelism: " + degreeOfParallelism + ", Mode: " + parallelismMode);
            var engine     = Connect4TestUtils.GetSearchEngine(degreeOfParallelism, parallelismMode);
            var startState = new StartState(new Connect4State(Connect4TestUtils.GetEmptyBoard(), Player.Max));

            var results = engine.Search(startState, 7);

            Console.WriteLine("Time: " + results.SearchTime);
            Console.WriteLine("Leaves: " + results.Leaves);
            Console.WriteLine("InternalNodes: " + results.InternalNodes);
        }
        public void Search_DontStopWithUnstableState(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var searchEngine = new SearchEngine(CacheMode.NewCache, CacheKeyType.StateOnly)
            {
                IsUnstableState        = (s, d, l) => s.Evaluate(d, l) < 10,
                MaxDegreeOfParallelism = degreeOfParallelism,
                ParallelismMode        = parallelismMode,
                SkipEvaluationForFirstNodeSingleNeighbor = false,
            };
            var result = searchEngine.Search(new IncreasingNumberState(0, Player.Max), 1);

            Assert.AreEqual(10, result.Evaluation, "Engine seems to have stopped before reaching a stable state");
        }
        private void Benchmark(Player[,] startBoard, int searchDepth,
                               ParallelismMode parallelismMode = ParallelismMode.FirstLevelOnly, int degreeOfParallelism = 1,
                               int levelOfParallelism          = 1)
        {
            Console.WriteLine(GetTestMessage(parallelismMode, degreeOfParallelism, levelOfParallelism));
            var engine     = Connect4TestUtils.GetSearchEngine(degreeOfParallelism, parallelismMode, levelOfParallelism);
            var startState = new Connect4State(startBoard, Player.Max);

            var results = engine.Search(startState, searchDepth);

            Console.WriteLine("Time: " + results.SearchTime);
            Console.WriteLine("Leaves: " + results.Leaves);
            Console.WriteLine("InternalNodes: " + results.InternalNodes);
        }
        public static SearchEngine GetSearchEngine(int degreeOfParallelism, ParallelismMode parallelismMode, CacheMode cacheMode = CacheMode.NewCache)
        {
            var searchEngine = cacheMode == CacheMode.NoCache ? new SearchEngine() : new SearchEngine(cacheMode, CacheKeyType.StateOnly);

            searchEngine.MaxDegreeOfParallelism = degreeOfParallelism;
            searchEngine.DieEarly        = true;
            searchEngine.MinScore        = -1;
            searchEngine.MaxScore        = 1;
            searchEngine.ParallelismMode = parallelismMode;
            searchEngine.SkipEvaluationForFirstNodeSingleNeighbor = false;
            searchEngine.StateDefinesDepth = true;

            return(searchEngine);
        }
        private string GetTestMessage(ParallelismMode parallelismMode, int degreeOfParallelism, int levelOfParallelism)
        {
            var stringBuilder = new StringBuilder("Running Mode " + parallelismMode);

            if (parallelismMode == ParallelismMode.TotalParallelism)
            {
                stringBuilder.Append($" {nameof(degreeOfParallelism)} == {degreeOfParallelism}");
            }
            if (parallelismMode == ParallelismMode.ParallelismByLevel)
            {
                stringBuilder.Append($" {nameof(levelOfParallelism)} == {levelOfParallelism}");
            }

            return(stringBuilder.ToString());
        }
        public void MinCanWinInFiveTurns_MinWins(ParallelismMode parallelismMode, bool dieEarly)
        {
            var board = TestUtils.GetEmptyBoard(7);

            board[2, 4] = CheckerPiece.MinKing;
            board[5, 3] = CheckerPiece.MaxKing;
            var startState = board.ToState(Player.Min);

            var engine = TestUtils.GetCheckersSearchEngine(4, parallelismMode, 1, dieEarly);

            var searchResult = engine.Search(startState, 5);

            Assert.AreEqual(CheckerPiece.MinKing, searchResult.NextMove.GetBoard()[3, 3], "Min should have set a trap by moving to [3, 3] " + Environment.NewLine + searchResult.NextMove);
            Assert.AreEqual(CheckersState.MIN_WIN, searchResult.Evaluation, "Min should have won!");
        }
        public void NewGame_CheckCancellationToken(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var startState = new Connect4State(Connect4TestUtils.GetEmptyBoard(), Player.Max);

            var engine             = Connect4TestUtils.GetSearchEngine(degreeOfParallelism, parallelismMode);
            var cancellationSource = new CancellationTokenSource();
            var searchTask         = engine.SearchAsync(startState, 20, cancellationSource.Token);

            Thread.Sleep(500);
            cancellationSource.Cancel();
            Thread.Sleep(500);

            Assert.IsTrue(searchTask.IsCompleted, "Search should have complated by now");
            var t = searchTask.Result; // Check that we can get a result even if the search was terminated
        }
        public static SearchEngine GetSearchEngine(int maxDegreeOfParallelism, ParallelismMode parallelismMode, int levelOfParallelism = 1, CacheMode cacheMode = CacheMode.NewCache)
        {
            var engine = cacheMode == CacheMode.NoCache ? new SearchEngine() : new SearchEngine(cacheMode, CacheKeyType.StateOnly);

            engine.MaxDegreeOfParallelism = maxDegreeOfParallelism;
            engine.MaxLevelOfParallelism  = levelOfParallelism;
            engine.DieEarly        = true;
            engine.MinScore        = BoardEvaluator.MinEvaluation;
            engine.MaxScore        = BoardEvaluator.MaxEvaluation;
            engine.ParallelismMode = parallelismMode;
            engine.SkipEvaluationForFirstNodeSingleNeighbor = false;
            engine.StateDefinesDepth = true;

            return(engine);
        }
        public void MaxCanWinInThreeTurns_MaxWins(ParallelismMode parallelismMode, bool dieEarly)
        {
            var board = TestUtils.GetEmptyBoard(5);

            board[0, 0] = CheckerPiece.MaxChecker;
            board[1, 3] = CheckerPiece.MaxChecker;
            board[3, 3] = CheckerPiece.MinChecker;
            var startState = board.ToState(Player.Max);

            var engine = TestUtils.GetCheckersSearchEngine(4, parallelismMode, 1, dieEarly);

            var searchResult = engine.Search(startState, 3);

            Assert.AreEqual(CheckerPiece.MaxChecker, searchResult.NextMove.GetBoard()[2, 2], "Max should have set a trap by moving to [2, 2] " + Environment.NewLine + searchResult.NextMove);
            Assert.AreEqual(CheckersState.MAX_WIN, searchResult.Evaluation, "Max should have won!");
        }
        public void Search_RowOfMixedTurns_FindBest(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var tree = new DeterministicTree2();

            A.CallTo(() => tree.ChildState1.Turn).Returns(Player.Max);
            tree.EndState1.SetEvaluationTo(5);
            tree.EndState2.SetEvaluationTo(6);
            tree.EndState3.SetEvaluationTo(4);
            tree.EndState4.SetEvaluationTo(7);

            var engine = TestUtils.GetBasicSearchEngine(parallelismMode, degreeOfParallelism);
            var result = engine.Search(tree.RootState, 5);

            Assert.AreEqual(tree.EndState2, result.StateSequence.Last());
            Assert.AreEqual(6, result.Evaluation);
        }
        private void BenchmarkWithDegreeOfParallelism(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            Console.WriteLine("Running with degreeOfParallelism: " + degreeOfParallelism + ", Mode: " + parallelismMode);
            var engine     = TicTacToeBassicTests.GetSearchEngine(degreeOfParallelism, parallelismMode);
            var startState = new TicTacToeState(new[, ]
            {
                { Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty },
            }, Player.Max);

            var results = engine.Search(startState, 10);

            Console.WriteLine("Time: " + results.SearchTime);
            Console.WriteLine("Leaves: " + results.Leaves);
            Console.WriteLine("InternalNodes: " + results.InternalNodes);
        }
        public void CancelSearch_ReturnBestResultSoFar(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var tree = new EndlessTree();

            tree.ChildState1.SetEvaluationTo(1);
            tree.ChildState2.SetEvaluationTo(3);
            var cancellationSource = new CancellationTokenSource(20);
            var engine             = TestUtils.GetBasicSearchEngine(parallelismMode, degreeOfParallelism);

            var result = engine.SearchAsync(tree.RootState, int.MaxValue, cancellationSource.Token).Result;

            Assert.AreEqual(3, result.Evaluation, "Didn't get a good enough state");
            if (parallelismMode == ParallelismMode.NonParallelism)
            {
                A.CallTo(() => tree.ChildState2.GetNeighbors()).MustNotHaveHappened();
            }
        }
        private void RunThreadSaftyTest(int degreeOfParallelism, ParallelismMode parallelismMode, Action <SearchEngine> searchMethod)
        {
            var engine = TicTacToeBassicTests.GetSearchEngine(degreeOfParallelism, parallelismMode);

            var tasks = new Task[TEST_RUNS];

            for (int i = 0; i < TEST_RUNS; i++)
            {
                tasks[i] = Task.Run(() => searchMethod(engine));
            }

            var allTaskFinished = true;

            foreach (var task in tasks)
            {
                allTaskFinished = allTaskFinished && task.Wait(TimeSpan.FromSeconds(30));
            }

            Assert.IsTrue(allTaskFinished, "Not all tasks finished");
        }
        public void Search_PrunerTest(int maxDegreeOfParallelism, ParallelismMode parallelismMode)
        {
            var tree   = new DeterministicTree();
            var pruner = A.Fake <IPruner>();

            A.CallTo(() => pruner.ShouldPrune(A <IState> ._, A <int> ._, A <List <IState> > ._))
            .ReturnsLazily((IState s, int d, List <IState> l) => s.Equals(tree.ChildState2));
            var engine = TestUtils.GetBasicSearchEngine(parallelismMode, maxDegreeOfParallelism);

            engine.AddPruner(pruner);

            tree.EndState1.SetEvaluationTo(1);
            tree.EndState2.SetEvaluationTo(2);
            tree.EndState3.SetEvaluationTo(2);
            tree.ChildState2.SetEvaluationTo(-2);

            var result = engine.Search(tree.RootState, 4);

            Assert.AreEqual(1, result.Evaluation, $"We should have pruned away {nameof(tree.ChildState2)}");
        }
        public void Search_DieEarllyOptionWorks(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var tree = new DeterministicTree3();

            tree.EndState1.SetEvaluationTo(10);
            tree.EndState2.SetEvaluationTo(15);
            tree.EndState3.SetEvaluationTo(0);

            var searchEngine = new SearchEngine(CacheMode.NewCache, CacheKeyType.StateOnly)
            {
                DieEarly = true,
                MaxScore = 5,
                MinScore = 5,
                MaxDegreeOfParallelism = degreeOfParallelism,
                ParallelismMode        = parallelismMode,
                SkipEvaluationForFirstNodeSingleNeighbor = false,
            };
            var evaluation = searchEngine.Search(tree.RootState, 2);

            Assert.AreEqual(tree.EndState1, evaluation.StateSequence.Last(), "Should have ended with" + nameof(tree.EndState1));
        }
        public void NewGame_NoOneCanWin(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var startState = new Connect4State(Connect4TestUtils.GetEmptyBoard(), Player.Max);

            var engine     = Connect4TestUtils.GetSearchEngine(degreeOfParallelism, parallelismMode);
            var evaluation = engine.Search(startState, 7);

            Assert.IsFalse(BoardEvaluator.IsWin(((Connect4State)evaluation.StateSequence.Last()).Board, Player.Max));
            Assert.IsFalse(evaluation.FullTreeSearchedOrPruned);
            Assert.IsFalse(evaluation.AllChildrenAreDeadEnds);

            if (degreeOfParallelism == 1)
            {
                //Check that the our optimizations are working
                Assert.IsTrue(evaluation.Leaves < 26000, "Too many leaves in search. Leaves = " + evaluation.Leaves);
                Assert.IsTrue(evaluation.InternalNodes < 10000,
                              "Too many intarnal nodes in search. Nodes = " + evaluation.InternalNodes);
            }
            // Too few leaves or internal nodes means that something went wrong
            Assert.IsTrue(evaluation.Leaves > 1000, "Too few leaves in search. Leaves = " + evaluation.Leaves);
            Assert.IsTrue(evaluation.InternalNodes > 1000, "Too few intarnal nodes in search. Nodes = " + evaluation.InternalNodes);
        }
        public void TwoStepsAwayFromMaxWinning__MinsTurn_DontLetMinMax(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var startState = new Connect4State(new[, ]
            {
                { Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
            }, Player.Min);

            var engine   = Connect4TestUtils.GetSearchEngine(degreeOfParallelism, parallelismMode);
            var newState = (Connect4State)engine.Search(startState, 2).NextMove;

            Assert.AreEqual(Player.Min, newState.Board[3, 1], "Min didn't block Max's win");
        }
        public void MaxCanWinNextMoveOrInThree_MaxWinsNextMove(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var startState = new Connect4State(new[, ]
            {
                { Player.Empty, Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
            }, Player.Max);

            var engine = new SearchEngine(CacheMode.NewCache, CacheKeyType.StateOnly)
            {
                FavorShortPaths        = true,
                MaxDegreeOfParallelism = degreeOfParallelism,
                ParallelismMode        = parallelismMode,
                SkipEvaluationForFirstNodeSingleNeighbor = false,
                StateDefinesDepth = true
            };
            var evaluation = engine.Search(startState, 5);

            Assert.IsTrue(evaluation.StateSequence.Count == 1, "Max should have won in one move");
            Assert.AreEqual(Player.Max, ((Connect4State)evaluation.NextMove).Board[3, 2], "Max didn't win");
        }
        public void Search_TaskCanceld_DontContinueSearching(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var tree = new UnaryDeterministicTree();
            var cancellationSource = new CancellationTokenSource();

            A.CallTo(() => tree.RootState.GetNeighbors()).ReturnsLazily(() =>
            {
                cancellationSource.Cancel();
                return(new List <IDeterministicState> {
                    tree.State2
                });
            });

            tree.RootState.SetEvaluationTo(1);
            tree.State2.SetEvaluationTo(2);
            tree.EndState.SetEvaluationTo(2);

            var searchEngine = TestUtils.GetBasicSearchEngine(parallelismMode, degreeOfParallelism);
            var result       = searchEngine.Search(tree.RootState, 5, cancellationSource.Token);

            Assert.AreEqual(1, result.Evaluation);
            Assert.AreEqual(0, result.StateSequence.Count, "We shouldn't have gotten to past " + nameof(tree.RootState));
            Assert.IsFalse(result.IsSearchCompleted, "The search shouldn't have been completed");
        }
        public void MaxCanWinInFourMovesOrTwo_MinBlocksNearWin(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var startState = new Connect4State(new[, ]
            {
                { Player.Empty, Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Max, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
            }, Player.Min);

            var engine = new SearchEngine(CacheMode.NewCache, CacheKeyType.StateOnly)
            {
                FavorShortPaths        = true,
                MaxDegreeOfParallelism = degreeOfParallelism,
                ParallelismMode        = parallelismMode,
                SkipEvaluationForFirstNodeSingleNeighbor = false
            };
            var evaluation = engine.Search(startState, 5);

            Assert.IsTrue(evaluation.StateSequence.Count > 2, "Min should have blocked the near win");
            Assert.AreEqual(Player.Min, ((Connect4State)evaluation.NextMove).Board[3, 2], "Min didn't block Max's win");
        }
        public void Search_WinMovesTwoAndThreeStepsAway_FindTheNearerOne(int degreeOfParallelism, ParallelismMode parallelismMode, bool dieEarly)
        {
            var tree = new UnevenDeterministicTree();

            tree.EndState1.SetEvaluationTo(15);
            tree.EndState2.SetEvaluationTo(11);
            tree.EndState3.SetEvaluationTo(18);

            var engine = new SearchEngine(CacheMode.NewCache, CacheKeyType.StateOnly)
            {
                MaxDegreeOfParallelism = degreeOfParallelism,
                FavorShortPaths        = true,
                DieEarly        = dieEarly,
                MaxScore        = 10,
                ParallelismMode = parallelismMode,
                SkipEvaluationForFirstNodeSingleNeighbor = false,
            };
            var result = engine.Search(tree.RootState, 5);

            Assert.AreEqual(tree.EndState1, result.StateSequence.Last(), nameof(tree.EndState1) + " should have been good enough");
        }
        public void Search_CheckThatRecordPassThroughStatesIsWorking(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var tree = new DeterministicTree();

            A.CallTo(() => tree.EndState1.Evaluate(A <int> ._, A <List <IState> > .That.IsEmpty()))
            .Throws(new Exception("passedStats list should have been empty"));
            A.CallTo(() => tree.EndState1.Evaluate(A <int> ._, A <List <IState> > ._))
            .Invokes((int i, List <IState> l) =>
            {
                Assert.AreEqual(1, l.Count, "passThroughStates should only have one node (state1)");
                Assert.IsTrue(l.Contains(tree.ChildState1), "passThroughStates should contain state1");
            });

            var searchEngine = TestUtils.GetBasicSearchEngine(parallelismMode, degreeOfParallelism);

            searchEngine.Search(tree.ChildState1, 5);
        }
        public void FiveStepsAwayFromMaxWinning_MaxTurn_MaxWin(int degreeOfParallelism, ParallelismMode parallelismMode, CacheMode cacheMode)
        {
            var startState = Connect4TestUtils.GetMaxFiveMovesAwayFromWinningState();

            var engine     = Connect4TestUtils.GetSearchEngine(degreeOfParallelism, parallelismMode, 1, CacheMode.ReuseCache);
            var evaluation = engine.Search(startState, 5);

            Assert.AreEqual(BoardEvaluator.MaxEvaluation, evaluation.Evaluation);
            Assert.IsTrue(BoardEvaluator.IsWin(((Connect4State)evaluation.StateSequence.Last()).Board, Player.Max), "Should have found a wining state");
            Assert.IsTrue(evaluation.AllChildrenAreDeadEnds, "All children should be dead ends");
        }
        public void Search_MaxHasTwoTurnsInARow_FindBestMove(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var tree = new DeterministicTree();

            A.CallTo(() => tree.ChildState2.Turn).Returns(Player.Max);
            tree.EndState2.SetEvaluationTo(2);
            tree.EndState3.SetEvaluationTo(3);

            var engine = TestUtils.GetBasicSearchEngine(parallelismMode, degreeOfParallelism);
            var result = engine.Search(tree.ChildState2, 5);

            Assert.AreEqual(tree.EndState3, result.NextMove, "Actually found " + result.NextMove);
        }
        public void ThreeStepsAwayFromMaxWinning_MaxTurn_MaxWin(int degreeOfParallelism, ParallelismMode parallelismMode)
        {
            var startState = new Connect4State(new[, ]
            {
                { Player.Empty, Player.Empty, Player.Empty, Player.Max, Player.Max, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
                { Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty, Player.Empty },
            }, Player.Max);

            var engine     = Connect4TestUtils.GetSearchEngine(degreeOfParallelism, parallelismMode);
            var evaluation = engine.Search(startState, 3);

            Assert.IsTrue(BoardEvaluator.IsWin(((Connect4State)evaluation.StateSequence.Last()).Board, Player.Max), "Should have found a wining state");
        }