Exemple #1
0
        public static BruteForceAnalysis PerformBruteForce(SolverInfo information, WitnessWebIterator[] iterators, List <BoxWitness> witnesses)
        {
            BruteForceAnalysis bfa = new BruteForceAnalysis(information, iterators[0].getTiles(), SolverMain.MAX_BFDA_SOLUTIONS, null);

            Cruncher[] crunchers = new Cruncher[iterators.Length];
            Task[]     tasks     = new Task[iterators.Length];

            for (int i = 0; i < iterators.Length; i++)
            {
                crunchers[i] = new Cruncher(information, iterators[i], witnesses, bfa);
                Cruncher cruncher = crunchers[i];

                tasks[i] = Task.Factory.StartNew(() => { cruncher.Crunch(); });
            }
            Task.WaitAll(tasks);

            int solutions  = 0;
            int iterations = 0;

            for (int i = 0; i < iterators.Length; i++)
            {
                solutions  = solutions + crunchers[i].GetSolutionsFound();
                iterations = iterations + iterators[i].GetIterations();
            }

            information.Write("Solutions found by brute force " + solutions + " after " + iterations + " iterations");

            return(bfa);
        }
        private static SolverActionHeader BuildActionHeader(SolverInfo information, List <SolverAction> actions)
        {
            HashSet <SolverTile> dead = information.GetDeadTiles();

            // add any dead tiles to the list of actions
            List <SolverAction> deadList = new List <SolverAction>();

            foreach (SolverTile dt in dead)
            {
                deadList.Add(new SolverAction(dt, ActionType.Dead, 1));
            }

            return(new SolverActionHeader(actions, deadList));
        }
        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);
        }
        public BruteForceAnalysis(SolverInfo solver, List <SolverTile> locations, int size, List <SolverTile> startLocations)
        {
            this.solver          = solver;
            this.locations       = locations;
            this.maxSolutionSize = size;

            //this.top = new Node();
            sorters = new SortSolutions[locations.Count];
            for (int i = 0; i < sorters.Length; i++)
            {
                sorters[i] = new SortSolutions(i);
            }

            this.allSolutions = new SolutionTable(this, size);

            this.startLocations = startLocations;
        }
        private static List <SolverAction> FindTrivialActions(SolverInfo information)
        {
            List <SolverAction> actions = new List <SolverAction>();

            // provide actions for known mines which aren't flagged
            foreach (SolverTile tile in information.GetKnownMines())
            {
                if (!tile.IsFlagged())
                {
                    actions.Add(new SolverAction(tile, ActionType.Flag, 0)); // request it is flagged
                }
            }

            actions.AddRange(ScanForTrivialActions(information, information.GetWitnesses()));

            //actions.AddRange(ScanForTrivialActions(information, information.GetExcludedWitnesses()));

            information.Write("Found " + actions.Count + " trivial actions");

            return(actions);
        }
        // break a witness web search into a number of non-overlapping iterators
        private static WitnessWebIterator[] BuildParallelIterators(SolverInfo information, ProbabilityEngine pe, List <SolverTile> allCovered, BigInteger expectedIterations)
        {
            information.Write("Building parallel iterators");

            //information.Write("Non independent iterations = " + pe.GetNonIndependentIterations(mines));

            int minesLeft = information.GetMinesLeft();

            //the number of witnesses available
            int totalWitnesses = pe.GetIndependentWitnesses().Count + pe.GetDependentWitnesses().Count;

            // if there is only one cog then we can't lock it,so send back a single iterator
            if (pe.GetIndependentWitnesses().Count == 1 && pe.GetIndependentMines() >= minesLeft || expectedIterations.CompareTo(PARALLEL_MINIMUM) < 0 || totalWitnesses == 0)
            {
                information.Write("Only a single iterator will be used");
                WitnessWebIterator[] one = new WitnessWebIterator[1];
                one[0] = new WitnessWebIterator(information, pe.GetIndependentWitnesses(), pe.GetDependentWitnesses(), allCovered, minesLeft, information.GetTilesLeft(), -1);
                return(one);
            }

            int witMines = pe.GetIndependentWitnesses()[0].GetMinesToFind();
            int squares  = pe.GetIndependentWitnesses()[0].GetAdjacentTiles().Count;

            BigInteger bigIterations = Calculate(witMines, squares);

            int iter = (int)bigIterations;

            information.Write("The first cog has " + iter + " iterations, so parallel processing is possible");

            WitnessWebIterator[] result = new WitnessWebIterator[iter];

            for (int i = 0; i < iter; i++)
            {
                // create a iterator with a lock first got at position i
                result[i] = new WitnessWebIterator(information, pe.GetIndependentWitnesses(), pe.GetDependentWitnesses(), allCovered, minesLeft, information.GetTilesLeft(), i);
            }

            return(result);
        }
Exemple #7
0
        public Cruncher(SolverInfo information, WitnessWebIterator iterator, List <BoxWitness> witnesses, BruteForceAnalysis bfa)
        {
            this.information = information;
            this.iterator    = iterator;            // the iterator
            this.tiles       = iterator.getTiles(); // the tiles the iterator is iterating over
            this.witnesses   = witnesses;           // the dependent witnesses (class BoxWitness) which need to be checked to see if they are satisfied

            this.bfa = bfa;

            // determine how many found mines are currently next to each tile
            this.currentFlagsTiles = new sbyte[this.tiles.Count];
            for (int i = 0; i < this.tiles.Count; i++)
            {
                this.currentFlagsTiles[i] = (sbyte)this.information.AdjacentTileInfo(this.tiles[i]).mines;
            }


            // determine how many found mines are currently next to each witness
            this.currentFlagsWitnesses = new sbyte[this.witnesses.Count];
            for (int i = 0; i < this.witnesses.Count; i++)
            {
                this.currentFlagsWitnesses[i] = (sbyte)this.information.AdjacentTileInfo(this.witnesses[i].GetTile()).mines;
            }
        }
Exemple #8
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];
                }
            }
        }
        public SolutionCounter(SolverInfo information, List <SolverTile> allWitnesses, List <SolverTile> allWitnessed, int squaresLeft, int minesLeft)
        {
            this.information = information;

            this.witnessed = allWitnessed;

            // constraints in the game
            this.minesLeft     = minesLeft;
            this.tilesLeft     = squaresLeft;
            this.tilesOffEdge  = squaresLeft - allWitnessed.Count;  // squares left off the edge and unrevealed
            this.minTotalMines = minesLeft - this.tilesOffEdge;     //we can't use so few mines that we can't fit the remainder elsewhere on the board
            this.maxTotalMines = minesLeft;

            this.mineCountUpperCutoff = minesLeft;
            this.mineCountLowerCutoff = minTotalMines;

            information.Write("Tiles off edge " + tilesOffEdge);

            //this.boxProb = [];  // the probabilities end up here

            // generate a BoxWitness for each witness tile and also create a list of pruned witnesses for the brute force search
            int pruned = 0;

            foreach (SolverTile wit in allWitnesses)
            {
                BoxWitness boxWit = new BoxWitness(information, wit);

                // if the witness is a duplicate then don't store it
                bool duplicate = false;
                foreach (BoxWitness w in this.boxWitnesses)
                {
                    if (w.Equivalent(boxWit))
                    {
                        //if (boardState.getWitnessValue(w) - boardState.countAdjacentConfirmedFlags(w) != boardState.getWitnessValue(wit) - boardState.countAdjacentConfirmedFlags(wit)) {
                        //    boardState.display(w.display() + " and " + wit.display() + " share unrevealed squares but have different mine totals!");
                        //    validWeb = false;
                        //}
                        duplicate = true;
                        break;
                    }
                }
                if (!duplicate)
                {
                    this.prunedWitnesses.Add(boxWit);
                }
                else
                {
                    pruned++;
                }
                this.boxWitnesses.Add(boxWit);  // all witnesses are needed for the probability engine
            }
            information.Write("Pruned " + pruned + " witnesses as duplicates");
            information.Write("There are " + this.boxWitnesses.Count + " Box witnesses");

            // allocate each of the witnessed squares to a box
            int uid = 0;

            foreach (SolverTile tile in this.witnessed)
            {
                // for each adjacent tile see if it is a witness
                int count = 0;
                //foreach (SolverTile adjTile in information.GetAdjacentTiles(tile)) {
                //    if (information.GetWitnesses().Contains(adjTile)) {
                //        count++;
                //    }
                //}

                // count how many adjacent witnesses the tile has
                foreach (SolverTile tile1 in allWitnesses)
                {
                    if (tile.IsAdjacent(tile1))
                    {
                        count++;
                    }
                }

                // see if the witnessed tile fits any existing boxes
                bool found = false;
                foreach (Box box in this.boxes)
                {
                    if (box.Fits(tile, count))
                    {
                        box.Add(tile);
                        boxLookup.Add(tile, box);   // add this to the lookup
                        found = true;
                        break;
                    }
                }

                // if not found create a new box and store it
                if (!found)
                {
                    Box box = new Box(this.boxWitnesses, tile, uid++);
                    this.boxes.Add(box);
                    boxLookup.Add(tile, box);   // add this to the lookup
                }
            }

            // calculate the min and max mines for each box
            foreach (Box box in this.boxes)
            {
                box.Calculate(this.minesLeft);
                //console.log("Box " + box.tiles[0].asText() + " has min mines = " + box.minMines + " and max mines = " + box.maxMines);
            }

            // Report how many boxes each witness is adjacent to
            foreach (BoxWitness boxWit in this.boxWitnesses)
            {
                information.Write("Witness " + boxWit.GetTile().AsText() + " is adjacent to " + boxWit.GetBoxes().Count + " boxes and has " + boxWit.GetMinesToFind() + " mines to find");
            }
        }
Exemple #10
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);
        }
Exemple #11
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));
        }