Beispiel #1
0
        // returns all the indices adjacent to this index
        public List <SolverTile> GetAdjacentTiles(SolverTile tile)
        {
            // have we already calculated the adjacent tiles
            List <SolverTile> adjTiles = tile.GetAdjacentTiles();

            if (adjTiles != null)
            {
                return(adjTiles);
            }

            adjTiles = new List <SolverTile>();

            int first_row = Math.Max(0, tile.y - 1);
            int last_row  = Math.Min(description.height - 1, tile.y + 1);

            int first_col = Math.Max(0, tile.x - 1);
            int last_col  = Math.Min(description.width - 1, tile.x + 1);

            for (int r = first_row; r <= last_row; r++)
            {
                for (int c = first_col; c <= last_col; c++)
                {
                    if (r != tile.y || c != tile.x)
                    {
                        adjTiles.Add(tiles[c, r]);
                    }
                }
            }

            // remember them for next time
            tile.SetAdjacentTiles(adjTiles);

            return(adjTiles);
        }
Beispiel #2
0
        // sets the tile as a mine, stores it for future use and returns true if it is currently flagged
        public bool MineFound(SolverTile tile)
        {
            // if this is already known to be mine then nothing to do
            if (tile.IsMine())
            {
                return(tile.IsFlagged());
            }

            tilesLeft--;

            tile.SetAsMine(key);

            knownMines.Add(tile);

            if (tile.IsDead())
            {
                deadTiles.Remove(tile);  // remove the tile if it was on the dead list
            }
            //if (tile.IsExcluded()) {
            //    excludedTiles.Remove(tile); // remove the tile if it was excluded
            //   excludedMinesCount--;
            //}

            return(tile.IsFlagged());
        }
Beispiel #3
0
        // returns true if the tile provided is adjacent to this tile
        public bool IsAdjacent(SolverTile tile)
        {
            var dx = Math.Abs(this.x - tile.x);

            if (dx > 1)
            {
                return(false);
            }

            var dy = Math.Abs(this.y - tile.y);

            if (dy > 1)
            {
                return(false);
            }

            if (dx == 0 && dy == 0)
            {
                return(false);
            }
            else
            {
                return(true);
            }

            /*
             * // adjacent and not equal
             * if (dx < 2 && dy < 2 && !(dx == 0 && dy == 0)) {
             *  return true;
             * } else {
             *  return false;
             * }
             */
        }
Beispiel #4
0
        //<summary> return the number of hidden tiles shared by these tiles
        public int NumberOfSharedHiddenTiles(SolverTile tile)
        {
            // if the locations are too far apart they can't share any of the same squares
            if (Math.Abs(tile.x - this.x) > 2 || Math.Abs(tile.y - this.y) > 2)
            {
                return(0);
            }

            int count = 0;

            foreach (SolverTile tile1 in this.GetAdjacentTiles())
            {
                if (!tile1.IsHidden())
                {
                    continue;
                }
                foreach (SolverTile tile2 in tile.GetAdjacentTiles())
                {
                    if (!tile2.IsHidden())
                    {
                        continue;
                    }
                    if (tile1.IsEqual(tile2))    // if they share a tile then return true
                    {
                        count++;
                    }
                }
            }

            // no shared tile found
            return(count);
        }
        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);
        }
Beispiel #6
0
        /*
         * public void SetExcluded() {
         *  this.excluded = true;
         * }
         *
         * public bool IsExcluded() {
         *  return this.excluded;
         * }
         */

        public bool IsEqual(SolverTile tile)
        {
            if (this.x == tile.x && this.y == tile.y)
            {
                return(true);
            }
            else
            {
                return(false);
            }
        }
Beispiel #7
0
        public void SetTileToDead(SolverTile tile)
        {
            if (tile.IsMine())
            {
                Write("ERROR: Trying to set a mine tile to dead " + tile.AsText());
                return;
            }

            tile.SetDead(key);    // mark it
            deadTiles.Add(tile);  //  and add to the set
        }
Beispiel #8
0
        private static SolverTile OffEdgeGuess(SolverInfo information, HashSet <SolverTile> witnessedSet)
        {
            // see if the corners are available
            SolverTile bestGuess = information.GetTile(0, 0);

            if (bestGuess.IsHidden() && !witnessedSet.Contains(bestGuess))
            {
                return(bestGuess);
            }
            bestGuess = information.GetTile(0, information.description.height - 1);
            if (bestGuess.IsHidden() && !witnessedSet.Contains(bestGuess))
            {
                return(bestGuess);
            }
            bestGuess = information.GetTile(information.description.width - 1, 0);
            if (bestGuess.IsHidden() && !witnessedSet.Contains(bestGuess))
            {
                return(bestGuess);
            }
            bestGuess = information.GetTile(information.description.width - 1, information.description.height - 1);
            if (bestGuess.IsHidden() && !witnessedSet.Contains(bestGuess))
            {
                return(bestGuess);
            }

            bestGuess = null;
            int bestGuessCount = 9;

            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))
                    {
                        AdjacentInfo adjInfo = information.AdjacentTileInfo(tile);
                        if (adjInfo.hidden < bestGuessCount)
                        {
                            bestGuess      = tile;
                            bestGuessCount = adjInfo.hidden;
                        }
                    }
                }
            }

            return(bestGuess);
        }
Beispiel #9
0
        public SolverInfo(GameDescription description, bool verbose)
        {
            this.description = description;
            this.verbose     = verbose;

            tiles = new SolverTile[description.width, description.height];

            // populate the grid with solver tiles showing what we know so far ... which is nothing
            for (int x = 0; x < description.width; x++)
            {
                for (int y = 0; y < description.height; y++)
                {
                    tiles[x, y] = new SolverTile(key, x, y);
                }
            }

            tilesLeft = description.height * description.width;

            knownMines = new HashSet <SolverTile>(description.mines);
        }
Beispiel #10
0
        // returns the probability the tile is safe if known. Or -1 otherwise.
        public double GetProbability(int x, int y)
        {
            // if we are out of bounds then nothing to say
            if (x < 0 || x >= description.width || y < 0 || y >= description.height)
            {
                return(-1);
            }

            SolverTile tile = tiles[x, y];

            // an unflagged mine
            if (tile.IsMine() && !tile.IsFlagged())
            {
                return(0);
            }

            // otherwise if revealed then nothing to say
            if (!tile.IsHidden())
            {
                return(-1);
            }

            //if (tile.IsExcluded()) {
            //    return -1;
            //}

            if (probabilityEngine != null)
            {
                return(probabilityEngine.GetProbability(tile));
            }
            else if (pendingClears.Contains(tile))
            {
                return(1);
            }
            else
            {
                return(-1);
            }
        }
        private void ShowTree(int depth, int value, Node node)
        {
            String condition;

            if (depth == 0)
            {
                condition = node.GetSolutionSize() + " solutions remain";
            }
            else
            {
                condition = "When '" + value + "' ==> " + node.GetSolutionSize() + " solutions remain";
            }

            if (node.bestLiving == null)
            {
                String line1 = INDENT.Substring(0, depth * 3) + condition + " Solve chance " + node.GetProbability() * 100 + "%";
                Console.WriteLine(line1);
                return;
            }

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

            double prob = 1 - node.bestLiving.mineCount / node.GetSolutionSize();


            String line = INDENT.Substring(0, depth * 3) + condition + " play " + loc.AsText() + " Survival chance " + prob * 100 + "%, Solve chance " + node.GetProbability() * 100 + "%";

            Console.WriteLine(line);

            //for (Node nextNode: node.bestLiving.children) {
            for (int val = 0; val < node.bestLiving.children.Length; val++)
            {
                Node nextNode = node.bestLiving.children[val];
                if (nextNode != null)
                {
                    ShowTree(depth + 1, val, nextNode);
                }
            }
        }
Beispiel #12
0
        public AdjacentInfo AdjacentTileInfo(SolverTile tile)
        {
            int hidden   = 0;
            int mines    = 0;
            int excluded = 0;

            foreach (SolverTile adjTile in GetAdjacentTiles(tile))
            {
                if (adjTile.IsMine())
                {
                    mines++;
                }
                else if (adjTile.IsHidden())
                {
                    hidden++;
                    //if (adjTile.IsExcluded()) {
                    //    excluded++;
                    //}
                }
            }

            return(new AdjacentInfo(mines, hidden, excluded));
        }
Beispiel #13
0
        //public bool IsTileDead(SolverTile tile) {
        //    return deadTiles.Contains(tile);
        //}

        // allow the gui to see if a tile is dead
        //public bool IsTileDead(int x, int y) {
        //    return IsTileDead(tiles[x, y]);
        //}

        // Adds the tile to a list of known clears for future use
        public void ClearFound(SolverTile tile)
        {
            pendingClears.Add(tile);
        }
Beispiel #14
0
        private static List <SolverAction> ScanForTrivialActions(SolverInfo information, HashSet <SolverTile> set)
        {
            List <SolverAction> actions = new List <SolverAction>();;

            HashSet <SolverTile> alreadyProcessed = new HashSet <SolverTile>();

            foreach (SolverTile tile in set)
            {
                AdjacentInfo adjInfo = information.AdjacentTileInfo(tile);

                if (tile.GetValue() == adjInfo.mines)    // if we have the correct number of mines then the remaining hidden tiles can be cleared
                {
                    foreach (SolverTile adjTile in information.GetAdjacentTiles(tile))
                    {
                        if (!alreadyProcessed.Contains(adjTile) && adjTile.IsHidden() && !adjTile.IsMine())
                        {
                            alreadyProcessed.Add(adjTile);   // avoid marking as cleared more than once
                            //Utility.Write(adjTile.AsText() + " can be cleared");
                            actions.Add(new SolverAction(adjTile, ActionType.Clear, 1));
                        }
                    }
                }
                if (tile.GetValue() == adjInfo.mines + adjInfo.hidden)    // If Hidden + Mines we already know about = tile value then the rest must be mines
                {
                    foreach (SolverTile adjTile in information.GetAdjacentTiles(tile))
                    {
                        if (!alreadyProcessed.Contains(adjTile) && adjTile.IsHidden() && !adjTile.IsMine())
                        {
                            alreadyProcessed.Add(adjTile);   // avoid marking as a mine more than once
                            //Utility.Write(adjTile.AsText() + " is a mine");
                            if (!information.MineFound(adjTile))
                            {
                                actions.Add(new SolverAction(adjTile, ActionType.Flag, 0)); // and request it is flagged
                            }
                        }
                    }
                }

                // 2 mines to find and only 3 tiles to put them
                if (tile.GetValue() - adjInfo.mines == 2 && adjInfo.hidden == 3)
                {
                    foreach (SolverTile adjTile in information.GetAdjacentTiles(tile))
                    {
                        if (!adjTile.IsHidden() && !adjTile.IsMine() && !adjTile.IsExhausted())
                        {
                            AdjacentInfo tileAdjInfo = information.AdjacentTileInfo(adjTile);

                            if (adjTile.GetValue() - tileAdjInfo.mines == 1)     // an adjacent tile only needs to find one mine

                            {
                                int        notAdjacentCount = 0;
                                SolverTile notAdjTile       = null;
                                foreach (SolverTile adjTile2 in information.GetAdjacentTiles(tile))
                                {
                                    if (adjTile2.IsHidden() && !adjTile2.IsAdjacent(adjTile))
                                    {
                                        notAdjacentCount++;
                                        notAdjTile = adjTile2;
                                    }
                                }

                                if (notAdjacentCount == 1 && !alreadyProcessed.Contains(notAdjTile)) // we share all but one tile, so that one tile must contain the extra mine
                                {
                                    alreadyProcessed.Add(notAdjTile);                                // avoid marking as a mine more than once
                                    if (!information.MineFound(notAdjTile))
                                    {
                                        actions.Add(new SolverAction(notAdjTile, ActionType.Flag, 0)); // and request it is flagged
                                        break;                                                         // restrict to finding one mine at a time  (the action of marking the mine throws any further analysis out)
                                    }
                                }
                            }
                        }
                    }
                }

                // Subtraction method
                //continue;  // skip this bit
                foreach (SolverTile adjTile in information.GetAdjacentTiles(tile))
                {
                    if (!adjTile.IsHidden() && !adjTile.IsMine() && !adjTile.IsExhausted())
                    {
                        AdjacentInfo tileAdjInfo = information.AdjacentTileInfo(adjTile);

                        if (adjTile.GetValue() - tileAdjInfo.mines == tile.GetValue() - adjInfo.mines)    // if the adjacent tile is revealed and shares the same number of mines to find
                                                                                                          // If all the adjacent tiles adjacent tiles are also adjacent to the original tile
                        {
                            bool allAdjacent = true;
                            foreach (SolverTile adjTile2 in information.GetAdjacentTiles(adjTile))
                            {
                                if (adjTile2.IsHidden() && !adjTile2.IsAdjacent(tile))
                                {
                                    allAdjacent = false;
                                    break;
                                }
                            }

                            if (allAdjacent)
                            {
                                //information.Write(tile.AsText() + " can be subtracted by " + adjTile.AsText());
                                foreach (SolverTile adjTile2 in information.GetAdjacentTiles(tile))
                                {
                                    if (adjTile2.IsHidden() && !adjTile2.IsAdjacent(adjTile) && !alreadyProcessed.Contains(adjTile2))
                                    {
                                        alreadyProcessed.Add(adjTile2);   // avoid marking as a mine more than once
                                        actions.Add(new SolverAction(adjTile2, ActionType.Clear, 1));
                                    }
                                }
                            }
                        }
                    }
                }
            }

            return(actions);
        }
Beispiel #15
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));
        }
Beispiel #16
0
        public void AddInformation(GameResult information)
        {
            //long start = DateTime.Now.Ticks;

            gameStatus = information.status;

            this.probabilityEngine = null; // previous probability engine invalidated

            newClears.Clear();

            // the game results tell us when a tile is cleared, flagged or unflagged
            foreach (ActionResult ar in information.actionResults)
            {
                if (ar.resultType == ResultType.Cleared)
                {
                    tilesLeft--;
                    tiles[ar.x, ar.y].SetValue(ar.value);

                    SolverTile tile = tiles[ar.x, ar.y];
                    newClears.Add(tile);
                    if (tile.IsDead())
                    {
                        deadTiles.Remove(tile);
                    }
                    //if (tile.IsExcluded()) {
                    //    excludedTiles.Remove(tile);
                    //}
                    //pendingClears.Remove(tile);
                }
                else if (ar.resultType == ResultType.Flagged)
                {
                    tiles[ar.x, ar.y].SetFlagged(true);
                }
                else if (ar.resultType == ResultType.Hidden)
                {
                    tiles[ar.x, ar.y].SetFlagged(false);
                }
                else if (ar.resultType == ResultType.Exploded)
                {
                    SolverTile tile = tiles[ar.x, ar.y];
                    tile.SetFlagged(false);
                    MineFound(tile);
                    this.bfa = null;   // can't walk the bfa tree if we've trodden on a mine
                }
            }

            // find and mark tiles as exhausted
            //int removed = 0;
            //if (newClears.Count > 0) {

            foreach (SolverTile tile in livingWitnesses)
            {
                if (AdjacentTileInfo(tile).hidden == 0)
                {
                    tile.SetExhausted();
                }
            }
            // remove all exhausted tiles from the list of witnesses
            livingWitnesses.RemoveWhere(x => x.IsExhausted());

            //}

            // add new witnesses which aren't exhausted
            foreach (SolverTile newTile in newClears)
            {
                AdjacentInfo adjInfo = AdjacentTileInfo(newTile);
                if (adjInfo.hidden != 0)
                {
                    if (adjInfo.excluded != adjInfo.hidden)
                    {
                        livingWitnesses.Add(newTile);
                    }
                    else
                    {
                        //excludedWitnesses.Add(newTile);
                    }
                }
            }

            //Write("There are " + livingWitnesses.Count + " living witnesses (" + removed + " deleted)");
            //Write("Adding Information to Solver took " + (DateTime.Now.Ticks - start) + " ticks");
        }
Beispiel #17
0
        // this checks whether the positions of the mines are a valid candidate solution
        private bool CheckSample(int[] sample)
        {
            // get the tiles which are mines in this sample
            SolverTile[] mine = new SolverTile[sample.Length];
            for (int i = 0; i < sample.Length; i++)
            {
                mine[i] = this.tiles[sample[i]];
            }

            for (int i = 0; i < this.witnesses.Count; i++)
            {
                int flags1 = this.currentFlagsWitnesses[i];
                int flags2 = 0;

                // count how many candidate mines are next to this witness
                for (int j = 0; j < mine.Length; j++)
                {
                    if (mine[j].IsAdjacent(this.witnesses[i].GetTile()))
                    {
                        flags2++;
                    }
                }

                int flags3 = this.witnesses[i].GetTile().GetValue();  // number of flags indicated on the tile

                if (flags3 != flags1 + flags2)
                {
                    //Console.WriteLine("Failed");
                    return(false);
                }
            }

            //if it is a good solution then calculate the distribution if required

            //Console.WriteLine("Solution found");

            sbyte[] solution = new sbyte[this.tiles.Count];

            for (int i = 0; i < this.tiles.Count; i++)
            {
                bool isMine = false;
                for (int j = 0; j < sample.Length; j++)
                {
                    if (i == sample[j])
                    {
                        isMine = true;
                        break;
                    }
                }

                // if we are a mine then it doesn't matter how many mines surround us
                if (!isMine)
                {
                    sbyte flags2 = this.currentFlagsTiles[i];
                    // count how many candidate mines are next to this square
                    for (int j = 0; j < mine.Length; j++)
                    {
                        if (mine[j].IsAdjacent(this.tiles[i]))
                        {
                            flags2++;
                        }
                    }
                    solution[i] = flags2;
                }
                else
                {
                    solution[i] = BOMB;
                }
            }

            bfa.AddSolution(solution);

            /*
             * string output = "";
             * for (int i = 0; i < mine.length; i++) {
             *  output = output + mine[i].asText();
             * }
             * console.log(output);
             */

            return(true);
        }
Beispiel #18
0
        /*
         * // if rotation is -1 then this does all the possible iterations
         * // if rotation is not - 1 then this locks the first 'cog' in that position and iterates the remaining cogs.  This allows parallel processing based on the position of the first 'cog'
         * public WitnessWebIterator(ProbabilityEngine pe, List<SolverTile> allCoveredTiles, int rotation) {
         *
         *  this.tiles = new List<SolverTile>();  // list of tiles being iterated over
         *
         *  this.cogs = new SequentialIterator[pe.GetIndependentWitnesses().Count + 1]; // array of cogs
         *  this.squareOffset = new int[pe.GetIndependentWitnesses().Count + 1];  // int array
         *  this.mineOffset = new int[pe.GetIndependentWitnesses().Count + 1];   // int array
         *
         *  this.iterationsDone = 0;
         *
         *  this.done = false;
         *
         *  //this.probabilityEngine = pe;
         *
         *  // if we are setting the position of the top cog then it can't ever change
         *  if (rotation == -1) {
         *      this.bottom = 0;
         *  } else {
         *      this.bottom = 1;
         *  }
         *
         *  List<SolverTile> loc = new List<SolverTile>();  // array of locations
         *
         *  List<BoxWitness> indWitnesses = pe.GetIndependentWitnesses();
         *
         *  int cogi = 0;
         *  int indSquares = 0;
         *  int indMines = 0;
         *
         *  // create an array of locations in the order of independent witnesses
         *  foreach (BoxWitness w in indWitnesses) {
         *
         *      this.squareOffset[cogi] = indSquares;
         *      this.mineOffset[cogi] = indMines;
         *      this.cogs[cogi] = new SequentialIterator(w.GetMinesToFind(), w.GetAdjacentTiles().Count);
         *      cogi++;
         *
         *      indSquares = indSquares + w.GetAdjacentTiles().Count;
         *      indMines = indMines + w.GetMinesToFind();
         *
         *      loc.AddRange(w.GetAdjacentTiles());
         *
         *  }
         *
         *  //System.out.println("Mines left = " + (mines - indMines));
         *  //System.out.println("Squrs left = " + (web.getSquares().length - indSquares));
         *
         *  // the last cog has the remaining squares and mines
         *
         *  //add the rest of the locations
         *  for (int i = 0; i < allCoveredTiles.Count; i++) {
         *
         *      SolverTile l = allCoveredTiles[i];
         *      bool skip = false;
         *      for (int j = 0; j < loc.Count; j++) {
         *
         *          SolverTile m = loc[j];
         *
         *          if (l.IsEqual(m)) {
         *              skip = true;
         *              break;
         *          }
         *      }
         *      if (!skip) {
         *          loc.Add(l);
         *      }
         *  }
         *
         *  this.tiles = loc;
         *
         *  SolverInfo information = pe.GetSolverInfo();
         *
         *  int minesLeft = information.GetMinesLeft() - information.GetExcludedMineCount();
         *  int tilesLeft = information.GetTilesLeft() - information.GetExcludedTiles().Count;
         *
         *  information.Write("Mines left " + minesLeft);
         *  information.Write("Independent Mines " + indMines);
         *  information.Write("Tiles left " + tilesLeft);
         *  information.Write("Independent tiles " + indSquares);
         *
         *
         *  // if there are more mines left then squares then no solution is possible
         *  // if there are not enough mines to satisfy the minimum we know are needed
         *  if (minesLeft - indMines > tilesLeft - indSquares
         || indMines > minesLeft) {
         ||     this.done = true;
         ||     this.top = 0;
         ||     Console.WriteLine("Nothing to do in this iterator");
         ||     return;
         || }
         ||
         || // if there are no mines left then no need for a cog
         || if (minesLeft > indMines) {
         ||     this.squareOffset[cogi] = indSquares;
         ||     this.mineOffset[cogi] = indMines;
         ||     this.cogs[cogi] = new SequentialIterator(minesLeft - indMines, tilesLeft - indSquares);
         ||     this.top = cogi;
         || } else {
         ||     top = cogi - 1;
         || }
         ||
         || //this.top = this.cogs.Length - 1;
         ||
         || this.sample = new int[minesLeft];  // make the sample array the size of the number of mines
         ||
         || // if we are locking and rotating the top cog then do it
         || if (rotation != -1) {
         ||     for (var i = 0; i < rotation; i++) {
         ||         this.cogs[0].GetNextSample();
         ||     }
         || }
         ||
         || // now set up the initial sample position
         || for (int i = 0; i < this.top; i++) {
         ||     int[] s = this.cogs[i].GetNextSample();
         ||     for (int j = 0; j < s.Length; j++) {
         ||         this.sample[this.mineOffset[i] + j] = this.squareOffset[i] + s[j];
         ||     }
         || }
         ||}
         */

        // if rotation is -1 then this does all the possible iterations
        // if rotation is not - 1 then this locks the first 'cog' in that position and iterates the remaining cogs.  This allows parallel processing based on the position of the first 'cog'
        public WitnessWebIterator(SolverInfo information, List <BoxWitness> independentWitnesses, List <BoxWitness> depdendentWitnesses
                                  , List <SolverTile> allCoveredTiles, int minesLeft, int tilesLeft, int rotation)
        {
            this.tiles = new List <SolverTile>();  // list of tiles being iterated over

            int cogs;

            if (independentWitnesses == null)
            {
                cogs = 1;
            }
            else
            {
                cogs = independentWitnesses.Count + 1;
            }

            this.cogs         = new SequentialIterator[cogs]; // array of cogs
            this.squareOffset = new int[cogs];                // int array
            this.mineOffset   = new int[cogs];                // int array

            this.iterationsDone = 0;

            this.done = false;

            //this.probabilityEngine = pe;

            // if we are setting the position of the top cog then it can't ever change
            if (rotation == -1)
            {
                this.bottom = 0;
            }
            else
            {
                this.bottom = 1;
            }

            List <SolverTile> loc = new List <SolverTile>();  // array of locations

            int cogi       = 0;
            int indSquares = 0;
            int indMines   = 0;

            // create an array of locations in the order of independent witnesses
            if (independentWitnesses != null)
            {
                foreach (BoxWitness w in independentWitnesses)
                {
                    this.squareOffset[cogi] = indSquares;
                    this.mineOffset[cogi]   = indMines;
                    this.cogs[cogi]         = new SequentialIterator(w.GetMinesToFind(), w.GetAdjacentTiles().Count);
                    cogi++;

                    indSquares = indSquares + w.GetAdjacentTiles().Count;
                    indMines   = indMines + w.GetMinesToFind();

                    loc.AddRange(w.GetAdjacentTiles());
                }
            }


            //System.out.println("Mines left = " + (mines - indMines));
            //System.out.println("Squrs left = " + (web.getSquares().length - indSquares));

            // the last cog has the remaining squares and mines

            //add the rest of the locations
            for (int i = 0; i < allCoveredTiles.Count; i++)
            {
                SolverTile l    = allCoveredTiles[i];
                bool       skip = false;
                for (int j = 0; j < loc.Count; j++)
                {
                    SolverTile m = loc[j];

                    if (l.IsEqual(m))
                    {
                        skip = true;
                        break;
                    }
                }
                if (!skip)
                {
                    loc.Add(l);
                }
            }

            this.tiles = loc;

            information.Write("Mines left " + minesLeft);
            information.Write("Independent Mines " + indMines);
            information.Write("Tiles left " + tilesLeft);
            information.Write("Independent tiles " + indSquares);


            // if there are more mines left then squares then no solution is possible
            // if there are not enough mines to satisfy the minimum we know are needed
            if (minesLeft - indMines > tilesLeft - indSquares ||
                indMines > minesLeft)
            {
                this.done = true;
                this.top  = 0;
                Console.WriteLine("Nothing to do in this iterator");
                return;
            }

            // if there are no mines left then no need for a cog
            if (minesLeft > indMines)
            {
                this.squareOffset[cogi] = indSquares;
                this.mineOffset[cogi]   = indMines;
                this.cogs[cogi]         = new SequentialIterator(minesLeft - indMines, tilesLeft - indSquares);
                this.top = cogi;
            }
            else
            {
                top = cogi - 1;
            }

            //this.top = this.cogs.Length - 1;

            this.sample = new int[minesLeft];  // make the sample array the size of the number of mines

            // if we are locking and rotating the top cog then do it
            if (rotation != -1)
            {
                for (var i = 0; i < rotation; i++)
                {
                    this.cogs[0].GetNextSample();
                }
            }

            // now set up the initial sample position
            for (int i = 0; i < this.top; i++)
            {
                int[] s = this.cogs[i].GetNextSample();
                for (int j = 0; j < s.Length; j++)
                {
                    this.sample[this.mineOffset[i] + j] = this.squareOffset[i] + s[j];
                }
            }
        }
Beispiel #19
0
        //public readonly bool isExcluded;

        public SolverAction(SolverTile tile, ActionType action, double safeprob) : base(tile.x, tile.y, action)
        {
            this.safeProbability = safeprob;
            this.isDead          = tile.IsDead();
            //this.isExcluded = tile.IsExcluded();
        }