Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnreachableWhiteCellContradictionRule Pathfinding #321

Merged
merged 12 commits into from
Oct 21, 2022
122 changes: 65 additions & 57 deletions src/main/java/edu/rpi/legup/puzzle/nurikabe/NurikabeUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import edu.rpi.legup.utility.DisjointSets;

import java.awt.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

public class NurikabeUtilities {
Expand Down Expand Up @@ -165,72 +166,79 @@ public static DisjointSets<NurikabeCell> getPossibleWhiteRegions(NurikabeBoard b
}

/**
* Gets a list of flood filled white regions with remaining white cells
* Makes a map where the keys are white/numbered cells
* and the values are the amount of cells that need
* to be added to the region
*
* @param board nurikabe board
* @return a list of flood filled white regions
* @return a map of cell keys to integer values
*/
public static ArrayList<Set<NurikabeCell>> getFloodFillWhite(NurikabeBoard board) {
public static HashMap<NurikabeCell,Integer> getWhiteRegionMap(NurikabeBoard board) {
int width = board.getWidth();
int height = board.getHeight();

DisjointSets<NurikabeCell> whiteRegions = new DisjointSets<>();
for (PuzzleElement data : board.getPuzzleElements()) {
NurikabeCell cell = (NurikabeCell) data;
whiteRegions.createSet(cell);
}

for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
NurikabeCell cell = board.getCell(x, y);
NurikabeCell rightCell = board.getCell(x + 1, y);
NurikabeCell downCell = board.getCell(x, y + 1);
if (cell.getType() == NurikabeType.WHITE || cell.getType() == NurikabeType.NUMBER) {
if (rightCell != null && (rightCell.getType() == NurikabeType.WHITE ||
rightCell.getType() == NurikabeType.NUMBER)) {
whiteRegions.union(cell, rightCell);
}
if (downCell != null && (downCell.getType() == NurikabeType.WHITE ||
downCell.getType() == NurikabeType.NUMBER)) {
whiteRegions.union(cell, downCell);
}
}
}
}

Set<NurikabeCell> numberedCells = getNurikabeNumberedCells(board);
ArrayList<Set<NurikabeCell>> floodFilledRegions = new ArrayList<>();
for (NurikabeCell numberCell : numberedCells) {
int number = numberCell.getData();
Set<NurikabeCell> region = whiteRegions.getSet(numberCell);
floodFilledRegions.add(region);

int flood = number - region.size();
for (int i = 0; i < flood; i++) {
Set<NurikabeCell> newSet = new HashSet<>();
for (NurikabeCell c : region) {
Point loc = c.getLocation();
NurikabeCell upCell = board.getCell(loc.x, loc.y - 1);
NurikabeCell rightCell = board.getCell(loc.x + 1, loc.y);
NurikabeCell downCell = board.getCell(loc.x, loc.y + 1);
NurikabeCell leftCell = board.getCell(loc.x - 1, loc.y);
if (upCell != null) {
newSet.add(upCell);
}
if (rightCell != null) {
newSet.add(rightCell);
}
if (downCell != null) {
newSet.add(downCell);
}
if (leftCell != null) {
newSet.add(leftCell);
// Final mapping of cell to size
HashMap<NurikabeCell,Integer> whiteRegionMap = new HashMap<>();
for (NurikabeCell center: numberedCells) {
//BFS for each center to find the size of the region
int size = 1;
// Mark all the vertices as not visited(By default
// set as false)
HashMap<NurikabeCell,Boolean> visited= new HashMap<>();

// Create a queue for BFS
LinkedList<NurikabeCell> queue = new LinkedList<>();

// Mark the current node as visited and enqueue it
visited.put(center,true);
queue.add(center);

// Set of cells in the current region
Set<NurikabeCell> connected = new HashSet<>();

while (queue.size() != 0) {
// Dequeue a vertex from queue and print it
// s is the source node in the graph
NurikabeCell s = queue.poll();
System.out.print(s+" ");

// Make a linked list of all adjacent squares
Set<NurikabeCell> adj = new HashSet<>();

Point loc = s.getLocation();
// First check if the side is on the board
if (loc.x >= 1) {
adj.add(board.getCell(loc.x-1, loc.y));
}
if (loc.x < width-1) {
adj.add(board.getCell(loc.x+1, loc.y));
}
if (loc.y >= 1) {
adj.add(board.getCell(loc.x, loc.y-1));
}
if (loc.y < height-1) {
adj.add(board.getCell(loc.x, loc.y+1));
}
// Get all adjacent vertices of the dequeued vertex s
// If a adjacent has not been visited, then mark it
// visited and enqueue it
for (NurikabeCell n : adj) {
if (!visited.getOrDefault(n,false)
&& n.getType() == NurikabeType.WHITE) {
connected.add(n);
visited.put(n,true);
queue.add(n);
++size;
}
}
region.addAll(newSet);
}
// Map the cells to the center-size (including the center)
whiteRegionMap.put(center,center.getData()-size);
for (NurikabeCell member : connected) {
whiteRegionMap.put(member,center.getData()-size);
}
}

return floodFilledRegions;
return whiteRegionMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,12 @@ protected String checkRuleRawAt(TreeTransition transition, PuzzleElement puzzleE
NurikabeBoard origBoardState = (NurikabeBoard) transition.getParents().get(0).getBoard();
NurikabeBoard modified = origBoardState.copy();

for (int i = 0; i < modified.getWidth(); i++) {
for (int j = 0; j < modified.getHeight(); j++) {
NurikabeCell currentCell = modified.getCell(i, j);
if (currentCell.getType() == NurikabeType.WHITE) {
currentCell.setData(NurikabeType.UNKNOWN.toValue());
}
}
}
NurikabeCell modifiedCell = (NurikabeCell) modified.getPuzzleElement(puzzleElement);
modifiedCell.setData(NurikabeType.WHITE.toValue());
if (contraRule.checkContradiction(modified) == null) {
if (contraRule.checkContradictionAt(modified,modifiedCell) == null) {
return null;
}
return super.getInvalidUseOfRuleMessage() + ": This is not the only way for black to escape!";
return super.getInvalidUseOfRuleMessage() + ": Cell at this index can be reached";
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import edu.rpi.legup.puzzle.nurikabe.NurikabeType;
import edu.rpi.legup.puzzle.nurikabe.NurikabeUtilities;

import java.util.ArrayList;
import java.util.Set;
import java.awt.*;
import java.util.*;

public class UnreachableWhiteCellContradictionRule extends ContradictionRule {

Expand Down Expand Up @@ -40,14 +40,63 @@ public String checkContradictionAt(Board board, PuzzleElement puzzleElement) {
return super.getInvalidUseOfRuleMessage() + ": " + this.INVALID_USE_MESSAGE;
}

ArrayList<Set<NurikabeCell>> regions = NurikabeUtilities.getFloodFillWhite(nurikabeBoard);
int height = nurikabeBoard.getHeight();
int width = nurikabeBoard.getWidth();

for (Set<NurikabeCell> region : regions) {
for (NurikabeCell c : region) {
if (c == cell) {
return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE;
// Get regions
HashMap<NurikabeCell,Integer> whiteRegionMap = NurikabeUtilities.getWhiteRegionMap(nurikabeBoard);
if (whiteRegionMap.containsKey(cell)) {
return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE;
}
// BFS to a region

// Create a queue for BFS
LinkedList<NurikabeCell> queue = new LinkedList<>();

// Mark the current node as visited and enqueue it
HashMap<NurikabeCell,Boolean> visited= new HashMap<>();
visited.put(cell,true);
queue.add(cell);
int pathLength = 1;
while (queue.size() != 0) {
// Set of adjacent squares
Set<NurikabeCell> adj = new HashSet<>();
while (queue.size() != 0) {
// Dequeue a vertex from queue and print it
NurikabeCell s = queue.poll();

Point loc = s.getLocation();
// First check if the side is on the board
if (loc.x >= 1) {
adj.add(nurikabeBoard.getCell(loc.x-1, loc.y));
}
if (loc.x < width-1) {
adj.add(nurikabeBoard.getCell(loc.x+1, loc.y));
}
if (loc.y >= 1) {
adj.add(nurikabeBoard.getCell(loc.x, loc.y-1));
}
if (loc.y < height-1) {
adj.add(nurikabeBoard.getCell(loc.x, loc.y+1));
}

for (NurikabeCell n :adj) {
int regionNeed = whiteRegionMap.getOrDefault(n,-1);
if (pathLength <= regionNeed) {
return super.getNoContradictionMessage() + ": " + this.NO_CONTRADICTION_MESSAGE;
}
}
}

for (NurikabeCell n : adj) {
if (!visited.getOrDefault(n,false)
&& (n.getType() == NurikabeType.UNKNOWN ||
n.getType() == NurikabeType.WHITE)) {
visited.put(n,true);
queue.add(n);
}
}
++pathLength;
}

return null;
Expand Down