// calculate a probability for each un-revealed tile on the board public void Process() { this.mask = new bool[this.boxes.Count]; // create an initial solution of no mines anywhere ProbabilityLine held = new ProbabilityLine(this.boxes.Count); held.SetSolutionCount(1); this.heldProbs.Add(held); // add an empty probability line to get us started this.workingProbs.Add(new ProbabilityLine(this.boxes.Count)); NextWitness nextWitness = FindFirstWitness(); while (nextWitness != null) { // mark the new boxes as processed - which they will be soon foreach (Box box in nextWitness.GetNewBoxes()) { this.mask[box.GetUID()] = true; } this.workingProbs = MergeProbabilities(nextWitness); nextWitness = FindNextWitness(nextWitness); } CalculateBoxProbabilities(); }
// this is used to recursively place the missing Mines into the available boxes for the probability line private List <ProbabilityLine> DistributeMissingMines(ProbabilityLine pl, NextWitness nw, int missingMines, int index) { //console.log("Distributing " + missingMines + " missing mines to box " + nw.newBoxes[index].uid); this.recursions++; if (this.recursions % 100000 == 0) { information.Write("Solution Counter recursision = " + recursions); } List <ProbabilityLine> result = new List <ProbabilityLine>(); // if there is only one box left to put the missing mines we have reach the end of this branch of recursion if (nw.GetNewBoxes().Count - index == 1) { // if there are too many for this box then the probability can't be valid if (nw.GetNewBoxes()[index].GetMaxMines() < missingMines) { //console.log("Abandon (1)"); return(result); } // if there are too few for this box then the probability can't be valid if (nw.GetNewBoxes()[index].GetMinMines() > missingMines) { //console.log("Abandon (2)"); return(result); } // if there are too many for this game then the probability can't be valid if (pl.GetMineCount() + missingMines > this.maxTotalMines) { //console.log("Abandon (3)"); return(result); } // otherwise place the mines in the probability line pl.SetMineBoxCount(nw.GetNewBoxes()[index].GetUID(), new BigInteger(missingMines)); pl.SetMineCount(pl.GetMineCount() + missingMines); result.Add(pl); //console.log("Distribute missing mines line after " + pl.mineBoxCount); return(result); } // this is the recursion int maxToPlace = Math.Min(nw.GetNewBoxes()[index].GetMaxMines(), missingMines); for (int i = nw.GetNewBoxes()[index].GetMinMines(); i <= maxToPlace; i++) { ProbabilityLine npl = ExtendProbabilityLine(pl, nw.GetNewBoxes()[index], i); List <ProbabilityLine> r1 = DistributeMissingMines(npl, nw, missingMines - i, index + 1); result.AddRange(r1); } return(result); }
// counts the number of mines already placed private BigInteger CountPlacedMines(ProbabilityLine pl, NextWitness nw) { BigInteger result = 0; foreach (Box b in nw.GetOldBoxes()) { result = result + pl.GetMineBoxCount(b.GetUID()); } return(result); }
// take the next witness details and merge them into the currently held details private List <ProbabilityLine> MergeProbabilities(NextWitness nw) { List <ProbabilityLine> newProbs = new List <ProbabilityLine>(); foreach (ProbabilityLine pl in this.workingProbs) { int missingMines = nw.GetBoxWitness().GetMinesToFind() - (int)CountPlacedMines(pl, nw); if (missingMines < 0) { //console.log("Missing mines < 0 ==> ignoring line"); // too many mines placed around this witness previously, so this probability can't be valid } else if (missingMines == 0) { //console.log("Missing mines = 0 ==> keeping line as is"); newProbs.Add(pl); // witness already exactly satisfied, so nothing to do } else if (nw.GetNewBoxes().Count == 0) { //console.log("new boxes = 0 ==> ignoring line since nowhere for mines to go"); // nowhere to put the new mines, so this probability can't be valid } else { List <ProbabilityLine> result = DistributeMissingMines(pl, nw, missingMines, 0); newProbs.AddRange(result); } } //if (newProbs.length == 0) { // console.log("Returning no lines from merge probability !!"); //} return(newProbs); }
// look for the next witness to process private NextWitness FindNextWitness(NextWitness prevWitness) { // flag the last set of details as processed prevWitness.GetBoxWitness().SetProcessed(true); foreach (Box newBox in prevWitness.GetNewBoxes()) { newBox.SetProcessed(true); } int bestTodo = 99999; BoxWitness bestWitness = null; // and find a witness which is on the boundary of what has already been processed foreach (Box b in this.boxes) { if (b.IsProcessed()) { foreach (BoxWitness w in b.GetBoxWitnesses()) { if (!w.IsProcessed()) { int todo = 0; foreach (Box b1 in w.GetBoxes()) { if (!b1.IsProcessed()) { todo++; } } if (todo == 0) // prioritise the witnesses which have the least boxes left to process { return(new NextWitness(w)); } else if (todo < bestTodo) { bestTodo = todo; bestWitness = w; } } } } } if (bestWitness != null) { return(new NextWitness(bestWitness)); } else { information.Write("Ending independent edge"); } //independentGroups++; // since we have calculated all the mines in an independent set of witnesses we can crunch them down and store them for later // store the details for this edge StoreProbabilities(); // get an unprocessed witness NextWitness nw = FindFirstWitness(); if (nw != null) { information.Write("Starting a new independent edge"); } // If there is nothing else to process then either do the local clears or calculate the probabilities if (nw == null) { edgeStore.Sort(EdgeStore.SortByLineCount); AnalyseAllEdges(); long start = DateTime.Now.Ticks; foreach (EdgeStore edgeDetails in edgeStore) { workingProbs = edgeDetails.data; CombineProbabilities(); } information.Write("Combined all edges in " + (DateTime.Now.Ticks - start) + " ticks"); } // return the next witness to process return(nw); }