private void RunNegaMaxAtAParticularDepth()
        {
            SearchNode newRootNode = new SearchNode(Coordinator.CurrentGameState);

            GameState mutableGameState = null;

            if (gameStateGenerationMethod == NegaMaxGameStateGenerationMethod.MutableGameStateWithUndo)
            {
                mutableGameState = Coordinator.CurrentGameState.Clone();
            }

            double evaluation = Negamax(newRootNode, mutableGameState);

            if (SolverState == SolverState.Stopping)
            {
                newRootNode.EvaluationStatus = EvaluationStatus.Stopped;
            }
            else
            {
                RootNode           = newRootNode;
                LastDepthCompleted = CurrentDepth;

                // A solution was found at this depth. Choose a best solution:
                RootNode.Evaluation = evaluation;

                // Always choose the first child node matching the best evaluation, as other nodes might be a result of pruning:
                SearchNode chosenChildNode = RootNode.ChildNodes.Where(
                    childNode => childNode.EvaluationStatus == EvaluationStatus.Evaluated && childNode.Evaluation == evaluation
                    ).FirstOrDefault();

                if (chosenChildNode != null)
                {
                    GameState chosenGameState = chosenChildNode.GameState;
#if DEBUG
                    // Perform algorithms on chosen node, since search nodes no longer do this (except at leaf nodes):
                    Dijkstra.Perform(chosenGameState);
                    BiconnectedComponentsAlgorithm bcAlg = new BiconnectedComponentsAlgorithm();
                    bcAlg.Calculate(chosenGameState, ReachableCellsThenClosestCellsThenDegreesOfClosestCellsEvaluator.Instance);
#endif
                    Coordinator.SetBestMoveSoFar(chosenGameState);
                }
                else
                {
                    Debug.WriteLine("NegaMax found no best child");
                }

#if DEBUG
                int numberOfEvaluationsAtThisDepth = numberOfEvaluationsByDepth.ContainsKey(CurrentDepth) ? numberOfEvaluationsByDepth[CurrentDepth] : 0;
                System.Diagnostics.Debug.WriteLine("*** NegaMax performed {0} evaluations at depth {1}", numberOfEvaluationsAtThisDepth, CurrentDepth);
#endif
            }
        }
        private double Negamax(SearchNode searchNode, GameState gameState, int depth = 0, double alpha = double.NegativeInfinity,
                               double beta = double.PositiveInfinity)
        {
            if (gameStateGenerationMethod == NegaMaxGameStateGenerationMethod.GameStatePerSearchNode)
            {
                gameState = searchNode.GameState;
            }

            int multiplier;

            if (searchNode.ParentNode == null)
            {
                multiplier = gameState.PlayerToMoveNext == PlayerType.You ? 1 : -1;
            }
            else
            {
                // A more efficient way, since it doesn't cause game states to be re-generated:
                multiplier = searchNode.Move.PlayerType == PlayerType.Opponent ? 1 : -1;
            }

            if (depth >= CurrentDepth)
            {
                // Perform the algorithms on the game state, before running the evaluation:
                Dijkstra.Perform(gameState);
                BiconnectedComponentsAlgorithm bcAlg = new BiconnectedComponentsAlgorithm();
                bcAlg.Calculate(gameState, ReachableCellsThenClosestCellsThenDegreesOfClosestCellsEvaluator.Instance);

                Evaluate(searchNode, gameState);

                int evaluationsAtThisDepth = numberOfEvaluationsByDepth.ContainsKey(depth) ? numberOfEvaluationsByDepth[depth] : 0;
                numberOfEvaluationsByDepth[depth] = evaluationsAtThisDepth + 1;

                return(multiplier * searchNode.Evaluation);
            }
            else
            {
                searchNode.Expand(gameState);

                if (SolverState == SolverState.Stopping)
                {
                    return(0.0);  // Calling code will check solver state and ignore the result
                }

                if (!searchNode.ChildNodes.Any())
                {
                    /* Game is terminal: */
                    if (gameState.IsGameOver)
                    {
                        if (gameState.PlayerToMoveNext == PlayerType.You)
                        {
                            searchNode.Evaluation = double.NegativeInfinity;
                        }
                        else
                        {
                            searchNode.Evaluation = double.PositiveInfinity;
                        }
                    }
                    else
                    {
                        throw new ApplicationException("Game state should be over if there are no child search nodes!");
                    }
                    return(multiplier * searchNode.Evaluation);
                }
                else
                {
                    double max     = double.NegativeInfinity;
                    bool   pruning = false;

                    foreach (SearchNode childNode in searchNode.ChildNodes)
                    {
                        if (SolverState == SolverState.Stopping)
                        {
                            childNode.EvaluationStatus = EvaluationStatus.Stopped;
                        }
                        else
                        if (pruning)
                        {
                            childNode.EvaluationStatus = EvaluationStatus.Pruned;
                        }
                        else
                        {
                            if (gameStateGenerationMethod == NegaMaxGameStateGenerationMethod.MutableGameStateWithUndo)
                            {
                                gameState.MakeMove(childNode.Move, false, false);      // Don't perform algorithms
                            }
                            try
                            {
                                double evaluation = -Negamax(childNode, gameState, depth + 1, -beta, -alpha);

                                if (SolverState == SolverState.Stopping)
                                {
                                    childNode.EvaluationStatus = EvaluationStatus.Stopped;
                                    return(0.0);
                                }
                                else
                                {
                                    childNode.Evaluation = evaluation;

                                    // Alpha-beta pruning:
                                    if (evaluation > max)
                                    {
                                        max = evaluation;
                                    }
                                    if (evaluation > alpha)
                                    {
                                        alpha = evaluation;
                                    }
                                    if (alpha >= beta)
                                    {
                                        pruning = true;
                                    }
                                }
                            }
                            finally
                            {
                                if (gameStateGenerationMethod == NegaMaxGameStateGenerationMethod.MutableGameStateWithUndo)
                                {
                                    gameState.UndoLastMove();
                                }
                            }
                        }
                    }
                    if (pruning)
                    {
                        searchNode.EvaluationStatus = EvaluationStatus.Pruned;
                        return(alpha);
                    }
                    return(max);
                }
            }
        }