Exemplo n.º 1
0
        public SolverAction GetNextMove()
        {
            LivingLocation bestLiving = getBestLocation(currentNode);

            if (bestLiving == null)
            {
                return(null);
            }

            SolverTile loc = this.locations[bestLiving.index];

            //solver.display("first best move is " + loc.display());
            double prob = 1 - bestLiving.mineCount / currentNode.GetSolutionSize();

            while (!loc.IsHidden())
            {
                int value = loc.GetValue();

                currentNode = bestLiving.children[value];
                bestLiving  = getBestLocation(currentNode);
                if (bestLiving == null)
                {
                    return(null);
                }
                prob = 1 - ((double)bestLiving.mineCount) / currentNode.GetSolutionSize();

                loc = this.locations[bestLiving.index];
            }

            solver.Write("mines = " + bestLiving.mineCount + " solutions = " + currentNode.GetSolutionSize());
            for (int i = 0; i < bestLiving.children.Length; i++)
            {
                if (bestLiving.children[i] == null)
                {
                    //solver.display("Value of " + i + " is not possible");
                    continue; //ignore this node but continue the loop
                }

                String probText;
                if (bestLiving.children[i].bestLiving == null)
                {
                    probText = (100 / (bestLiving.children[i].GetSolutionSize())) + "%";
                }
                else
                {
                    probText = bestLiving.children[i].GetProbability() * 100 + "%";
                }
                solver.Write("Value of " + i + " leaves " + bestLiving.children[i].GetSolutionSize() + " solutions and winning probability " + probText + " (work size " + bestLiving.children[i].work + ")");
            }

            //String text = " (solve " + (currentNode.GetProbability() * 100) + "%)";
            SolverAction action = new SolverAction(loc, ActionType.Clear, 0.5);

            expectedMove = loc;

            return(action);
        }
Exemplo n.º 2
0
        public static SolverActionHeader FindActions(SolverInfo information)
        {
            long time1 = DateTime.Now.Ticks;

            List <SolverAction> actions;

            if (information.GetGameStatus() == GameStatus.Lost)
            {
                information.Write("Game has been lost already - no valid moves");
                return(new SolverActionHeader());
            }
            else if (information.GetGameStatus() == GameStatus.Won)
            {
                information.Write("Game has been won already - no valid moves");
                return(new SolverActionHeader());
            }
            else if (information.GetGameStatus() == GameStatus.NotStarted)
            {
                information.Write("Game has not yet started - currently unable to provide help");
                return(new SolverActionHeader());
            }

            // are we walking down a brute force deep analysis tree?
            BruteForceAnalysis lastBfa = information.GetBruteForceAnalysis();

            if (lastBfa != null)     // yes
            {
                SolverTile expectedMove = lastBfa.GetExpectedMove();
                if (expectedMove != null && expectedMove.IsHidden())      // the expected move wasn't played !
                {
                    information.Write("The expected Brute Force Analysis move " + expectedMove.AsText() + " wasn't played");
                    information.SetBruteForceAnalysis(null);
                }
                else
                {
                    SolverAction move = lastBfa.GetNextMove();
                    if (move != null)
                    {
                        information.Write("Next Brute Force Deep Analysis move is " + move.AsText());
                        actions = new List <SolverAction>(1);
                        actions.Add(move);
                        return(BuildActionHeader(information, actions));
                    }
                }
            }


            actions = FindTrivialActions(information);

            long time2 = DateTime.Now.Ticks;

            information.Write("Finding Trivial Actions took " + (time2 - time1) + " ticks");

            // if we have some actions from the trivial search use them
            if (actions.Count > 0)
            {
                return(BuildActionHeader(information, actions));
            }


            if (information.GetTilesLeft() == information.GetDeadTiles().Count)
            {
                information.Write("All tiles remaining are dead");

                // when all the tiles are dead return the first one (they are all equally good or bad)
                foreach (SolverTile guess in information.GetDeadTiles())
                {
                    actions.Add(new SolverAction(guess, ActionType.Clear, 0.5));  // not all 0.5 safe though !!
                    return(BuildActionHeader(information, actions));
                }
            }

            // get all the witnessed tiles

            List <SolverTile>    witnesses    = new List <SolverTile>(information.GetWitnesses());
            HashSet <SolverTile> witnessedSet = new HashSet <SolverTile>();

            foreach (SolverTile witness in witnesses)
            {
                foreach (SolverTile adjTile in information.GetAdjacentTiles(witness))
                {
                    if (adjTile.IsHidden())
                    {
                        witnessedSet.Add(adjTile);
                    }
                }
            }

            List <SolverTile> witnessed = new List <SolverTile>(witnessedSet);
            int livingTilesLeft         = information.GetTilesLeft();
            int livingMinesLeft         = information.GetMinesLeft();
            int offEdgeTilesLeft        = information.GetTilesLeft() - witnessed.Count;

            //information.Write("Excluded tiles " + information.GetExcludedTiles().Count + " out of " + information.GetTilesLeft());
            //information.Write("Excluded witnesses " + information.GetExcludedWitnesses().Count);
            //information.Write("Excluded mines " + information.GetExcludedMineCount() + " out of " + information.GetMinesLeft());

            // if there are no living mines but some living tiles then the living tiles can be cleared
            if (livingMinesLeft == 0 && livingTilesLeft > 0)
            {
                information.Write("There are no living mines left - all living tiles must be clearable");

                for (int x = 0; x < information.description.width; x++)
                {
                    for (int y = 0; y < information.description.height; y++)
                    {
                        SolverTile tile = information.GetTile(x, y);
                        if (tile.IsHidden() && !tile.IsMine())
                        {
                            actions.Add(new SolverAction(tile, ActionType.Clear, 1));
                        }
                    }
                }
                return(BuildActionHeader(information, actions));
            }


            SolutionCounter solutionCounter = new SolutionCounter(information, witnesses, witnessed, livingTilesLeft, livingMinesLeft);

            solutionCounter.Process();
            information.Write("Solution counter says " + solutionCounter.GetSolutionCount() + " solutions and " + solutionCounter.getClearCount() + " clears");


            //ProbabilityEngine pe = new ProbabilityEngine(information, witnesses, witnessed, information.GetTilesLeft(), information.GetMinesLeft());
            ProbabilityEngine pe = new ProbabilityEngine(information, witnesses, witnessed, livingTilesLeft, livingMinesLeft);

            pe.Process();

            long time3 = DateTime.Now.Ticks;

            information.Write("Probability Engine took " + (time3 - time2) + " ticks");

            // have we found any local clears which we can use
            List <SolverTile> localClears = pe.GetLocalClears();

            if (localClears.Count > 0)
            {
                foreach (SolverTile tile in localClears)     // place each local clear into an action
                {
                    actions.Add(new SolverAction(tile, ActionType.Clear, 1));
                }
                information.Write("The probability engine has found " + localClears.Count + " safe Local Clears");

                // add any mines to it
                List <SolverTile> minesFound = pe.GetMinesFound();
                foreach (SolverTile tile in minesFound)     // place each mine found into an action
                {
                    information.MineFound(tile);
                    actions.Add(new SolverAction(tile, ActionType.Flag, 0));
                }
                information.Write("The probability engine has found " + minesFound.Count + " mines");


                return(BuildActionHeader(information, actions));
            }

            if (pe.GetBestEdgeProbability() == 1)
            {
                actions = pe.GetBestCandidates(1);
                information.Write("The probability engine has found " + actions.Count + " safe Clears");

                // add any mines to it
                List <SolverTile> minesFound = pe.GetMinesFound();
                foreach (SolverTile tile in minesFound)     // place each mine found into an action
                {
                    information.MineFound(tile);
                    actions.Add(new SolverAction(tile, ActionType.Flag, 0));
                }
                information.Write("The probability engine has found " + minesFound.Count + " mines");

                return(BuildActionHeader(information, actions));
            }

            // dead edge (all tiles on the edge are dead and there is only one mine count value)
            if (pe.GetDeadEdge().Count != 0)
            {
                SolverTile tile = pe.GetDeadEdge()[0];
                information.Write("Probability engine has found a dead area, guessing at " + tile.AsText());
                double probability = Combination.DivideBigIntegerToDouble(BigInteger.One, pe.GetDeadEdgeSolutionCount(), 6);
                actions.Add(new SolverAction(tile, ActionType.Clear, probability));
                return(BuildActionHeader(information, actions));
            }

            // Isolated edges found (all adjacent tiles are also on the edge and there is only one mine count value)
            if (pe.GetOutcome() == ProbabilityEngine.Outcome.ISOLATED_EDGE)
            {
                information.Write("Probability engine has found an isolated area");

                Cruncher cruncher = pe.getIsolatedEdgeCruncher();

                // determine all possible solutions
                cruncher.Crunch();

                // determine best way to solver them
                BruteForceAnalysis bfa = cruncher.GetBruteForceAnalysis();
                bfa.process();

                // if after trying to process the data we can't complete then abandon it
                if (!bfa.IsComplete())
                {
                    information.Write("Abandoned the Brute Force Analysis after " + bfa.GetNodeCount() + " steps");
                    bfa = null;
                }
                else     // otherwise try and get the best long term move
                {
                    information.Write("Built probability tree from " + bfa.GetSolutionCount() + " solutions in " + bfa.GetNodeCount() + " steps");
                    SolverAction move = bfa.GetNextMove();
                    if (move != null)
                    {
                        information.SetBruteForceAnalysis(bfa);  // save the details so we can walk the tree
                        information.Write("Brute Force Analysis: " + move.AsText());
                        actions.Add(move);
                        return(BuildActionHeader(information, actions));
                    }
                    else if (bfa.GetAllDead())
                    {
                        SolverTile tile = cruncher.getTiles()[0];
                        information.Write("Brute Force Analysis has decided all tiles are dead on the Isolated Edge, guessing at " + tile.AsText());
                        double probability = Combination.DivideBigIntegerToDouble(BigInteger.One, bfa.GetSolutionCount(), 6);
                        actions.Add(new SolverAction(tile, ActionType.Clear, probability));
                        return(BuildActionHeader(information, actions));
                    }
                    else
                    {
                        information.Write("Brute Force Analysis: no move found!");
                    }
                }
            }


            // after this point we know the probability engine didn't return any certain clears. But there are still some special cases when everything off edge is either clear or a mine

            // If there are tiles off the edge and they are definitely safe then clear them all, or mines then flag them
            if (offEdgeTilesLeft > 0 && (pe.GetOffEdgeProbability() == 1 || pe.GetOffEdgeProbability() == 0))
            {
                information.Write("Looking for the certain moves off the edge found by the probability engine");
                bool clear;
                if (pe.GetOffEdgeProbability() == 1)
                {
                    information.Write("All off edge tiles are clear");
                    clear = true;
                }
                else
                {
                    information.Write("All off edge tiles are mines");
                    clear = false;
                }

                for (int x = 0; x < information.description.width; x++)
                {
                    for (int y = 0; y < information.description.height; y++)
                    {
                        SolverTile tile = information.GetTile(x, y);

                        if (tile.IsHidden() && !witnessedSet.Contains(tile))
                        {
                            if (clear)
                            {
                                information.Write(tile.AsText() + " is clear");
                                actions.Add(new SolverAction(tile, ActionType.Clear, 1));
                            }
                            else
                            {
                                information.Write(tile.AsText() + " is mine");
                                information.MineFound(tile);
                                actions.Add(new SolverAction(tile, ActionType.Flag, 0));
                            }
                        }
                    }
                }

                if (actions.Count > 0)
                {
                    // add any mines to it
                    List <SolverTile> minesFound = pe.GetMinesFound();
                    foreach (SolverTile tile in minesFound)     // place each mine found into an action
                    {
                        information.MineFound(tile);
                        actions.Add(new SolverAction(tile, ActionType.Flag, 0));
                    }
                    information.Write("The probability engine has found " + minesFound.Count + " mines");

                    return(BuildActionHeader(information, actions));
                }
                else
                {
                    Console.WriteLine("No Actions found!");
                }
            }


            // these are guesses
            List <SolverAction> guesses = pe.GetBestCandidates(1);

            // we know the Probability Engine completed so hold onto the information for the gui
            information.SetProbabilityEngine(pe);

            // if there aren't many possible solutions then do a brute force search
            if (pe.GetSolutionCount() <= MAX_BFDA_SOLUTIONS)
            {
                //if (minesFound.Count > 0) {
                //    information.Write("Not doing a brute force analysis because we found some mines using the probability engine");
                //    return BuildActionHeader(information, actions);
                //}

                // find a set of independent witnesses we can use as the base of the iteration
                pe.GenerateIndependentWitnesses();

                BigInteger expectedIterations = pe.GetIndependentIterations() * SolverMain.Calculate(livingMinesLeft - pe.GetIndependentMines(), livingTilesLeft - pe.GetIndependentTiles());

                information.Write("Expected Brute Force iterations " + expectedIterations);

                // do the brute force if there are not too many iterations
                if (expectedIterations < MAX_BRUTE_FORCE_ITERATIONS)
                {
                    List <SolverTile> allCoveredTiles = new List <SolverTile>();

                    for (int x = 0; x < information.description.width; x++)
                    {
                        for (int y = 0; y < information.description.height; y++)
                        {
                            SolverTile tile = information.GetTile(x, y);
                            if (tile.IsHidden() && !tile.IsMine())
                            {
                                allCoveredTiles.Add(tile);
                            }
                        }
                    }

                    WitnessWebIterator[] iterators = BuildParallelIterators(information, pe, allCoveredTiles, expectedIterations);
                    BruteForceAnalysis   bfa       = Cruncher.PerformBruteForce(information, iterators, pe.GetDependentWitnesses());

                    bfa.process();

                    // if after trying to process the data we can't complete then abandon it
                    if (!bfa.IsComplete())
                    {
                        information.Write("Abandoned the Brute Force Analysis after " + bfa.GetNodeCount() + " steps");
                        bfa = null;
                    }
                    else     // otherwise try and get the best long term move
                    {
                        information.Write("Built probability tree from " + bfa.GetSolutionCount() + " solutions in " + bfa.GetNodeCount() + " steps");
                        SolverAction move = bfa.GetNextMove();
                        if (move != null)
                        {
                            information.SetBruteForceAnalysis(bfa);  // save the details so we can walk the tree
                            information.Write("Brute Force Analysis: " + move.AsText());
                            actions.Add(move);
                            return(BuildActionHeader(information, actions));
                        }
                        else
                        {
                            information.Write("Brute Force Analysis: no move found!");
                        }
                    }
                }
                else
                {
                    information.Write("Too many iterations, Brute Force not atempted");
                }
            }


            if (guesses.Count == 0)    // find an off edge guess
            {
                if (offEdgeTilesLeft > 0)
                {
                    information.Write("getting an off edge guess");
                    SolverTile   tile   = OffEdgeGuess(information, witnessedSet);
                    SolverAction action = new SolverAction(tile, ActionType.Clear, pe.GetOffEdgeProbability());
                    information.Write(action.AsText());
                    actions.Add(action);
                }
                else
                {
                    if (information.GetDeadTiles().Count > 0)
                    {
                        information.Write("Finding a dead tile to guess");
                        SolverTile tile = null;
                        foreach (SolverTile deadTile in information.GetDeadTiles())     // get the first dead tile
                        {
                            tile = deadTile;
                            break;
                        }
                        SolverAction action = new SolverAction(tile, ActionType.Clear, 0.5);  // probability may not be 0.5
                        actions.Add(action);
                    }
                }
            }
            else if (guesses.Count > 1)      // if we have more than 1 guess then do some tie break logic
            {
                information.Write("Doing a tie break for " + guesses.Count + " actions");
                actions = DoTieBreak(guesses);
            }
            else
            {
                actions = guesses;
            }

            return(BuildActionHeader(information, actions));
        }