I have to write a program that will read a picture and then print out the number of blocks inside it.
I have to read the picture as a binary matrix of the size r × c (number of rows times number of
columns).
The blocks are groups of one or more adjacent elements with the value 1.
Blocks are built exclusively of elements with value 1
Each element with value 1 is a part of some block
Adjacent elements with value 1 belong to the same block.
We only take into account the horizontal and vertical adjacency but not diagonal.
INPUT:
In the first line of the input we have the integers r and c, separated with one space.
Then we have the r lines, where each contains s 0's and 1's.
The numbers inside the individual lines are NOT separated by spaces.
The OUTPUT only print the number of blocks in the picture.
For example:
EXAMPLE 1
INPUT:
7 5
01000
00010
00000
10000
01000
00001
00100
OUTPUT:
6
EXAMPLE 2:
INPUT:
25 20
00010000000000000000
00010000000000000000
00010000000000000100
00000000000000000100
00011111111000000100
00000000000000000100
00000000000000000100
00000000000000000100
00000000000000000100
01111111111000000100
00000000000000000100
00000000000000100100
00000000000000100100
00000000000000100100
01000000000000100000
01000000000000100000
01000000000000100000
01000000000000100000
00000000000000100000
00000000000000000000
00000000000000000000
00000000000000000000
00000000000000000000
00011111111111100000
00000000000000000000
OUTPUT:
7
THE PROBLEM:
The problem that I have is that my program only works for inputs such as in example 1.
So pictures that only consist of blocks of size 1. But it doesnt work if there are multiples 1's in a picture, such as EXAMPLE 2.
In example 2 where the output should be 7(Blocks are elements of 1.They can either be vertial or horizontal).... my programs output is 30.
I don't know how to adjust the program in a correct manner so it will give me the correct input.
Thank you for your help in advance, here is my code that I am posting bellow.
import java.util.Scanner;
class Blocks{
public static void main(String[] args){
Scanner sc=new Scanner(System.in);
int rowNum=sc.nextInt();
int columnNum=sc.nextInt();
char[][] matrix = new char[rowNum][columnNum];
int nbrOfBlocks = 0;
for (int a = 0; a < rowNum; a++) {
matrix[a] = sc.next().toCharArray();
int index = 0;
while (index < matrix[a].length) {
if (matrix[a][index] == '1') {
++nbrOfBlocks;
while (index < matrix[a].length && matrix[a][index] == '1') {
++index;
}
}
++index;
}
}
System.out.println(nbrOfBlocks);
}
}
EDIT: Ok, here is a solution that will work for complex shapes
public class BlockCounter {
public static void main(String[] args) {
Board board = null;
try {
board = new Board("in3.txt");
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
System.out.println("Block count: " + board.getBlockCount());
}
}
class Board {
ArrayList<String> data = new ArrayList<>();
boolean[][] used;
int colCount = 0;
public Board(String filename) throws FileNotFoundException, IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = br.readLine()) != null) {
data.add(line);
colCount = Math.max(colCount, line.length());
}
}
}
public int getBlockCount() {
used = new boolean[data.size()][colCount];
int count = 0;
for (int row = 0; row < data.size(); row++)
for (int col = 0; col < colCount; col++)
used[row][col] = peek(row, col) == '1';
for (int row = 0; row < data.size(); row++)
for (int col = 0; col < colCount; col++)
if (used[row][col]) {
fill(row, col);
count++;
}
used = null;
return count;
}
public char peek(int row, int col) {
if (row < 0 || row >= data.size() || col < 0)
return '0';
String rowData = data.get(row);
if (col >= rowData.length())
return '0';
return rowData.charAt(col);
}
public void fill(int row, int col) {
if (used[row][col]) {
used[row][col] = false;
if (row > 0 && used[row - 1][col])
fill(row - 1, col);
if (col > 0 && used[row][col - 1])
fill(row, col - 1);
if (col < colCount - 1 && used[row][col + 1])
fill(row, col + 1);
if (row < data.size() - 1 && used[row + 1][col])
fill(row + 1, col);
}
}
public int getRowCount() {
return data.size();
}
public int getColCount() {
return colCount;
}
}
Explanation:
When Board.getBlockCount() is called if creates a temporary array of booleans to work with so the original board is not messed up. Then it searches the entire board for "trues" (which correspond to '1's on the board). Every time a "true" is found, a flood fill algorithm clears the entire shape to which it is connected.
If you need more performance and less memory usage (specially stack) for larger boards, you can use another flood fill algorithm like in the example that follows. The big advantage here is that it doesn't use the stack for every pixel like the one above. It is considerably more complex though.
public class BlockCounter2 {
public static void main(String[] args) {
Board2 board = null;
try {
board = new Board2("in4.txt");
} catch (Exception e) {
e.printStackTrace();
System.exit(0);
}
System.out.println("Block count: " + board.getBlockCount());
}
}
class Board2 {
ArrayList<String> data = new ArrayList<>();
boolean[][] used;
Deque<Point> pointStack = new LinkedList<>();
int colCount = 0;
public Board2(String filename) throws FileNotFoundException, IOException {
try (BufferedReader br = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = br.readLine()) != null) {
data.add(line);
colCount = Math.max(colCount, line.length());
}
}
}
public int getBlockCount() {
used = new boolean[data.size()][colCount];
int count = 0;
for (int row = 0; row < data.size(); row++)
for (int col = 0; col < colCount; col++)
used[row][col] = peek(row, col) == '1';
for (int row = 0; row < data.size(); row++)
for (int col = 0; col < colCount; col++)
if (used[row][col]) {
fill(row, col);
count++;
}
used = null;
return count;
}
public char peek(int row, int col) {
if (row < 0 || row >= data.size() || col < 0)
return '0';
String rowData = data.get(row);
if (col >= rowData.length())
return '0';
return rowData.charAt(col);
}
public void fill(int row, int col) {
pointStack.push(new Point(col, row));
Point p;
while (pointStack.size() > 0) {
p = pointStack.pop();
fillRow(p.y, p.x);
}
}
private void checkRow(int row, int col, int minCol, int maxCol) {
boolean uu = false;
for (int x = col; x < maxCol; x++) {
if (!uu && used[row][x])
pointStack.add(new Point(x, row));
uu = used[row][x];
}
uu = true;
for (int x = col; x > minCol; x--) {
if (!uu && used[row][x])
pointStack.add(new Point(x, row));
uu = used[row][x];
}
}
private void fillRow(int row, int col) {
int lx, rx;
if (used[row][col]) {
for (rx = col; rx < colCount; rx++)
if (used[row][rx])
used[row][rx] = false;
else
break;
for (lx = col - 1; lx >= 0; lx--)
if (used[row][lx])
used[row][lx] = false;
else
break;
if (row > 0)
checkRow(row - 1, col, lx, rx);
if (row < data.size() - 1)
checkRow(row + 1, col, lx, rx);
}
}
public int getRowCount() {
return data.size();
}
public int getColCount() {
return colCount;
}
}
EDIT2: Both solutions were made using input from txt files in order to make the debugging and testing easier for larger arrays. If you need them to work with user input (the same you have in your code) as well, just make the following changes:
Change the main method so it will listen from user input (again):
public static void main(String[] args) {
Scanner sc=new Scanner(System.in);
int rowNum=sc.nextInt();
int columnNum=sc.nextInt(); // Note columnNum is not necessary
String[] matrix = new String[rowNum]; // I hope char[][] is not a requirement
for (int a = 0; a < rowNum; a++) // Read array data from user input
matrix[a] = sc.next();
sc.close();
Board2 board = new Board2(matrix); // Call the new constructor
System.out.println("Block count: " + board.getBlockCount());
}
Add a new constructor to Board2, that takes a String[] as input:
public Board2(String[] data) {
for (String line : data) {
this.data.add(line);
colCount = Math.max(colCount, line.length());
}
}
You may remove the previous constructor Board2(String filename) if it is not useful for you but it's not necessary.
Are you searching for this:
import java.util.Scanner;
class Blocks {
public static void removeBlock(char[][] matrix, int posX, int posY) {
if(0 <= posX && posX < matrix.length
&& 0 <= posY && posY < matrix[posX].length) {
if(matrix[posX][posY] == '0') {
return;
}
matrix[posX][posY] = '0';
} else {
return;
}
removeBlock(matrix, posX - 1, posY);
removeBlock(matrix, posX + 1, posY);
removeBlock(matrix, posX, posY - 1);
removeBlock(matrix, posX, posY + 1);
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// read in
char[][] matrix = new char[sc.nextInt()][sc.nextInt()];
for(int a = 0; a < matrix.length; a++) {
matrix[a] = sc.next().toCharArray();
}
// calculate number of blocks
int nrBlocks = 0;
for(int i = 0; i < matrix.length; i++) {
for(int j = 0; j < matrix[i].length; j++) {
if(matrix[i][j] == '1') {
// we have found a block, so increment number of blocks
nrBlocks += 1;
// remove any 1's of the block from the array, so that they each block is not counted multiple times
removeBlock(matrix, i, j);
}
}
}
System.out.println(nrBlocks);
}
}
There's a linear time (in number of cells) solution to this. If I get time, I'll add the code to this answer, but if not the Wikipedia article (see EDIT below) gives pseudo code.
The idea is to scan line-by-line, assigning an incrementing unique run numbers to each runs of 1s we see (and changing the cell content to be that unique number) If in any run, the cells immediately above in the previous line were also 1 (that is, they now have a run number), then we know that their unique-run-number and the current unique-run number form part of a single block. So record that the run-above-run-number, and the current-run-number are equivalent (but don't bother to change anything on the board)
At the end, we have a count of the runs we've seen. and a set of equivalence relationships of unique-run-numbers. For each set of equivalent-numbers (say runs 3, 5, 14 form a block), we subtract from the run count the number of runs, minus 1 (in other words, replacing the multiple runs with a single block count).
I think I have some ideas to mitigate the worst case, but they're probably not worth it.
And that's the number of blocks.
The worst case for the equivalence classes is O(size of board) though (1-wide vertical blocks, with one space between them will do this). Fortunately, the entirely-black board will need only O(height of board) - each row will get one number, which will be marked equivalent with the next row.
EDIT: There's a Stackoverflow question about this already: can counting contiguous regions in a bitmap be improved over O(r * c)?
and it turns out I've just re-invented the two-pass "Connected Component Labelling" algorithm discussed on Wikipedia
Related
i have a file which has in it numbers that has to be inserted to 2d array but it doesn't works
here is my code
sorry for bad English
file
2,1 //starting point
3,2 //ending point
5,6 //array size which is maze
0,1,1,1,1,1 //from here till end its all gonna be inserted to 2d array
0,1,0,0,1,1 //and i somehow managed to take starting, ending point and arraysize
0,1,0,0,1,0 //all i have to do is fill the array
0,0,1,0,1,0
0,0,1,1,1,0
Complete Code
public class Mouse {
// global variables to take input from file
public static int startRow;
public static int startCol;
public static int endRow;
public static int endCol;
public static int arrayRow;
public static int arrayCol;
public static String arrayDetail;
public static void main(String[] args) throws IOException {
StringBuilder stringbuilder = new StringBuilder();
try {
BufferedReader input = new java.io.BufferedReader(new java.io.FileReader("src/input.txt"));
List<String> lines = new ArrayList<String>();
String[] stringArray = new String[lines.size()];
String line = null;
while ((line = input.readLine()) != null) {
lines = Arrays.asList(line.split("\\s*,\\s*"));
stringArray = lines.toArray(stringArray);
for (int i = 0; i < stringArray.length; i++) {
stringbuilder.append(stringArray[i]);
}
}
}
catch (FileNotFoundException e) {
System.err.println("File not found.");
}
System.out.println(stringbuilder);
//giving global variables a value that i need
startRow = Integer.parseInt(stringbuilder.substring(0, 1));
startCol = Integer.parseInt(stringbuilder.substring(1, 2));
endRow = Integer.parseInt(stringbuilder.substring(2, 3));
endCol = Integer.parseInt(stringbuilder.substring(3, 4));
arrayRow = Integer.parseInt(stringbuilder.substring(4, 5));
arrayCol = Integer.parseInt(stringbuilder.substring(5, 6));
arrayDetail = stringbuilder.substring(6);
//i cant give array values to the long variable because it's out of range
Home home = new Home();
home.print(); // print maze before releasing mouse
if (home.walk(startRow,startCol)) { //starting position
System.out.println("Ok"); //print okay if its successful
}
else {
System.out.println("No Solution"); //if unsuccessful
}
home.print(); // print maze to track how mouse went
}
}
class Home{
Home(){}
int[][] A = new int[Mouse.arrayRow][Mouse.arrayCol]; //giving array size
{
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < A[i].length; j++) {
for (int k = 0; k < Mouse.arrayDetail.length(); k++) {
//and its the problem doesn't take values
A[i][j] = Integer.parseInt(Mouse.arrayDetail.substring(k, k+1));
//and it gives me bunch of 000 it means its null and not taking values right ?
}
}
}
}
// road is 1
// wall is 0
// the roads that mouse went only one time is 2
// 3 is the road that mouse went but did not get success i mean the roads mouse went 2 times
public boolean walk(int row, int col) { //method to walk
boolean result = false;
if (check(row, col)){
A[row][col] = 3;
if (row == Mouse.endRow && col == Mouse.endCol) { //ending position
result = true;
}
else {
result = walk(row+1, col); //down
if(!result)
result = walk(row, col+1); //right
if(!result)
result = walk(row-1, col); //up
if(!result)
result = walk(row, col-1); //left
}
}
if (result == true) {
A[row][col] = 2;
}
return result;
}
public boolean check(int row, int col) { //check there is a road
boolean result = false;
if (row<A.length && row >=0 && col >=0 && col < A[0].length ) {
if (A[row][col] == 1) {
result = true;
}
}
return result;
}
public void print() { //method to print maze
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < A[i].length; j++) {
System.out.print(A[i][j]);
}
System.out.println();
}
}
}
output
000000
000000
000000
000000
000000
No Solution
000000
000000
000000
000000
000000
// It looks like your post is mostly code; please add some more details. so i am adding nonsense texts. It looks like your post is mostly code; please add some more details. so i am adding nonsense texts.
You can fill the 2d array A like this
int[][] A = new int[Mouse.arrayRow][Mouse.arrayCol]; //giving array size
{
for (int i = 0; i < A.length; i++) {
for (int j = 0; j < A[i].length; j++) {
A[i][j] = Mouse.arrayDetail.charAt(i * Mouse.arrayCol + j) - '0';
}
}
}
I am supposed to count how many # signs are connected to the original predetermined spot in a 2D array matrix. I am getting a stack overflow error and do not know why. Any suggestions are appreciated (Take a provided row and col location and count how many # signs connect to the original location. # signs are connected if they are connected up, down, left, and right of one another.)
Current Code:
import static java.lang.System.*;
public class AtCounter
{
private String[][] atMat;
private int totalCount = 0;
private boolean[][] visited; //used to see if location has been visited before.
public AtCounter(int rows, int cols)
{
//size the matrix
atMat = new String[rows][cols];
//use nested loops to randomly load the matrix
for(int r = 0; r < atMat.length; r++)
{
for(int c = 0; c < atMat[r].length; c++)
{
int num = (int) (Math.random() * 2);
if(num == 0)
atMat[r][c] = "#";
else
atMat[r][c] = "-";
}
}
//you will need to use Math.random()
visited = new boolean[atMat.length][atMat.length];
}
/**
* Used to find out if location is in the 2D Matrix.
*/
public boolean inBounds(int r, int c)
{
return ((r > -1 && r < atMat.length) && (c > -1 && c < atMat.length));
}
public int countAts(int r, int c)
{
//add in recursive code to count up the # of #s connected
//start checking at spot [r,c]
if(atMat[r][c].equals("-") || !inBounds(r,c))
return 0;
if(!visited[r][c])
{
if(atMat[r][c].equals("#"))
{
totalCount+=1;
if(inBounds(r - 1, c))//up
countAts(r - 1, c);
if(inBounds(r + 1, c))//down
countAts(r + 1, c);
if(inBounds(r, c + 1))//right
countAts(r , c + 1);
if(inBounds(r, c - 1))//left
countAts(r, c - 1);
}
}
return totalCount;
}
/*
*this method will return all values in the matrix
*this method should return a view of the matrix
*that looks like a matrix
*/
public String toString()
{
String grid = "";
for(String[] row : atMat)
{
for(String val : row)
{
grid += val + " ";
}
grid += "\n";
}
return grid;
}
}
You have several errors in your code:
You are created the visited arrays using atMat.length which will create an square array even if your original dimensions (rows and cols) aren't equal.
In the inBounds method you the column argument c against the length of the rows, not the length of the columns
In the countAts method:
The checking order of the invalid conditions needs to be reversed, you need to check first if it is valid position and later if the value in that cell is an #.
If the current cell hasn't been visited then the first thing is to marked as visited within the if block, to avoid falling in an endless recursion.
With all that said, a possible solution could be as follows:
import static java.lang.System.*;
public class AtCounter
{
private String[][] atMat;
private int totalCount = 0;
private boolean[][] visited; //used to see if location has been visited before.
private int rows; // To store rows length
private int cols; // To store cols length
public AtCounter(int rows, int cols)
{
//size of the matrix
this.rows = rows;
this.cols = cols;
atMat = new String[rows][cols];
//use nested loops to randomly load the matrix
for(int r = 0; r < rows; r++)
for(int c = 0; c < cols; c++) {
int num = (int) (Math.random() * 2);
if(num == 0)
atMat[r][c] = "#";
else
atMat[r][c] = "-";
}
visited = new boolean[rows][cols];
}
/**
* Used to find out if location is in the 2D Matrix.
*/
public boolean inBounds(int r, int c)
{
return r > -1 && r < rows && c > -1 && c < cols;
}
public int countAts(int r, int c)
{
//add in recursive code to count up the # of #s connected
//start checking at spot [r,c]
if(!inBounds(r,c) || atMat[r][c].equals("-")) // The order here matters
return 0;
if(!visited[r][c])
{
visited[r][c] = true; // Marks the current cell as visited
if(atMat[r][c].equals("#"))
{
totalCount+=1;
if(inBounds(r - 1, c))//up
countAts(r - 1, c);
if(inBounds(r + 1, c))//down
countAts(r + 1, c);
if(inBounds(r, c + 1))//right
countAts(r , c + 1);
if(inBounds(r, c - 1))//left
countAts(r, c - 1);
}
}
return totalCount;
}
}
Im rather new to java and have just started learning how to use 2d arrays. What I am trying to do is compare my different sums from rows and columns and the different diagonals to determine where the array is a true magic square. However, Right now, all I can do is print out my arrays. I keep getting the error that it cant find the variable I am trying to put in the parameters for colSUm and rowSum. Because of this, im not sure if my logic for sums is actually correct.
public class MagicSquare
{
private int[][] grid;
public MagicSquare(int[][] g)
{
grid=g;
}
/**
* find the sum of a given row
*/
public int rowSum(int row)
{
int sum;
for (row=0; row < grid.length; row++)
{
sum = 0;
for ( int col=0; col < grid[row].length; col++)
{
sum = sum + grid[row][col];
}
}
return sum;
}
/**
* find the sum of a given column
*/
public int colSum(int col)
{
int sum;
for (int i = 0; i < grid.length; i++)
{
sum = 0;
for (int j = 0; j < grid[i].length; j++)
{
sum += grid[j][i];
}
}
return sum;
}
/**
* returns the sum in the "up" diagonal (from the lower left to the upper right)
*/
public int upDiagSum()
{
int totalup = 0;
for (int row = 0; row < grid.length; row++)
{
totalup += grid[row][row];
}
return totalup;
}
/**
* determines if the sum all rows, columns, and main diagonals are equal
*/
public boolean isMagicSquare()
{
boolean isMagicSquare = false;
while(!isMagicSquare)
{
if (downDiagSum()!=upDiagSum() && rowSum(row) != colSum(i))
return false;
else
return true;
}
return isMagicSquare;
}
I didn't include the downDiag method in here because I did want to get repetitive. These are the 4 most important methods of my code.
I really don't understand what you are trying to do in the isMagicSquare function.This is the most straight forward logic I can think of
public class MagicSquare
{
private int[][] grid;
public MagicSquare(int[][] g)
{
grid = g;
}
/**
* find the sum of a given row
*/
public int rowSum(int row)
{
int sum;
sum = 0;
for (int col = 0; col < grid.length; col++)
{
sum = sum + grid[row][col];
}
return sum;
}
/**
* find the sum of a given column
*/
public int colSum(int col)
{
int sum;
sum = 0;
for (int row = 0; row < grid.length; row++)
{
sum += grid[row][col];
}
return sum;
}
/**
* returns the sum in the "up" diagonal (from the lower left to the upper
* right)
*/
public int upDiagSum()
{
int totalup = 0;
for (int row = 0; row < grid.length; row++)
{
totalup += grid[grid.length - row - 1][row];
}
return totalup;
}
public int downDiagSum()
{
int totalup = 0;
for (int row = 0; row < grid.length; row++)
{
totalup += grid[row][row];
}
return totalup;
}
/**
* determines if the sum all rows, columns, and main diagonals are equal
*/
public boolean isMagicSquare()
{
boolean isMagicSquare = true;
int upDiagSum = upDiagSum();
int downDiagSum = downDiagSum();
if (upDiagSum == downDiagSum)
{
for (int i = 0; i < grid.length; i++)
{
if (colSum(i) == upDiagSum)
{
continue;
}
else
{
isMagicSquare = false;
break;
}
}
if (isMagicSquare)
{
for (int i = 0; i < grid.length; i++)
{
if (rowSum(i) == upDiagSum)
{
continue;
}
else
{
isMagicSquare = false;
break;
}
}
}
}
else
{
isMagicSquare = false;
}
return isMagicSquare;
}
public static void main(String args[])
{
int[][] g = { { 8, 1, 6 }, { 3, 5, 7 }, { 4, 9, 2 } };
MagicSquare ms = new MagicSquare(g);
System.out.println(ms.isMagicSquare());
}
}
CHANGES:
1.Both the rowSum and colSum functions now only work on the particular row or column as opposed to iterating through both dimensions.
2.As for the logic of checking isMagicSquare, first check if the diagonals are equal, and if they are compare the sum of every row and column to either diagonal and on first fail, exit with false value, and on no fail exit with true value.
I am currently looking for a way of scanning a 2D matrix in Java for a number. Precisely, if in my matrix, there are numbers from 0 to 9, how do I "locate" the 0? This is intended for creating a Minesweeper game.
Here is what I have written so far. It is not complete. All I want is a clue on how to complete it.
class DemineurStackOverflow {
public static void minesweeper () {
int champDeMine[][];
boolean résultat[][];
int mine;
char réponse;
champDeMine = new int[longueur][largeur]; //Differenf from the matrix "champDeMines" from the second method
Arrays.asList(champDeMine).contains(0);
mine = (int) Math.floor(Math.random()*nbMines + 1);
System.out.println("Ajustement du nombre de mines en cours...");
if (mine < nbMines) {
for (mine = (int) Math.floor(Math.random()*nbMines + 1); mine < nbMines; mine++);
} else {
for (mine = (int) Math.floor(Math.random()*nbMines + 1); mine > nbMines; mine--);
}
if (mine == nbMines){
System.out.println("Chargement des mines OK.");
}
}
public static int [][] calculeProximité ( boolean [][] champDeMines ){
int row; //row index for prescence of 0, same value as longueur
int col; //column index for presence of 0, same value as largeur
int mine;
champDeMines = new boolean[row][col];
if (champDeMines = 0) {
champDeMines = mine;
}
//Here I am trying to figure a way of finding the 0s in this champDeMines matrix.
return (new int[champDeMines.length][champDeMines[0].length]);
}
}
The first method consists in generating an array from variables "longueur" and "largeur". The number of "mines" is supposed to represent the number 0 (which is why I want to scan for a 0), at random places. The second method consists in finding the "mines" in the array created. That is what I have trouble doing. Do you have any clues for completing the second method? I am simply looking for clues because I am learning to program in Java!
Thank you very much, your help is most certainly appreciated!
This is how minesweeper field population could look from the code provided. I hope you get the clue from the comments and do not hesitate to ask if anything is unclear.
import java.util.Random;
public class Demineur
{
// Here come CONSTANTS
public static final int MAX_MINES = 30;
public static final boolean MINE = true;
// Let's have a field 12x12 size
public static final int LONGEUR = 12;
public static final int LARGEUR = 12;
// each field contains number of mines it has access to
public static int champDeMine[][] = new int[LONGEUR][LARGEUR];
// each field can contain mine or be empty
public static boolean champDeMines[][] = new boolean[LONGEUR][LARGEUR];
public static void minesweeper()
{
Random random = new Random();
int mine ;
System.out.println("Ajustement du nombre de mines en cours...");
int nbMines = random.nextInt(MAX_MINES);
/**
* Let's plant mines. :-E
* Unoptimal but will suffice for demonstration purpose.
*/
int minesLeftToPlant = nbMines;
int skip = 0;
boolean planted = false;
while (minesLeftToPlant > 0)
{
skip = random.nextInt(LONGEUR*LARGEUR);
planted = false;
while (!planted && minesLeftToPlant > 0 && skip > 0)
{
for (int y = 0; !planted && minesLeftToPlant > 0 && y < LONGEUR; y++)
{
for (int x = 0; !planted && minesLeftToPlant > 0 && x < LARGEUR; x++)
{
if ( !MINE == champDeMines[y][x]
&& 0 == skip)
{
champDeMines[y][x] = MINE;
minesLeftToPlant--;
planted = true;
}
else
{
skip--;
}
}
}
}
}
System.out.println("Chargement des "+ nbMines +" mines OK.");
}
public static void calculeProximite()
{
int row ; //row index for prescence of 0, same value as longueur
int col ; //column index for presence of 0, same value as largeur
int mine;
//Check for each field it's neighbors and calculate which of them are mines
for (row = 0; row < LONGEUR; row++)
{
for (col = 0; col < LARGEUR; col++)
{
champDeMine[row][col] = numberOfMines(row,col);
}
}
}
public static void printChampDeMine()
{
for (int row = 0; row < LONGEUR; row++)
{
for (int col = 0; col < LARGEUR; col++)
{
System.out.print("'" + champDeMine[row][col] + "' ");
}
System.out.println();
}
}
public static void printChampDemines()
{
for (int row = 0; row < LONGEUR; row++)
{
for (int col = 0; col < LARGEUR; col++)
{
System.out.print("'" + (champDeMines[row][col] ? "m" : "e") + "' ");
}
System.out.println();
}
}
public static int numberOfMines(int row, int col)
{
return add(hasMine(row , col + 1))
+ add(hasMine(row - 1, col + 1))
+ add(hasMine(row - 1, col ))
+ add(hasMine(row - 1, col - 1))
+ add(hasMine(row , col - 1))
+ add(hasMine(row + 1, col - 1))
+ add(hasMine(row + 1, col ))
+ add(hasMine(row + 1, col + 1));
}
public static boolean hasMine(int row, int col)
{
return row >= 0 && col >= 0 && row < LONGEUR && col < LARGEUR
&& isMine(champDeMines[row][col]);
}
public static boolean isMine(boolean x)
{
return MINE == x;
}
public static int add(boolean c)
{
return c ? 1 : 0;
}
public static void main(String[] args)
{
minesweeper();
System.out.println("Champ de mines");
printChampDemines();
System.out.println("Champ de mine");
calculeProximite();
printChampDeMine();
}
}
(Disclaimer: There are maybe 20 different versions of this question on SO, but a reading through most of them still hasn't solved my issue)
Hello all, (relatively) beginner programmer here. So I've been trying to build a Sudoku backtracker that will fill in an incomplete puzzle. It seems to works perfectly well even when 1-3 rows are completely empty (i.e. filled in with 0's), but when more boxes start emptying (specifically around the 7-8 column in the fourth row, where I stopped writing in numbers) I get a Stack Overflow Error. Here's the code:
import java.util.ArrayList;
import java.util.HashSet;
public class Sudoku
{
public static int[][] puzzle = new int[9][9];
public static int filledIn = 0;
public static ArrayList<Integer> blankBoxes = new ArrayList<Integer>();
public static int currentIndex = 0;
public static int runs = 0;
/**
* Main method.
*/
public static void main(String args[])
{
//Manual input of the numbers
int[] completedNumbers = {0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,3,4,
8,9,1,2,3,4,5,6,7,
3,4,5,6,7,8,9,1,2,
6,7,8,9,1,2,3,4,5,
9,1,2,3,4,5,6,7,8};
//Adds the numbers manually to the puzzle array
ArrayList<Integer> completeArray = new ArrayList<>();
for(Integer number : completedNumbers) {
completeArray.add(number);
}
int counter = 0;
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
puzzle[i][j] = completeArray.get(counter);
counter++;
}
}
//Adds all the blank boxes to an ArrayList.
//The index is stored as 10*i + j, which can be retrieved
// via modulo and integer division.
boolean containsEmpty = false;
for(int i = 0; i < 9; i++) {
for(int j = 0; j < 9; j++) {
if(puzzle[i][j] == 0) {
blankBoxes.add(10*i + j);
containsEmpty = true;
}
}
}
filler(blankBoxes.get(currentIndex));
}
/**
* A general method for testing whether an array contains a
* duplicate, via a (relatively inefficient) sort.
* #param testArray The int[] that is being tested for duplicates
* #return True if there are NO duplicate, false if there
* are ANY duplicates.
*/
public static boolean checkDupl(int[] testArray) {
for(int i = 0; i < 8; i++) {
int num = testArray[i];
for(int j = i + 1; j < 9; j++) {
if(num == testArray[j] && num != 0) {
return false;
}
}
}
return true;
}
/**
* If the puzzle is not full, the filler will be run. The filler is my attempt at a backtracker.
* It stores every (i,j) for which puzzle[i][j] == 0. It then adds 1 to it's value. If the value
* is already somewhere else, it adds another 1. If it is 9, and that's already there, it loops to
* 0, and the index beforehand is rechecked.
*/
public static void filler(int indexOfBlank) {
//If the current index is equal to the size of blankBoxes, meaning that we
//went through every index of blankBoxes, meaning the puzzle is full and correct.
runs++;
if(currentIndex == blankBoxes.size()) {
System.out.println("The puzzle is full!" + "\n");
for(int i = 0; i < 9; i++) {
System.out.println();
for(int j = 0; j < 9; j++) {
System.out.print(puzzle[i][j]);
}
}
System.out.println("\n" + "The filler method was run " + runs + " times");
return;
}
//Assuming the puzzle isn't full, find the row/column of the blankBoxes index.
int row = blankBoxes.get(currentIndex) / 10;
int column = blankBoxes.get(currentIndex) % 10;
//Adds one to the value of that box.
puzzle[row][column] = (puzzle[row][column] + 1);
//Just used as a breakpoint for a debugger.
if(row == 4 && column == 4){
int x = 0;
}
//If the value is 10, meaning it went through all the possible values:
if(puzzle[row][column] == 10) {
//Do filler() on the previous box
puzzle[row][column] = 0;
currentIndex--;
filler(currentIndex);
}
//If the number is 1-9, but there are duplicates:
else if(!(checkSingleRow(row) && checkSingleColumn(column) && checkSingleBox(row, column))) {
//Do filler() on the same box.
filler(currentIndex);
}
//If the number is 1-9, and there is no duplicate:
else {
currentIndex++;
filler(currentIndex);
}
}
/**
* Used to check if a single row has any duplicates or not. This is called by the
* filler method.
* #param row
* #return
*/
public static boolean checkSingleRow(int row) {
return checkDupl(puzzle[row]);
}
/**
* Used to check if a single column has any duplicates or not.
* filler method, as well as the checkColumns of the checker.
* #param column
* #return
*/
public static boolean checkSingleColumn(int column) {
int[] singleColumn = new int[9];
for(int i = 0; i < 9; i++) {
singleColumn[i] = puzzle[i][column];
}
return checkDupl(singleColumn);
}
public static boolean checkSingleBox(int row, int column) {
//Makes row and column be the first row and the first column of the box in which
//this specific cell appears. So, for example, the box at puzzle[3][7] will iterate
//through a box from rows 3-6 and columns 6-9 (exclusive).
row = (row / 3) * 3;
column = (column / 3) * 3;
//Iterates through the box
int[] newBox = new int[9];
int counter = 0;
for(int i = row; i < row + 3; i++) {
for(int j = row; j < row + 3; j++) {
newBox[counter] = puzzle[i][j];
counter++;
}
}
return checkDupl(newBox);
}
}
Why am I calling it a weird error? A few reasons:
The box that the error occurs on changes randomly (give or take a box).
The actual line of code that the error occurs on changes randomly (it seems to usually happen in the filler method, but that's probably just because that's the biggest one.
Different compilers have different errors in different boxes (probably related to 1)
What I assume is that I just wrote inefficient code, so though it's not an actual infinite recursion, it's bad enough to call a Stack Overflow Error. But if anyone that sees a glaring issue, I'd love to hear it. Thanks!
Your code is not backtracking. Backtracking implies return back on failure:
if(puzzle[row][column] == 10) {
puzzle[row][column] = 0;
currentIndex--;
filler(currentIndex);// but every fail you go deeper
}
There are must be something like:
public boolean backtrack(int currentIndex) {
if (NoBlankBoxes())
return true;
for (int i = 1; i <= 9; ++i) {
if (NoDuplicates()) {
puzzle[row][column] = i;
++currentIndex;
if (backtrack(currentIndex) == true) {
return true;
}
puzzle[row][column] = 0;
}
}
return false;
}