// 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); }
// 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()); }
// 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; * } */ }
//<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); }
/* * 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); } }
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 }
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 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); }
// 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); } } }
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)); }
//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); }
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); }
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)); }
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"); }
// 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); }
/* * // 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 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(); }