889 lines
23 KiB
Java
889 lines
23 KiB
Java
package minesweeper.solver;
|
|
|
|
import java.math.BigDecimal;
|
|
import java.util.ArrayList;
|
|
import java.util.Collection;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
|
|
import minesweeper.gamestate.GameStateModel;
|
|
import minesweeper.gamestate.MoveMethod;
|
|
import minesweeper.solver.BoardStateCache.AdjacentSquares;
|
|
import minesweeper.solver.BoardStateCache.Cache;
|
|
import minesweeper.solver.utility.Logger;
|
|
import minesweeper.solver.utility.Logger.Level;
|
|
import minesweeper.structure.Action;
|
|
import minesweeper.structure.Area;
|
|
import minesweeper.structure.Location;
|
|
|
|
public class BoardState {
|
|
|
|
private final static int[] DX = {0, 1, 1, 1, 0, -1, -1, -1};
|
|
private final static int[] DY = {-1, -1, 0, 1, 1, 1, 0, -1};
|
|
|
|
//private AdjacentSquares[][] adjacentLocations1;
|
|
//private AdjacentSquares[][] adjacentLocations2;
|
|
|
|
private int[][] board;
|
|
private boolean[][] revealed;
|
|
private boolean[][] confirmedMine;
|
|
private boolean[][] flagOnBoard;
|
|
|
|
//private int[][] clearAll;
|
|
|
|
private int[][] adjFlagsConfirmed;
|
|
private int[][] adjFlagsOnBoard;
|
|
private int[][] adjUnrevealed;
|
|
|
|
// this holds the actions made against each location and a list of actions generated this turn
|
|
private Action[][] action;
|
|
private List<Action> actionList = new ArrayList<Action>();
|
|
|
|
private final GameStateModel myGame;
|
|
private final Solver solver;
|
|
private final int height;
|
|
private final int width;
|
|
|
|
private int totalFlags = 0;
|
|
private int confirmedMinesTotal = 0;
|
|
private int numOfHidden = 0;
|
|
|
|
private int[] unplayedMoves;
|
|
|
|
private int testMoveBalance = 0;
|
|
|
|
private Set<Location> livingWitnesses = new HashSet<>();
|
|
|
|
private final Cache cache;
|
|
|
|
public BoardState(Solver solver) {
|
|
|
|
this.solver = solver;
|
|
this.myGame = solver.getGame();
|
|
this.width = myGame.getWidth();
|
|
this.height = myGame.getHeight();
|
|
|
|
confirmedMine = new boolean[myGame.getWidth()][myGame.getHeight()];
|
|
adjFlagsConfirmed = new int[myGame.getWidth()][myGame.getHeight()];
|
|
adjUnrevealed = new int[myGame.getWidth()][myGame.getHeight()];
|
|
revealed = new boolean[myGame.getWidth()][myGame.getHeight()];
|
|
board = new int[myGame.getWidth()][myGame.getHeight()];
|
|
|
|
flagOnBoard = new boolean[myGame.getWidth()][myGame.getHeight()];
|
|
adjFlagsOnBoard = new int[myGame.getWidth()][myGame.getHeight()];
|
|
|
|
action = new Action[myGame.getWidth()][myGame.getHeight()];
|
|
|
|
// look up the adjacent squares details
|
|
cache = BoardStateCache.getInstance().getAdjacentSquares1(myGame.getWidth(), myGame.getHeight());
|
|
//adjacentLocations1 = cache.adjacentLocations1;
|
|
//adjacentLocations2 = cache.adjacentLocations2;
|
|
|
|
|
|
final int bottom = myGame.getHeight() - 1;
|
|
final int right = myGame.getWidth() - 1;
|
|
|
|
// set up how many adjacent locations there are to each square - they are all unrevealed to start with
|
|
for (int x=0; x < width; x++) {
|
|
for (int y=0; y < height; y++) {
|
|
|
|
int adjacent = 8;
|
|
// corners
|
|
if (x == 0 && y == 0 || x == 0 && y == bottom || x == right && y == 0 || x == right && y == bottom) {
|
|
adjacent = 3;
|
|
// the edge
|
|
} else if (x == 0 || y == 0 || x == right || y == bottom){
|
|
adjacent = 5;
|
|
}
|
|
|
|
adjUnrevealed[x][y] = adjacent;
|
|
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
public void process() {
|
|
|
|
totalFlags = 0;
|
|
confirmedMinesTotal = 0;
|
|
numOfHidden = 0;
|
|
|
|
// clear down this array, which is a lot faster then defining it fresh
|
|
for (int i=0; i < width; i++) {
|
|
for (int j=0; j < height; j++) {
|
|
adjFlagsOnBoard[i][j] = 0;
|
|
}
|
|
}
|
|
|
|
// clear down the moves we collected last turn
|
|
actionList.clear();
|
|
|
|
// load up what we can see on the board
|
|
for (int i=0; i < width; i++) {
|
|
for (int j=0; j < height; j++) {
|
|
|
|
Location location = getLocation(i, j);
|
|
|
|
flagOnBoard[i][j] = false; // until proven otherwise
|
|
int info = myGame.query(location);
|
|
|
|
Action act = action[i][j];
|
|
|
|
// if the move isn't a certainty then don't bother with it. The opening book is a certainty on the first move, but isn't really if the player plays somewhere else.
|
|
if (act != null && (!act.isCertainty() || act.getMoveMethod() == MoveMethod.BOOK)) {
|
|
action[i][j] = null;
|
|
act = null;
|
|
}
|
|
|
|
if (info != GameStateModel.HIDDEN) {
|
|
if (info == GameStateModel.FLAG) {
|
|
totalFlags++;
|
|
flagOnBoard[i][j] = true;
|
|
|
|
// inform its neighbours they have a flag on the board
|
|
for (int k=0; k < DX.length; k++) {
|
|
if (i + DX[k] >= 0 && i + DX[k] < width && j + DY[k] >= 0 && j + DY[k] < height) {
|
|
adjFlagsOnBoard[i + DX[k]][j + DY[k]]++;
|
|
}
|
|
}
|
|
|
|
if (confirmedMine[i][j]) { // mine found by solver
|
|
confirmedMinesTotal++;
|
|
} else {
|
|
numOfHidden++; // flag on the board but we can't confirm it
|
|
}
|
|
|
|
// if the board is a flag, but we are 100% sure its a clear then remove the flag
|
|
// then clear the square
|
|
if (act != null && act.getAction() == Action.CLEAR && act.isCertainty()) {
|
|
actionList.add(new Action(act, Action.FLAG, MoveMethod.CORRECTION, "Remove flag", BigDecimal.ONE, 0));
|
|
actionList.add(act);
|
|
}
|
|
|
|
} else {
|
|
|
|
// if this is a new unrevealed location then set it up and inform it's neighbours they have one less unrevealed adjacent location
|
|
if (!revealed[i][j]) {
|
|
|
|
livingWitnesses.add(location); // add this to living witnesses
|
|
//display("Location (" + i + "," + j + ") is revealed");
|
|
|
|
revealed[i][j] = true;
|
|
board[i][j] = info;
|
|
|
|
for (int k=0; k < DX.length; k++) {
|
|
if (i + DX[k] >= 0 && i + DX[k] < width && j + DY[k] >= 0 && j + DY[k] < height) {
|
|
adjUnrevealed[i + DX[k]][j + DY[k]]--;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
} else {
|
|
if ((solver.getPlayStyle().flagless || solver.getPlayStyle().efficiency) && confirmedMine[i][j]) { // if we are playing flags free then all confirmed mines are consider to be flagged
|
|
confirmedMinesTotal++;
|
|
totalFlags++;
|
|
} else {
|
|
numOfHidden++;
|
|
}
|
|
|
|
// if we have an action against this location which we are 100% sure about then do it
|
|
if (act != null && act.isCertainty()) {
|
|
if ((solver.getPlayStyle().flagless || solver.getPlayStyle().efficiency) && act.getAction() == Action.FLAG) {
|
|
// unless the we are playing flag free and it's a flag
|
|
} else {
|
|
actionList.add(act);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
List<Location> toRemove = new ArrayList<>();
|
|
for (Location wit: livingWitnesses) {
|
|
if (this.countAdjacentUnrevealed(wit) == 0) {
|
|
//display("Location " + wit.display() + " is now a dead witness");
|
|
toRemove.add(wit);
|
|
}
|
|
}
|
|
livingWitnesses.removeAll(toRemove);
|
|
|
|
|
|
// this sorts the moves by when they were discovered
|
|
Collections.sort(actionList, Action.SORT_BY_MOVE_NUMBER);
|
|
|
|
unplayedMoves = new int[MoveMethod.values().length];
|
|
|
|
// accumulate how many unplayed moves there are by method
|
|
for (Action a: actionList) {
|
|
unplayedMoves[a.getMoveMethod().ordinal()]++;
|
|
}
|
|
|
|
getLogger().log(Level.INFO, "Moves left to play is %d", actionList.size());
|
|
for (int i=0; i < unplayedMoves.length; i++) {
|
|
if (unplayedMoves[i] != 0) {
|
|
getLogger().log(Level.INFO, " %s has %d moves unplayed",MoveMethod.values()[i], unplayedMoves[i]);
|
|
}
|
|
}
|
|
|
|
|
|
}
|
|
|
|
protected int getGameWidth() {
|
|
return width;
|
|
}
|
|
|
|
protected int getGameHeight() {
|
|
return height;
|
|
}
|
|
|
|
protected int getMines() {
|
|
return this.myGame.getMines();
|
|
}
|
|
|
|
|
|
//public void setAction(Action a) {
|
|
// setAction(a, true);
|
|
//}
|
|
|
|
/**
|
|
* Register the action against the location(x,y);
|
|
* Optionally add the action to the list of actions to play this turn
|
|
*/
|
|
public void setAction(Action a) {
|
|
|
|
//display("Setting action at " + a.display());
|
|
|
|
if (action[a.x][a.y] != null) {
|
|
return;
|
|
}
|
|
|
|
action[a.x][a.y] = a;
|
|
|
|
if (a.getAction() == Action.FLAG) {
|
|
setMineFound(a);
|
|
}
|
|
|
|
if ((solver.getPlayStyle().flagless || solver.getPlayStyle().efficiency) && a.getAction() == Action.FLAG) {
|
|
// if it is flag free or efficiency and we have discovered a mine then don't flag it
|
|
} else if (isFlagOnBoard(a) && a.getAction() == Action.FLAG) {
|
|
// if the flag is already on the board then nothing to do
|
|
} else if (isFlagOnBoard(a) && a.getAction() == Action.CLEAR) {
|
|
// if a flag is blocking the clear move then remove the flag first
|
|
actionList.add(new Action(a, Action.FLAG, MoveMethod.CORRECTION, "Remove flag", BigDecimal.ONE, 0));
|
|
actionList.add(a);
|
|
} else {
|
|
actionList.add(a);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
protected boolean alreadyActioned(Location l) {
|
|
return alreadyActioned(l.x, l.y);
|
|
}
|
|
|
|
protected boolean alreadyActioned(int x, int y) {
|
|
return (action[x][y] != null);
|
|
}
|
|
|
|
|
|
protected List<Action> getActions() {
|
|
return this.actionList;
|
|
}
|
|
|
|
/**
|
|
* This will consider chords when returning the moves to play
|
|
*/
|
|
/*
|
|
protected List<Action> getActionsWithChords() {
|
|
|
|
// if we aren't using chords or none are available then skip all this expensive processing
|
|
if (chordLocations.isEmpty() || !solver.isPlayChords()) {
|
|
return getActions();
|
|
}
|
|
|
|
List<Action> actions = new ArrayList<>();
|
|
boolean[][] processed = new boolean[myGame.getWidth()][myGame.getHeight()];
|
|
|
|
// sort the most beneficial chords to the top
|
|
Collections.sort(chordLocations, ChordLocation.SORT_BY_BENEFIT_DESC);
|
|
|
|
List<ChordLocation> toDelete = new ArrayList<>();
|
|
|
|
for (ChordLocation cl: chordLocations) {
|
|
|
|
int benefit = 0;
|
|
int cost = 0;
|
|
|
|
for (Location l: getAdjacentSquaresIterable(cl)) {
|
|
// flag not yet on board
|
|
if (!processed[l.x][l.y] && isConfirmedFlag(l) && !isFlagOnBoard(l)) {
|
|
cost++;
|
|
}
|
|
if (!processed[l.x][l.y] && isUnrevealed(l)) {
|
|
benefit++;
|
|
}
|
|
}
|
|
|
|
|
|
if (benefit - cost > 1) {
|
|
|
|
for (Location l: getAdjacentSquaresIterable(cl)) {
|
|
// flag not yet on board
|
|
if (!processed[l.x][l.y] && isConfirmedFlag(l) && !isFlagOnBoard(l)) {
|
|
actions.add(new Action(l, Action.FLAG, MoveMethod.TRIVIAL, "Place flag", BigDecimal.ONE, 0));
|
|
}
|
|
// flag on board in error
|
|
if (!processed[l.x][l.y] && !isConfirmedFlag(l) && isFlagOnBoard(l)) {
|
|
actions.add(new Action(l, Action.FLAG, MoveMethod.CORRECTION, "Remove flag", BigDecimal.ONE, 0));
|
|
}
|
|
processed[l.x][l.y] = true;
|
|
}
|
|
// now add the clear all
|
|
actions.add(new Action(cl, Action.CLEARALL, MoveMethod.TRIVIAL, "Clear All", BigDecimal.ONE, 1));
|
|
|
|
} else {
|
|
//toDelete.add(cl);
|
|
}
|
|
}
|
|
|
|
chordLocations.removeAll(toDelete);
|
|
|
|
// now add the the actions that haven't been resolved by a chord play
|
|
for (Action act: actionList) {
|
|
if (!processed[act.x][act.y]) {
|
|
processed[act.x][act.y] = true;
|
|
}
|
|
actions.add(act);
|
|
}
|
|
|
|
return actions;
|
|
}
|
|
*/
|
|
/**
|
|
* Get the probability of a mine being in this square (based upon the actions still pending)
|
|
*/
|
|
protected BigDecimal getProbability(int x, int y) {
|
|
|
|
for (Action act: actionList) {
|
|
if (act.x == x && act.y== y) {
|
|
if (act.getAction() == Action.FLAG) {
|
|
return BigDecimal.ZERO;
|
|
} else if (act.getAction() == Action.CLEAR) {
|
|
return act.getBigProb();
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
//protected int getActionsCount() {
|
|
// return this.actionList.size();
|
|
//}
|
|
|
|
/**
|
|
* Add a isolated dead tile
|
|
*/
|
|
//protected void addIsolatedDeadTile(Location loc) {
|
|
// isolatedDeadTiles.add(loc);
|
|
//}
|
|
|
|
//public int getIsolatedDeadTileCount() {
|
|
// return this.isolatedDeadTiles.size();
|
|
//}
|
|
|
|
/**
|
|
* Returns and removes the first Isolated Dead Tile in the set
|
|
*/
|
|
//public Location getIsolatedDeadTile() {
|
|
// for (Location loc: isolatedDeadTiles) {
|
|
// //isolatedDeadTiles.remove(loc);
|
|
// return loc;
|
|
// }
|
|
// return null;
|
|
//}
|
|
|
|
protected List<Location> getWitnesses(Collection<? extends Location> square) {
|
|
return new ArrayList<Location>(getWitnessesArea(square).getLocations());
|
|
}
|
|
|
|
/**
|
|
* From the given locations, generate all the revealed squares that can witness these locations
|
|
*/
|
|
protected Area getWitnessesArea(Collection<? extends Location> square) {
|
|
|
|
Set<Location> work = new HashSet<>(10);
|
|
|
|
for (Location loc: square) {
|
|
|
|
for (Location adj: this.getAdjacentSquaresIterable(loc)) {
|
|
// determine the number of distinct witnesses
|
|
if (isRevealed(adj)) {
|
|
work.add(adj);
|
|
}
|
|
}
|
|
}
|
|
|
|
return new Area(work);
|
|
|
|
}
|
|
|
|
/**
|
|
* From the given locations, generate an area containing all the un-revealed squares around them
|
|
*/
|
|
protected Area getUnrevealedArea(List<? extends Location> witnesses) {
|
|
return new Area(getUnrevealedSquaresDo(witnesses));
|
|
}
|
|
|
|
/**
|
|
* From the given locations, generate all the un-revealed squares around them
|
|
*/
|
|
protected List<Location> getUnrevealedSquares(List<? extends Location> witnesses) {
|
|
return new ArrayList<Location>(getUnrevealedSquaresDo(witnesses));
|
|
}
|
|
|
|
|
|
private Set<Location> getUnrevealedSquaresDo(List<? extends Location> witnesses) {
|
|
|
|
Set<Location> work = new HashSet<>(witnesses.size() * 3);
|
|
|
|
for (Location loc: witnesses) {
|
|
|
|
for (Location adj: this.getAdjacentSquaresIterable(loc)) {
|
|
|
|
if (isUnrevealed(adj)) {
|
|
work.add(adj);
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
return work;
|
|
}
|
|
|
|
/**
|
|
* Return all the unrevealed Locations on the board
|
|
*/
|
|
protected List<Location> getAllUnrevealedSquares() {
|
|
|
|
ArrayList<Location> work = new ArrayList<>(width * height);
|
|
|
|
for (int i=0; i < width; i++) {
|
|
for (int j=0; j < height; j++) {
|
|
if (isUnrevealed(i,j)) {
|
|
work.add(getLocation(i,j));
|
|
}
|
|
}
|
|
}
|
|
|
|
return work;
|
|
}
|
|
|
|
protected List<Location> getAllLivingWitnesses() {
|
|
return new ArrayList<>(livingWitnesses);
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a list of Unrevealed Locations adjacent to this one
|
|
*/
|
|
protected List<Location> getAdjacentUnrevealedSquares(Location loc) {
|
|
|
|
return getAdjacentUnrevealedSquares(loc, 1);
|
|
}
|
|
|
|
/**
|
|
* Return an Area of un-revealed Locations adjacent to this one
|
|
*/
|
|
protected Area getAdjacentUnrevealedArea(Location loc) {
|
|
|
|
return new Area(getAdjacentUnrevealedSquares(loc, 1));
|
|
}
|
|
|
|
|
|
/**
|
|
* Return a list of Unrevealed Locations adjacent to this one
|
|
*/
|
|
protected List<Location> getAdjacentUnrevealedSquares(Location loc, int size ) {
|
|
|
|
ArrayList<Location> work = new ArrayList<>();
|
|
|
|
for (Location a: getAdjacentSquaresIterable(loc, size)) {
|
|
if (isUnrevealed(a)) {
|
|
work.add(a);
|
|
}
|
|
}
|
|
|
|
return work;
|
|
}
|
|
|
|
/**
|
|
* Return the Location for this position, avoids having to instantiate one
|
|
*/
|
|
protected Location getLocation(int x, int y) {
|
|
if (x < 0 || x >= this.width || y < 0 || y >= this.height) {
|
|
return null;
|
|
} else {
|
|
return cache.getLocation(x, y);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* This returns the adjacent squares to a depth of size away. So (l,2) returns the 24 squares 2 deep surrounding l.
|
|
*/
|
|
protected Iterable<Location> getAdjacentSquaresIterable(Location loc, int size) {
|
|
if (size == 1) {
|
|
return getAdjacentSquaresIterable(loc);
|
|
} else {
|
|
return cache.adjacentLocations2[loc.x][loc.y];
|
|
}
|
|
|
|
}
|
|
|
|
protected Iterable<Location> getAdjacentSquaresIterable(Location loc) {
|
|
return cache.adjacentLocations1[loc.x][loc.y];
|
|
}
|
|
|
|
|
|
/**
|
|
* Done as part of validating Locations, must be undone using clearWitness()
|
|
*/
|
|
protected void setWitnessValue(Location l, int value) {
|
|
board[l.x][l.y] = value;
|
|
revealed[l.x][l.y] = true;
|
|
|
|
testMoveBalance++;
|
|
|
|
}
|
|
|
|
/**
|
|
* Done as part of validating Locations
|
|
*/
|
|
protected void clearWitness(Location l) {
|
|
board[l.x][l.y] = 0;
|
|
revealed[l.x][l.y] = false;
|
|
|
|
testMoveBalance--;
|
|
|
|
}
|
|
|
|
/**
|
|
* Method to read our own array of reveal values.
|
|
*/
|
|
protected int getWitnessValue(Location l) {
|
|
return getWitnessValue(l.x, l.y);
|
|
}
|
|
|
|
/**
|
|
* Method to read our own array of reveal values.
|
|
*/
|
|
protected int getWitnessValue(int x, int y) {
|
|
if (isUnrevealed(x,y)) {
|
|
throw new RuntimeException("Trying to get a witness value for an unrevealed square");
|
|
}
|
|
return board[x][y];
|
|
}
|
|
|
|
/**
|
|
* indicates a flag is on the board, but the solver can't confirm it
|
|
*/
|
|
protected boolean isUnconfirmedFlag(Location l) {
|
|
return isUnconfirmedFlag(l.x, l.y);
|
|
}
|
|
|
|
/**
|
|
* indicates a flag is on the board, but the solver can't confirm it
|
|
*/
|
|
protected boolean isUnconfirmedFlag(int x, int y) {
|
|
return flagOnBoard[x][y] && !confirmedMine[x][y];
|
|
}
|
|
|
|
/**
|
|
* indicates a flag is on the board
|
|
*/
|
|
protected boolean isFlagOnBoard(Location l) {
|
|
return isFlagOnBoard(l.x, l.y);
|
|
}
|
|
|
|
/**
|
|
* indicates a flag is on the board
|
|
*/
|
|
protected boolean isFlagOnBoard(int x, int y) {
|
|
return flagOnBoard[x][y];
|
|
}
|
|
|
|
|
|
protected void setFlagOnBoard(Location l) {
|
|
setFlagOnBoard(l.x, l.y);
|
|
}
|
|
|
|
protected void setFlagOnBoard(int x, int y) {
|
|
flagOnBoard[x][y] = true;
|
|
}
|
|
|
|
protected void setMineFound(Location loc) {
|
|
|
|
if (isConfirmedMine(loc)) {
|
|
return;
|
|
}
|
|
|
|
confirmedMinesTotal++;
|
|
confirmedMine[loc.x][loc.y] = true;
|
|
|
|
// if the flag isn't already on the board then this is also another on the total of all flags
|
|
if (!flagOnBoard[loc.x][loc.y]) {
|
|
totalFlags++;
|
|
}
|
|
|
|
// let all the adjacent squares know they have one more flag next to them and one less unrevealed location
|
|
for (Location a: getAdjacentSquaresIterable(loc)) {
|
|
adjFlagsConfirmed[a.x][a.y]++;
|
|
adjUnrevealed[a.x][a.y]--;
|
|
}
|
|
|
|
}
|
|
|
|
protected void unsetMineFound(Location loc) {
|
|
|
|
if (!isConfirmedMine(loc)) {
|
|
return;
|
|
}
|
|
|
|
confirmedMinesTotal--;
|
|
confirmedMine[loc.x][loc.y] = false;
|
|
|
|
|
|
// if the flag isn't already on the board then this is also another on the total of all flags
|
|
if (!flagOnBoard[loc.x][loc.y]) {
|
|
totalFlags--;
|
|
}
|
|
|
|
// let all the adjacent squares know they have one less mine next to them and one more unrevealed location
|
|
for (Location a: getAdjacentSquaresIterable(loc)) {
|
|
adjFlagsConfirmed[a.x][a.y]--;
|
|
adjUnrevealed[a.x][a.y]++;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Since Flag Free is a thing, we can't rely on the GameState to tell us where the flags are,
|
|
* so this replaces that.
|
|
*/
|
|
protected boolean isConfirmedMine(Location l) {
|
|
return isConfirmedMine(l.x, l.y);
|
|
}
|
|
|
|
/**
|
|
* Since Flag Free is a thing, we can't rely on the GameState to tell us where the flags are,
|
|
* so this replaces that.
|
|
*/
|
|
protected boolean isConfirmedMine(int x, int y) {
|
|
return confirmedMine[x][y];
|
|
}
|
|
|
|
/**
|
|
* Returns whether the location is revealed
|
|
*/
|
|
protected boolean isRevealed(Location l) {
|
|
return isRevealed(l.x, l.y);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the location is revealed
|
|
*/
|
|
protected boolean isRevealed(int x, int y) {
|
|
return revealed[x][y];
|
|
}
|
|
|
|
/**
|
|
* Returns whether the location is unrevealed (neither revealed nor a confirmed flag)
|
|
*/
|
|
protected boolean isUnrevealed(Location l) {
|
|
return isUnrevealed(l.x, l.y);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the location is unrevealed (neither revealed nor a confirmed flag)
|
|
*/
|
|
protected boolean isUnrevealed(int x, int y) {
|
|
return !confirmedMine[x][y] && !revealed[x][y];
|
|
}
|
|
|
|
/**
|
|
* count how many confirmed flags are adjacent to this square
|
|
*/
|
|
protected int countAdjacentConfirmedFlags(Location l) {
|
|
return countAdjacentConfirmedFlags(l.x, l.y);
|
|
}
|
|
|
|
/**
|
|
* count how many confirmed flags are adjacent to this square
|
|
*/
|
|
protected int countAdjacentConfirmedFlags(int x, int y) {
|
|
|
|
return adjFlagsConfirmed[x][y];
|
|
|
|
}
|
|
|
|
/**
|
|
* count how many flags are adjacent to this square on the board
|
|
*/
|
|
protected int countAdjacentFlagsOnBoard(Location l) {
|
|
return countAdjacentFlagsOnBoard(l.x, l.y);
|
|
}
|
|
|
|
/**
|
|
* count how many flags are adjacent to this square on the board
|
|
*/
|
|
protected int countAdjacentFlagsOnBoard(int x, int y) {
|
|
|
|
return adjFlagsOnBoard[x][y];
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
* count how many confirmed and unconfirmed flags are adjacent to this square
|
|
*/
|
|
protected int countAllFlags(int x, int y) {
|
|
|
|
int result = 0;
|
|
|
|
for (int i=0; i < DX.length; i++) {
|
|
if (x + DX[i] >= 0 && x + DX[i] < width && y + DY[i] >= 0 && y + DY[i] < height) {
|
|
if (confirmedMine[x + DX[i]][y + DY[i]] || flagOnBoard[x + DX[i]][y + DY[i]]) {
|
|
result++;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
/**
|
|
* count how many adjacent squares are neither flags nor revealed
|
|
*/
|
|
protected int countAdjacentUnrevealed(Location l) {
|
|
|
|
return countAdjacentUnrevealed(l.x, l.y);
|
|
|
|
}
|
|
|
|
/**
|
|
* count how many adjacent squares are neither confirmed flags nor revealed
|
|
*/
|
|
protected int countAdjacentUnrevealed(int x, int y) {
|
|
|
|
return adjUnrevealed[x][y];
|
|
|
|
}
|
|
|
|
/**
|
|
* count how many squares are neither confirmed flags nor revealed
|
|
*/
|
|
protected int getTotalUnrevealedCount() {
|
|
|
|
return numOfHidden;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the number of mines the solver knows the location of
|
|
*/
|
|
protected int getConfirmedMineCount() {
|
|
|
|
return confirmedMinesTotal;
|
|
|
|
}
|
|
|
|
/**
|
|
* Number of flags on the board both confirmed and unconfirmed
|
|
*/
|
|
protected int getTotalFlagCount() {
|
|
return this.totalFlags;
|
|
}
|
|
|
|
protected int getUnplayedMoves(MoveMethod method) {
|
|
return unplayedMoves[method.ordinal()];
|
|
}
|
|
|
|
|
|
// check for flags which can be determined to be wrong
|
|
protected boolean validateData() {
|
|
|
|
for (int i=0; i < width; i++) {
|
|
for (int j=0; j < height; j++) {
|
|
|
|
// if there is an unconfirmed flag on the board but the solver
|
|
// thinks it is clear then the flag is wrong
|
|
if (isUnconfirmedFlag(i,j) && action[i][j] != null && action[i][j].getAction() == Action.CLEAR) {
|
|
getLogger().log(Level.INFO, "Flag in Error at (%d, %d) confirmed CLEAR", i, j);
|
|
return false;
|
|
}
|
|
|
|
if (isRevealed(i,j) && getWitnessValue(i,j) != 0) {
|
|
int flags = countAllFlags(i,j);
|
|
|
|
// if we have too many flags by a revealed square then a mistake has been made
|
|
if (getWitnessValue(i,j) < flags) {
|
|
getLogger().log(Level.INFO, "Flag in Error at witness (%d, %d) Overloads: Flags %d" ,i, j, flags);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns true if the board is consider high mine density
|
|
* @return
|
|
*/
|
|
public boolean isHighDensity() {
|
|
|
|
int minesLeft = getMines() - getConfirmedMineCount();
|
|
int tilesLeft = getTotalUnrevealedCount();
|
|
|
|
return (minesLeft * 5 > tilesLeft * 2) && Solver.CONSIDER_HIGH_DENSITY_STRATEGY;
|
|
}
|
|
|
|
|
|
public int getTestMoveBalance() {
|
|
return this.testMoveBalance;
|
|
}
|
|
|
|
protected Logger getLogger() {
|
|
return solver.logger;
|
|
}
|
|
|
|
//protected void display(String text) {
|
|
// solver.display(text);
|
|
//}
|
|
|
|
protected Solver getSolver() {
|
|
return solver;
|
|
}
|
|
|
|
}
|