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);
        }
        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);
        }