Following this question here, I have ran into some problems, I have used this DFS algorithm to search through the 2d array, but when I try to run it It gets the first row and it crashes, it goes to as far as here in my utility class, The algorithm should store the state if its correct, and then go to a next row, but It seems that the array is getting deleted after first search.
int r = p[0];
int c = p[1];
System.out.println("expanding game state for node at (" + r + ", " + c + ")");
Then i Get the
java.lang.NullPointerException: Attempt to read from null array
error.
Here is the Main search()
function InitializeEasy()
with the code to generate the array
public int[][] debug_board_state_easy = new int[4][4];
void search(){
Map<Point, List<Direction>> remainingOptions = new HashMap<>();
Stack<Land> gameTree = new Stack<>();
gameTree.push(new Land(STARTING_CLUES));
while(true){
Land state = gameTree.peek();
int[] p = state.lowestTodo();
if (p == null)
System.out.println("solution found");
// move to next game state
int r = p[0];
int c = p[1];
System.out.println("expanding game state for node at (" + r + ", " + c + ")");
List<Direction> ds = null;
if(remainingOptions.containsKey(new Point(r,c)))
ds = remainingOptions.get(new Point(r,c));
else{
ds = new ArrayList<>();
for(Direction dir : Direction.values()) {
int[] tmp = state.nextIsland(r, c, dir);
if(tmp == null)
continue;
if(state.canBuildBridge(r,c,tmp[0], tmp[1]))
ds.add(dir);
}
remainingOptions.put(new Point(r,c), ds);
}
// if the node can no longer be expanded, and backtracking is not possible we quit
if(ds.isEmpty() && gameTree.isEmpty()){
System.out.println("no valid configuration found");
return;
}
// if the node can no longer be expanded, we need to backtrack
if(ds.isEmpty()){
gameTree.pop();
remainingOptions.remove(new Point(r,c));
System.out.println("going back to previous decision");
continue;
}
Direction dir = ds.remove(0);
System.out.println("connecting " + dir.name());
remainingOptions.put(new Point(r,c), ds);
Land nextState = new Land(state);
int[] tmp = state.nextIsland(r,c,dir);
nextState.connect(r,c, tmp[0], tmp[1]);
gameTree.push(nextState);
}
}
private void InitializeEasy() {
Random rand = new Random();
setCurrentState(new State(WIDTH_EASY));
for (int row = 0; row < debug_board_state_easy.length; row++) {
for (int column = 0; column < debug_board_state_easy[row].length; column++) {
debug_board_state_easy[row][column] = Integer.valueOf(rand.nextInt(5));
}
}
for (int row = 0; row < debug_board_state_easy.length; row++) {
for (int column = 0; column < debug_board_state_easy[row].length; column++) {
System.out.print(debug_board_state_easy[row][column] + " ");
}
System.out.println(debug_board_state_easy);
this.search();
}
this.search();
for (int row = 0; row < WIDTH_EASY; ++row) {
for (int column = 0; column < WIDTH_EASY; ++column) {
// System.out.println();
getCurrentState().board_elements[row][column] = new BoardElement();
getCurrentState().board_elements[row][column].max_connecting_bridges = Integer.valueOf(debug_board_state_easy[row][column]);
getCurrentState().board_elements[row][column].row = row;
getCurrentState().board_elements[row][column].col = column;
if (getCurrentState().board_elements[row][column].max_connecting_bridges > 0) {
getCurrentState().board_elements[row][column].is_island = true;
}
}
}
}
Land.Java - Utility class
public class Land {
private int[][] BRIDGES_TO_BUILD;
private boolean[][] IS_ISLAND;
private Direction[][] BRIDGES_ALREADY_BUILT;
public Land(int[][] bridgesToDo){
BRIDGES_TO_BUILD = copy(bridgesToDo);
int R = bridgesToDo.length;
int C = bridgesToDo[0].length;
BRIDGES_ALREADY_BUILT = new Direction[R][C];
IS_ISLAND = new boolean[R][C];
for(int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
BRIDGES_ALREADY_BUILT[i][j] = null;
IS_ISLAND[i][j] = bridgesToDo[i][j] > 0;
}
}
}
public Land(Land other){
BRIDGES_TO_BUILD = copy(other.BRIDGES_TO_BUILD);
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
BRIDGES_ALREADY_BUILT = new Direction[R][C];
IS_ISLAND = new boolean[R][C];
for(int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
BRIDGES_ALREADY_BUILT[i][j] = other.BRIDGES_ALREADY_BUILT[i][j];
IS_ISLAND[i][j] = other.IS_ISLAND[i][j];
}
}
}
public int[] next(int r, int c, Path.Direction dir){
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
// out of bounds
if(r < 0 || r >=R || c < 0 || c >= C)
return null;
// motion vectors
int[][] motionVector = {{-1, 0},{0,1},{1,0},{0,-1}};
int i = Arrays.asList(values()).indexOf(dir);
// calculate next
int[] out = new int[]{r + motionVector[i][0], c + motionVector[i][1]};
r = out[0];
c = out[1];
// out of bounds
if(r < 0 || r >=R || c < 0 || c >= C)
return null;
// return
return out;
}
public int[] nextIsland(int r, int c, Path.Direction dir){
int[] tmp = next(r,c,dir);
if(tmp == null)
return null;
while(!IS_ISLAND[tmp[0]][tmp[1]]){
tmp = next(tmp[0], tmp[1], dir);
if(tmp == null)
return null;
}
return tmp;
}
public boolean canBuildBridge(int r0, int c0, int r1, int c1){
if(r0 == r1 && c0 > c1){
return canBuildBridge(r0, c1, r1, c0);
}
if(c0 == c1 && r0 > r1){
return canBuildBridge(r1, c0, r0, c1);
}
if(r0 == r1){
int[] tmp = nextIsland(r0, c0, CW);
if(tmp[0] != r1 || tmp[1] != c1)
return false;
if(BRIDGES_TO_BUILD[r0][c0] == 0)
return false;
if(BRIDGES_TO_BUILD[r1][c1] == 0)
return false;
for (int i = c0; i <= c1 ; i++) {
if(IS_ISLAND[r0][i])
continue;
if(BRIDGES_ALREADY_BUILT[r0][i] == Direction.NORTH)
return false;
}
}
if(c0 == c1){
int[] tmp = nextIsland(r0, c0, CCW);
if(tmp[0] != r1 || tmp[1] != c1)
return false;
if(BRIDGES_TO_BUILD[r0][c0] == 0 || BRIDGES_TO_BUILD[r1][c1] == 0)
return false;
for (int i = r0; i <= r1 ; i++) {
if(IS_ISLAND[i][c0])
continue;
if(BRIDGES_ALREADY_BUILT[i][c0] == Direction.SOUTH) {
return false;
}
}
}
// default
return true;
}
public int[] lowestTodo(){
int R = BRIDGES_TO_BUILD.length;
int C = BRIDGES_TO_BUILD[0].length;
int[] out = {0, 0};
for (int i=0;i<R;i++) {
for (int j = 0; j < C; j++) {
if(BRIDGES_TO_BUILD[i][j] == 0)
continue;
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0)
out = new int[]{i, j};
if (BRIDGES_TO_BUILD[i][j] < BRIDGES_TO_BUILD[out[0]][out[1]])
out = new int[]{i, j};
}
}
if (BRIDGES_TO_BUILD[out[0]][out[1]] == 0) {
return null;
}
return out;
}
@TargetApi(Build.VERSION_CODES.GINGERBREAD)
private int[][] copy(int[][] other){
int[][] out = new int[other.length][other.length == 0 ? 0 : other[0].length];
for(int r=0;r<other.length;r++)
out[r] = Arrays.copyOf(other[r], other[r].length);
return out;
}
public void connect(int r0, int c0, int r1, int c1){
if(r0 == r1 && c0 > c1){
connect(r0, c1, r1, c0);
return;
}
if(c0 == c1 && r0 > r1){
connect(r1, c0, r0, c1);
return;
}
if(!canBuildBridge(r0, c0, r1, c1))
return;
BRIDGES_TO_BUILD[r0][c0]--;
BRIDGES_TO_BUILD[r1][c1]--;
if(r0 == r1){
for (int i = c0; i <= c1 ; i++) {
if(IS_ISLAND[r0][i])
continue;
BRIDGES_ALREADY_BUILT[r0][i] = Direction.EAST;
}
}
if(c0 == c1){
for (int i = r0; i <= r1 ; i++) {
if(IS_ISLAND[i][c0])
continue;
BRIDGES_ALREADY_BUILT[i][c0] = Direction.NORTH;
}
}
}
}