Add References to git
This commit is contained in:
@ -0,0 +1,464 @@
|
||||
package minesweeper.solver;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import Monitor.AsynchMonitor;
|
||||
import minesweeper.solver.constructs.CandidateLocation;
|
||||
import minesweeper.solver.constructs.Square;
|
||||
import minesweeper.solver.constructs.Witness;
|
||||
import minesweeper.solver.iterator.WitnessWebIterator;
|
||||
import minesweeper.solver.utility.Logger.Level;
|
||||
import minesweeper.structure.Location;
|
||||
|
||||
public class BruteForce {
|
||||
|
||||
//private final static BigDecimal ZERO_THRESHOLD = new BigDecimal("0.25");
|
||||
|
||||
|
||||
private final WitnessWeb web;
|
||||
private final Solver solver;
|
||||
private final BoardState boardState;
|
||||
|
||||
|
||||
private final int mines;
|
||||
private final BigInteger maxIterations;
|
||||
private final int bfMaxSolutions;
|
||||
|
||||
private CrunchResult crunchResult;
|
||||
|
||||
private boolean hasRun = false;
|
||||
private boolean certainClear = false;
|
||||
|
||||
//private final List<SuperLocation> zeroLocations = new ArrayList<>();
|
||||
|
||||
private final List<CandidateLocation> results = new ArrayList<>();
|
||||
|
||||
private final String scope;
|
||||
|
||||
private BigInteger iterations;
|
||||
|
||||
private BruteForceAnalysisModel bruteForceAnalysis;
|
||||
|
||||
public BruteForce(Solver solver, BoardState boardState, WitnessWeb web, int mines, BigInteger maxIterations, int bfMaxSolutions, String scope) {
|
||||
|
||||
this.solver = solver;
|
||||
this.boardState = boardState;
|
||||
this.maxIterations = maxIterations;
|
||||
this.bfMaxSolutions = bfMaxSolutions;
|
||||
this.scope = scope;
|
||||
|
||||
this.web = web;
|
||||
|
||||
this.mines = mines;
|
||||
|
||||
}
|
||||
|
||||
public void process() {
|
||||
|
||||
solver.logger.log(Level.INFO, "Brute force on %d Squares with %d mines", web.getSquares().size(), mines);
|
||||
|
||||
// if we have no mines to place then everything must be a clear
|
||||
if (mines == 0 ) {
|
||||
solver.logger.log(Level.INFO, "brute force but already found all the mines - clear all the remaining squares");
|
||||
for (Square squ: web.getSquares()) {
|
||||
results.add(new CandidateLocation(squ.x, squ.y, BigDecimal.ONE, boardState.countAdjacentUnrevealed(squ), boardState.countAdjacentConfirmedFlags(squ)));
|
||||
}
|
||||
iterations = BigInteger.ONE;
|
||||
hasRun = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// now doing this logic 'just in time' rather than always at witness web generation
|
||||
web.generateIndependentWitnesses();
|
||||
|
||||
// and crunch the result if we have something to check against
|
||||
if (web.getPrunedWitnesses().size() >= 0) {
|
||||
|
||||
iterations = web.getIterations(mines);
|
||||
|
||||
if (iterations.compareTo(maxIterations) <= 0) {
|
||||
|
||||
//display("Brute Force about to process " + iterations + " iterations");
|
||||
WitnessWebIterator[] iterators = buildParallelIterators(mines, iterations);
|
||||
|
||||
this.bruteForceAnalysis = new BruteForceAnalysis(solver, iterators[0].getLocations(), bfMaxSolutions, scope, solver.bfdaStartLocations());
|
||||
|
||||
|
||||
crunchResult = crunchParallel(web.getSquares(), web.getPrunedWitnesses(), true, iterators);
|
||||
|
||||
// if there are too many to process then don't bother
|
||||
if (this.bruteForceAnalysis != null && this.bruteForceAnalysis.tooMany()) {
|
||||
this.bruteForceAnalysis = null;
|
||||
}
|
||||
|
||||
|
||||
int actIterations = 0;
|
||||
for (WitnessWebIterator i: iterators) {
|
||||
actIterations = actIterations + i.getIterations();
|
||||
}
|
||||
|
||||
solver.logger.log(Level.DEBUG, "Expected iterations %d Actual iterations %d", iterations, actIterations);
|
||||
|
||||
solver.logger.log(Level.INFO, "Found %d candidate solutions in the %s", crunchResult.bigGoodCandidates, scope);
|
||||
|
||||
certainClear = findCertainClear(crunchResult);
|
||||
if (certainClear) {
|
||||
this.bruteForceAnalysis = null;
|
||||
}
|
||||
|
||||
hasRun = true;
|
||||
|
||||
//TODO zero additional mines calculater - do we want it?
|
||||
/*
|
||||
if (crunchResult.bigGoodCandidates.signum() != 0) {
|
||||
BigInteger hwm = BigInteger.ZERO;
|
||||
int best = -1;
|
||||
for (int i=0; i < crunchResult.getSquare().size(); i++) {
|
||||
|
||||
Location loc = crunchResult.getSquare().get(i);
|
||||
|
||||
int adjacentMines = boardState.countAdjacentConfirmedFlags(loc);
|
||||
|
||||
BigDecimal prob = new BigDecimal(crunchResult.bigDistribution[i][adjacentMines]).divide(new BigDecimal(crunchResult.bigGoodCandidates), Solver.DP, RoundingMode.HALF_UP);
|
||||
|
||||
if (prob.compareTo(ZERO_THRESHOLD) >= 0) {
|
||||
SuperLocation zl = new SuperLocation(loc.x, loc.y, boardState.countAdjacentUnrevealed(loc), adjacentMines);
|
||||
zl.setProbability(prob);
|
||||
zeroLocations.add(zl);
|
||||
}
|
||||
if (crunchResult.bigDistribution[i][adjacentMines].compareTo(hwm) > 0) {
|
||||
hwm = crunchResult.bigDistribution[i][adjacentMines];
|
||||
best = i;
|
||||
}
|
||||
}
|
||||
|
||||
if (best != -1) {
|
||||
BigDecimal prob = new BigDecimal(hwm).divide(new BigDecimal(crunchResult.bigGoodCandidates), Solver.DP, RoundingMode.HALF_UP);
|
||||
boardState.display("Location " + crunchResult.getSquare().get(best).display() + " is a 'zero additional mines' with probability " + prob);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
} else {
|
||||
if (maxIterations.signum() != 0) {
|
||||
solver.logger.log(Level.INFO, "Brute Force too large with %d iterations", iterations);
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
solver.logger.log(Level.INFO, "Brute Force not performed since there are no witnesses");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// break a witness web search into a number of non-overlapping iterators
|
||||
private WitnessWebIterator[] buildParallelIterators(int mines, BigInteger totalIterations) {
|
||||
|
||||
solver.logger.log(Level.DEBUG, "Building parallel iterators");
|
||||
|
||||
//WitnessWebIterator[] result1 = new WitnessWebIterator[1];
|
||||
//result1[0] = new WitnessWebIterator(web, mines);
|
||||
//return result1;
|
||||
|
||||
solver.logger.log(Level.DEBUG, "Non independent iterations %d", web.getNonIndependentIterations(mines));
|
||||
|
||||
|
||||
// if there is only one cog then we can't lock it,so send back a single iterator
|
||||
if (web.getIndependentWitnesses().size() == 1 && web.getIndependentMines() >= mines || totalIterations.compareTo(Solver.PARALLEL_MINIMUM) < 0
|
||||
|| web.getPrunedWitnesses().size() == 0 || solver.preferences.isSingleThread()) {
|
||||
solver.logger.log(Level.DEBUG, "Only a single iterator will be used");
|
||||
WitnessWebIterator[] result = new WitnessWebIterator[1];
|
||||
result[0] = new WitnessWebIterator(web, mines);
|
||||
return result;
|
||||
}
|
||||
|
||||
int witMines = web.getIndependentWitnesses().get(0).getMines();
|
||||
int squares = web.getIndependentWitnesses().get(0).getSquares().size();
|
||||
|
||||
BigInteger bigIterations = Solver.combination(witMines, squares);
|
||||
|
||||
int iter = bigIterations.intValue();
|
||||
|
||||
solver.logger.log(Level.DEBUG, "The first cog has %d iterations, so parallel processing is possible", iter);
|
||||
|
||||
WitnessWebIterator[] result = new WitnessWebIterator[iter];
|
||||
|
||||
for (int i=0; i < iter; i++) {
|
||||
result[i] = new WitnessWebIterator(web, mines, i); // create a iterator with a lock first got at position i
|
||||
}
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
// process the iterators in parallel
|
||||
private CrunchResult crunchParallel(List<Square> square, List<Witness> witness, boolean calculateDistribution, WitnessWebIterator... iterator) {
|
||||
|
||||
solver.logger.log(Level.DEBUG, "At parallel iterator processing");
|
||||
|
||||
Cruncher[] crunchers = new Cruncher[iterator.length];
|
||||
|
||||
for (int i=0; i < iterator.length; i++) {
|
||||
crunchers[i] = new Cruncher(boardState, iterator[i].getLocations(), witness, iterator[i], false, bruteForceAnalysis);
|
||||
}
|
||||
|
||||
AsynchMonitor monitor = new AsynchMonitor(crunchers);
|
||||
monitor.setMaxThreads(Solver.CORES);
|
||||
try {
|
||||
monitor.startAndWait();
|
||||
} catch (Exception ex) {
|
||||
solver.logger.log(Level.ERROR, "Parallel processing caused an error: %s", ex.getMessage());
|
||||
ex.printStackTrace();
|
||||
}
|
||||
|
||||
CrunchResult[] results = new CrunchResult[crunchers.length];
|
||||
for (int i=0; i < crunchers.length; i++) {
|
||||
results[i] = crunchers[i].getResult();
|
||||
}
|
||||
|
||||
CrunchResult result = CrunchResult.bigMerge(results);
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
private boolean findCertainClear(CrunchResult output) {
|
||||
|
||||
|
||||
// if there were no good candidates then there is nothing to check
|
||||
if (output.bigGoodCandidates.signum() == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// check the tally information to see if we have a square where a
|
||||
// mine is never present
|
||||
for (int i=0; i < output.bigTally.length; i++) {
|
||||
|
||||
if (output.bigTally[i].signum() == 0) {
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
// do the tally check using the BigInteger values
|
||||
private List<CandidateLocation> checkBigTally(CrunchResult output) {
|
||||
|
||||
List<CandidateLocation> result = new ArrayList<>();
|
||||
|
||||
// if there were no good candidates then there is nothing to check
|
||||
if (output.bigGoodCandidates.compareTo(BigInteger.ZERO) == 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// check the tally information to see if we have a square where a
|
||||
// mine is always present or never present
|
||||
for (int i=0; i < output.bigTally.length; i++) {
|
||||
|
||||
// if there is always a mine here then odds of clear is zero
|
||||
if (output.bigTally[i].compareTo(output.bigGoodCandidates) == 0) {
|
||||
|
||||
int x = output.getSquare().get(i).x;
|
||||
int y = output.getSquare().get(i).y;
|
||||
results.add(new CandidateLocation(x, y, BigDecimal.ZERO, boardState.countAdjacentUnrevealed(x, y), boardState.countAdjacentConfirmedFlags(x, y)));
|
||||
|
||||
// if never a mine then odds of clear is one
|
||||
} else if (output.bigTally[i].compareTo(BigInteger.ZERO) == 0) {
|
||||
|
||||
int x = output.getSquare().get(i).x;
|
||||
int y = output.getSquare().get(i).y;
|
||||
results.add(new CandidateLocation(x, y, BigDecimal.ONE, boardState.countAdjacentUnrevealed(x, y), boardState.countAdjacentConfirmedFlags(x, y)));
|
||||
|
||||
certainClear = true;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
public List<CandidateLocation> getBestSolutions(BigDecimal freshhold) {
|
||||
|
||||
if (crunchResult == null) {
|
||||
return results;
|
||||
}
|
||||
|
||||
if (!results.isEmpty()) {
|
||||
return results;
|
||||
}
|
||||
|
||||
List<CandidateLocation> candidates = new ArrayList<>();
|
||||
|
||||
boolean ignoreBad = true;
|
||||
if (crunchResult.getMaxCount() <= 1) {
|
||||
ignoreBad = false;
|
||||
solver.display("No candidates provide additional information");
|
||||
}
|
||||
|
||||
|
||||
// Calculate the probability of a mine being in the square and store in a list
|
||||
for (int i=0; i < crunchResult.bigTally.length; i++) {
|
||||
|
||||
BigDecimal mine = new BigDecimal(crunchResult.bigTally[i]).divide(new BigDecimal(crunchResult.bigGoodCandidates), Solver.DP, RoundingMode.HALF_UP);
|
||||
BigDecimal notMine = BigDecimal.ONE.subtract(mine);
|
||||
|
||||
Location l = crunchResult.getSquare().get(i);
|
||||
// ignore candidates that yield no info, unless none do or they are certainties
|
||||
if (crunchResult.getBigCount()[i] > 1 || !ignoreBad || notMine.compareTo(BigDecimal.ZERO) == 0 || notMine.compareTo(BigDecimal.ONE) == 0) {
|
||||
candidates.add(new CandidateLocation(l.x, l.y, notMine, boardState.countAdjacentUnrevealed(l), boardState.countAdjacentConfirmedFlags(l), crunchResult.getBigCount()[i]));
|
||||
} else {
|
||||
solver.display(l.display() + " clear probability " + notMine + " discarded because it reveals no further information");
|
||||
}
|
||||
//candidates.add(new CandidateLocation(l.x, l.y, notMine, boardState.countAdjacentUnrevealed(l), boardState.countAdjacentConfirmedFlags(l)));
|
||||
|
||||
}
|
||||
|
||||
// sort the candidates into descending order by probability
|
||||
Collections.sort(candidates, CandidateLocation.SORT_BY_PROB_FLAG_FREE);
|
||||
|
||||
BigDecimal hwm = candidates.get(0).getProbability();
|
||||
|
||||
BigDecimal tolerence;
|
||||
if (hwm.compareTo(BigDecimal.ONE) == 0) {
|
||||
tolerence = hwm;
|
||||
} else {
|
||||
tolerence = hwm.multiply(freshhold);
|
||||
}
|
||||
|
||||
for (CandidateLocation cl: candidates) {
|
||||
if (cl.getProbability().compareTo(tolerence) >= 0) {
|
||||
results.add(cl);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
boardState.display("Best Guess: " + candidates.size() + " candidates, " + results.size() + " passed tolerence at " + tolerence);
|
||||
|
||||
return results;
|
||||
}
|
||||
*/
|
||||
|
||||
public boolean hasRun() {
|
||||
return this.hasRun;
|
||||
}
|
||||
|
||||
public boolean hasCertainClear() {
|
||||
return this.certainClear;
|
||||
}
|
||||
|
||||
public CrunchResult getCrunchResult() {
|
||||
return this.crunchResult;
|
||||
}
|
||||
|
||||
public BigInteger getSolutionCount() {
|
||||
if (crunchResult == null) {
|
||||
return BigInteger.ONE;
|
||||
}
|
||||
|
||||
return crunchResult.bigGoodCandidates;
|
||||
}
|
||||
|
||||
public BigInteger getIterations() {
|
||||
return this.iterations;
|
||||
}
|
||||
|
||||
public int getTileCount() {
|
||||
return web.getSquares().size();
|
||||
}
|
||||
|
||||
|
||||
public BruteForceAnalysisModel getBruteForceAnalysis() {
|
||||
return bruteForceAnalysis;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the probability for the probabilityLocation being satisfied
|
||||
* @param list
|
||||
*/
|
||||
/*
|
||||
public <T extends ProbabilityLocation> List<T> setProbabilities(List<T> list) {
|
||||
|
||||
if (!hasRun) {
|
||||
return list;
|
||||
}
|
||||
|
||||
List<T> output = new ArrayList<>();
|
||||
|
||||
for (ProbabilityLocation pl: list) {
|
||||
for (int i=0; i < crunchResult.getSquare().size(); i++) {
|
||||
if (crunchResult.getSquare().get(i).equals(pl)) {
|
||||
|
||||
// get the values which are good for this location
|
||||
int[] adjFlagsRequired = pl.getAdjacentFlagsRequired();
|
||||
|
||||
// count the number of solutions which have those values
|
||||
BigInteger count = BigInteger.ZERO;
|
||||
for (int j = 0; j < adjFlagsRequired.length; j++) {
|
||||
count = count.add(crunchResult.bigDistribution[i][adjFlagsRequired[j]]);
|
||||
}
|
||||
|
||||
// work out the % chance of it happening or if zero chance discard the location
|
||||
if (count.signum() != 0) {
|
||||
BigDecimal prob = new BigDecimal(count).divide(new BigDecimal(crunchResult.bigGoodCandidates), Solver.DP, RoundingMode.HALF_UP);
|
||||
pl.setProbability(prob);
|
||||
boardState.display(pl.display() + " has probability " + prob);
|
||||
output.add((T) pl);
|
||||
} else {
|
||||
boardState.display(pl.display() + " has probability zero and is being discarded");
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the probability that this square is not a mine
|
||||
*/
|
||||
public BigDecimal getProbability(int x, int y) {
|
||||
|
||||
Location l = this.boardState.getLocation(x,y);
|
||||
|
||||
if (crunchResult == null) { // this can happen if there are no mines left to find, so everything is a clear
|
||||
for (Location loc: web.getSquares()) { // if the mouse is hovering over one of the brute forced squares
|
||||
if (loc.equals(l)) {
|
||||
return BigDecimal.ONE;
|
||||
}
|
||||
}
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
|
||||
for (int i=0; i < crunchResult.getSquare().size(); i++) {
|
||||
if (crunchResult.getSquare().get(i).equals(l)) {
|
||||
BigDecimal prob = new BigDecimal(crunchResult.bigTally[i]).divide(new BigDecimal(crunchResult.bigGoodCandidates), Solver.DP, RoundingMode.HALF_UP);
|
||||
return BigDecimal.ONE.subtract(prob);
|
||||
}
|
||||
}
|
||||
|
||||
return BigDecimal.ZERO;
|
||||
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user