Related
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
UPDATED CODE: My last question is how do I get it so that my program prints all 92 solutions of an 8x8 board, without any Queens attacking each other? So far, my code only prints 1 solution. For example, when I change it to a 4x4 board, I only have 1 solution, when there should be 2 I believe.
public class Queen{
public static void display(int[] board){
int n = board.length;
for(int column = 0; column < n; column++){
for(int row = 0; row < n; row++){
if(board[column] == row)
System.out.print('Q');
else
System.out.print('x');
}
System.out.print('\n');
}
}
public static int[] solveQueens(int n){
int board[] = new int[n];
placeQueen(board,0);
return board;
}
private static boolean placeQueen(int[] board, int column){
int n = board.length;
if (n == column){
return true;
}else{
for (int row = 0; row < n; row++){
int i; //remove
for (i = 0; i < column; i++){ //for (int i)
if (board[i] == row || i - column == board[i] - row || column - i == board[i] - row){
break;
}
}
if (i == column){
board[column] = row;
if (placeQueen(board, column + 1))
return true;
}
}
}
return false;
}
public static void main(String args[]){
int finished[] = solveQueens(8);
display(finished);
}
}
Now my programs returns:
Qxxxxxxx
xxxxQxxx
xxxxxxxQ
xxxxxQxx
xxQxxxxx
xxxxxxQx
xQxxxxxx
xxxQxxxx
OLD CODE:
I need to use recursive backtracking to solve the 8-queens problem. The n-queens problem is a puzzle that requires placing n chess queens on an n × n board so that none of the queens attack each other.
I need to Write a public solveQueens(int n) method to solve the problem for an nxn board
I also need to write a private recursive placeQueen(board, column) method to attempt to place a queen in the specified column.
This is my code so far:
public class Queen{
public static int[] solveQueens(int n){
int board[] = new int[n];
int finished[] = placeQueen(board,0);
return finished;
}
private static int[] placeQueen(int[] board, int column){
int n = board.length;
int row = column;
if (n == column){
return board;
}else{
for (row = n; row < n; row++){
board[column] = row;
if (board[n] == 0 && board[n-1] == 0 && board[n+1] == 0){
board[n] = 1;
placeQueen(board, column+1);
}
}
for (row = n; row < n; row++){
board[row] = column;
if (board[n] == 0 && board[n-1] == 0 && board[n+1] == 0){
board[n] = 1;
placeQueen(board, column+1);
}
}
}
return board;
}
public static void main(String args[]){
int finished[] = solveQueens(8);
for (int item: finished){
System.out.print(item);
}
}
}
Whenever I run my program, all it returns is
----jGRASP exec: java Queen
00000000
Is there any explanation on how to setup my 8x8 board, and how to place queens so they don't attack each other?
I made some changes in solveQueens and placeQueen:
public class Queen{
public static int[] solveQueens(int n){
int board[] = new int[n];
placeQueen(board,0); //it fills the board
return board;
}
private static boolean placeQueen(int[] board, int column){
int n = board.length;
int row;
if (n == column){
return true;
}else{
for (row = 0; row < n; row++){
int c;
for (c=0; c<column; c++){
if (board[c]==row || c-column==board[c]-row || column-c==board[c]-row){
//check if it is safe position (we know 2 queens couldn't place in a same column or row or diameter
break;
}
}
if (c==column){ //if previous loop didn't break...=> it is safe position
board[column]=row;
if (placeQueen(board, column+1)) //if it is possible to fill the rest of board //else: test the next row
return true;
}
}
}
return false;
}
public static void main(String args[]){
int finished[] = solveQueens(8);
for (int item: finished){
System.out.print(item);
}
}
}
I am working a problem that seems to be somewhat famous among beginning programmers, the 8 queens puzzle. I have seen several solutions to this problems using 2D arrays, recursion etc, but this problem is an assignment given in CS course book chapter introducing 1D arrays, so the available techniques to solve this problem are limited.
The procedure I have used, is by first creating a 1D array with the size of 64, which makes possible positions to place queens from index 0 to 63. A random position index is then generated, and a test is preformed to check if there is any queens attacking this position. If this position is not attacked by any queens, a queen is placed by setting the board[position] = true. When a queen is placed, the queenCount is incremented, and this process repeats until 8 queens have been placed.
If queens are placed in such a way that it is impossible to place 8, the board resets after 1 millisecond by preforming a timecheck, and retries to place the 8 queens. At the best I am able to place 7 queens, but the last remaining one is never placed. Each attempt is printed, along with queenCount for this attempt. Is it possible to use this approach, or is it a dead end?
Code example below:
package ch7;
public class Chapter_07_E22_EightQueens64bool {
public static void main(String[] args) {
int queenCount = 0;
int attemptCount = 0;
boolean[] board = new boolean[8 * 8];
final long TIME_LIMIT = 1; //Milliseconds
long startTime = System.currentTimeMillis();
while (queenCount < 8) {
int position = placeQueen(board.length);
if(checkPosition(position, board) && !board[position]) {
board[position] = true;
queenCount++;
}
long timeCheck = System.currentTimeMillis();
if (timeCheck - startTime > TIME_LIMIT) {
clearBoard(board);
queenCount = 0;
startTime = System.currentTimeMillis();
}
System.out.println("Attempt #" + ++attemptCount);
System.out.println(queenCount + " queens placed.");
printBoard(board);
}
}
public static void printBoard(boolean[] board) {
for (int i = 0; i < board.length; i++) {
if (board[i])
System.out.print("|Q");
else
System.out.print("| ");
if ((i + 1) % 8 == 0)
System.out.println("|");
}
}
public static int placeQueen(int boardSize) {
return (int)(Math.random() * boardSize);
}
public static boolean[] clearBoard(boolean[] board) {
for (int i = 0; i < board.length; i++)
board[i] = false;
return board;
}
public static boolean checkPosition(int position, boolean[] board) {
return checkTop(position, board) && checkBottom(position, board) && checkLeft(position, board) &&
checkRight(position, board) && checkTopLeft(position, board) && checkTopRight(position, board) &&
checkBottomLeft(position, board) && checkBottomRight(position, board);
}
public static boolean checkTop(int position, boolean[] board) {
// Checks each field above the current position while i >= 8
for (int i = position; i >= 8; i -= 8) {
if (board[i - 8])
return false;
}
return true;
}
public static boolean checkBottom(int position, boolean[] board) {
// Checks each field below the current position while i <= 55;
for (int i = position; i <= 55; i += 8) {
if (board[i + 8])
return false;
}
return true;
}
public static boolean checkRight(int position, boolean[] board) {
// Checks each field to the right of the current position while i % 8 < 7
for (int i = position; i % 8 < 7; i += 1) {
if (board[i + 1])
return false;
}
return true;
}
public static boolean checkLeft(int position, boolean[] board) {
// Checks each field to the left of the current position while i % 8 != 0
for (int i = position; i % 8 != 0; i -= 1) {
if (board[i - 1])
return false;
}
return true;
}
public static boolean checkTopLeft(int position, boolean[] board) {
// Checks each field top left of the current position while i >= 9
for (int i = position; i >= 9; i -= 9) {
if (board[i - 9])
return false;
}
return true;
}
public static boolean checkTopRight(int position, boolean[] board) {
// Checks each field top right of the current position while i >= 7
for (int i = position; i >= 7; i -= 7) {
if (board[i - 7])
return false;
}
return true;
}
public static boolean checkBottomRight(int position, boolean[] board) {
// Checks each field below the current position while i <= 54
for (int i = position; i <= 54; i += 9) {
if (board[i + 9])
return false;
}
return true;
}
public static boolean checkBottomLeft(int position, boolean[] board) {
// Checks each field below the current position while i <= 56
for (int i = position; i <= 56; i += 7) {
if (board[i + 7])
return false;
}
return true;
}
}
First, array of size 8 is perfectly sufficient.
The array index represents the column in which was the queen placed and the value represents the row.
[0, 2, 4, 6, 1, 3, 5, 7]
Means that queen in the first column was placed in the first row, second queen was placed in the 3rd row, 3rd queen in 5th row, etc...
So when you place a new queen, check if the row you add it in, isn't already in the array. This way, you only need to worry about diagonal collisions.
Simplest way of solving the problem is recursion (backtracking). If that is not allowed, you can simulate recursion with a stack. If that is not allowed either, you could use 8 nested loops - ugly.
You can improve your collision checking using a simple trick. It works like this -
Let's say your queen #0 is on row #3.
Which cells does she attack diagonally?
On the first column, it's row #2 and row #4 (-1 and +1)
On the second column, it's row #1 and row #5 (-2 and +2)
On the third column it's row #0 and row #6 (-3 and +3)
So when you add a new queen, you iterate previous queens checking one diagonal with (newIndex - oldIndex) + oldRow == newRow and the other diagonal with (newIndex - oldIndex) - oldRow == newRow
So, considering all this, the checking function could look like
boolean canAdd(List<Integer> p, int newRow) {
if (p.contains(newRow))
return false;
int insertIndex = p.size();
for (int i = 0; i < p.size(); i++) {
if (p.get(i) + (insertIndex - i) == newRow || p.get(i) - (insertIndex - i) == newRow)
return false;
}
return true;
}
While the main recursive function could look like
void solve(List<Integer> p, int index) {
if (index == 8) {
System.out.println(p);
return;
}
for (int i = 0; i < 8; i++) {
if (canAdd(p, i)) {
p.add(i);
solve(p, index + 1);
p.remove(p.size() - 1);
}
}
}
And you could call it like this
solve(new ArrayList<Integer>(), 0);
After working on this problem for a few days, I now have a solution that works that in a reasonable amount of time for N <= 20. It goes like this.
For i < N, initialize queens[i] = i. Each row can only hold 1 value, so no need to check for collisions on the left or right. As long as there is no duplicate values in the array, there will not be any column collisions either.
Use the method to check if a queen at a given point, shares a diagonal with a queen at another given point. The method checks to see if the distance between x1 and x0 is equal to the distance of y1 and y0. If the distance is equal, then the co-ordinates (x0,y0) and (x1,y1) share the same diagonal.
Use another method to invoke shareDiagonal(int x0, int y0, int x1, int y1) to check if a queen at a given row, for example on row 7, collides with a queen on any rows above row 7. As mentioned, only the rows above the given row are checked. The reason is that if you for example are checking row2 for any diagonal collisions, any collision with rows below row 2 will be revealed when checking a row with a higher index value. If a queen on row 2 collides with a queen on row 4, this will be revealed when checking row 4 and the rows above.
A third checking method invokes checkRowForCollision(int[] queens, int row), where each row is traversed checking for collisions on the rows above. Row 1 is checked if there is any collisions with queens on row 0, Row 2 is checked if there is any collisions on row 0 and 1, row 3 is checked if there is any collisions on row 0, 1 and 2, etc..
While there is diagonal collisions between any of the queens, the board shuffles until it shows a solution where no queens attack each other.
Code example below:
package ch7;
public class Chapter_07_E22_EightQueens {
static final int N = 8;
public static void main(String[] args) {
int[] queens = new int[N];
int attempts = 0;
for (int i = 0; i < N; i++)
queens[i] = i;
while (checkBoardForCollision(queens)) {
shuffleBoard(queens);
attempts++;
}
printBoard(queens);
System.out.println("Solution found in " + attempts + " attempts");
}
public static void printBoard(int[] queens) {
for (int row = 0; row < N; row++) {
System.out.printf("%-1c", '|');
for (int column = 0; column < N; column++) {
System.out.printf("%-1c|", (queens[row] == column) ? 'Q' : ' ');
}
System.out.println();
}
}
public static boolean shareDiagonal(int x0, int y0, int x1, int y1) {
int dy = Math.abs(y1 - y0);
int dx = Math.abs(x1 - x0);
return dx == dy;
}
public static boolean checkRowForCollision(int[] queens, int row) {
for (int i = 0; i < row; i++) {
if (shareDiagonal(i, queens[i], row, queens[row]))
return true;
}
return false;
}
public static boolean checkBoardForCollision(int[] queens) {
for (int row = 0; row < queens.length; row++)
if (checkRowForCollision(queens, row))
return true;
return false;
}
public static int[] shuffleBoard(int[] queens) {
for (int i = queens.length - 1; i > 0; i--) {
int j = (int)(Math.random() * (i + 1));
int temp = queens[i];
queens[i] = queens[j];
queens[j] = temp;
}
return queens;
}
}
One of the problems, there may be more though, is in the checkTop method.
public static boolean checkTop(int position, boolean[] board)
{
// Checks each field above the current position while i - 8 > - 1
for (int i = position; i > (i - 8); i -= 8)
{
if ((i - 8) > -1)
{
if (board[i - 8])
return false;
}
}
return true;
}
There are cases when the method doesn't find a slot (board[i - 8] = true) and i reaches the value of 7. After this point, the condition of the for loop (i > (i - 8)) will always be true and the condition of the outermost if inside the loop (if ( (i - 8) > -1) will always be false. This causes the program to infinitely stay in the loop.
Example (i reaches -5):
i = -5;
i > ( i - 8) : -5 > (-5 -8 = -13) (always true)
(i - 8) > -1 : -13 > -1 (false) always false for i <= 7
So, I have been making this Tic Tac Toe program for a while now.
It's a basic Tic Tac Toe game but the game board is scalable. The program is almost finished but one little feature is missing.
I have to make the game end if player gets five or more marks in a row when the game board is larger than 4x4.
F.E. If the game board is 9x9 the game has to end when the player or the computer gets five marks in a row.
(Mark = "O" or "X").
The game now ends when someone gets marks in a row equals to the size of the board (if 9x9 you need 9 marks in a row to win).
I have to implement a feature in the playerHasWon and I've been having a lot of trouble finding out how. I think it's a simple to implement thing but I have not found out how to do it.
Hope my explanation is easy enough to understand. Here's the code:
package tictac;
import java.util.Scanner;
import java.util.Random;
public class Tictac {
public static final int DRAW = 0; // game ends as a draw
public static final int COMPUTER = 1; // computer wins
public static final int PLAYER = 2; // player wins
public static final char PLAYER_MARK = 'X'; // The "X"
public static final char COMPUTER_MARK = 'O'; // The "O"
public static int size; // size of the board
public static String[][] board; // the board itself
public static int score = 0; // game win score
public static Scanner scan = new Scanner(System.in); // scanner
/**
* Builds the board with the integer size and user input.
*
* Displays game win message and switches play turns.
*
* #param args the command line parameters. Not used.
*/
public static void main(String[] args) {
while (true) {
System.out.println("Select board size");
System.out.print("[int]: ");
try {
size = Integer.parseInt(scan.nextLine());
} catch (Exception e) {
System.out.println("You can't do that.");
continue; // after message, give player new try
}
break;
}
int[] move = {};
board = new String[size][size];
setupBoard();
int i = 1;
loop: // creates the loop
while (true) {
if (i % 2 == 1) {
displayBoard();
move = getMove();
} else {
computerTurn();
}
switch (isGameFinished(move)) {
case PLAYER:
System.err.println("YOU WIN!");
displayBoard();
break loop;
case COMPUTER:
System.err.println("COMPUTER WINS!");
displayBoard();
break loop;
case DRAW:
System.err.println("IT'S A DRAW");
displayBoard();
break loop;
}
i++;
}
}
/**
* Checks for game finish.
*
* #param args command line parameters. Not used.
*
* #return DRAW the game ends as draw.
* #return COMPUTER the game ends as computer win.
* #return PLAYERE the game ends as player win.
*/
private static int isGameFinished(int[] move) {
if (isDraw()) {
return DRAW;
} else if (playerHasWon(board, move,
Character.toString(COMPUTER_MARK))) {
return COMPUTER;
} else if (playerHasWon(board, move,
Character.toString(PLAYER_MARK))) {
return PLAYER;
}
return -1; // can't be 0 || 1 || 2
}
/**
* Checks for win for every direction on the board.
*
* #param board the game board.
* #param move move on the board.
* #param playerMark mark on the board "X" or "O".
* #return the game is won.
*/
public static boolean playerHasWon(String[][] board, int[] move,
String playerMark) { //playermark x || o
// horizontal check
for (int i = 0; i < size; i++) {
if (board[i][0].equals(playerMark)) {
int j;
for (j = 1; j < size; j++) {
if (!board[i][j].equals(playerMark)) {
break;
}
}
if (j == size) {
return true;
}
}
}
// vertical check
for (int i = 0; i < size; i++) {
if (board[0][i].equals(playerMark)) {
int j;
for (j = 1; j < size; j++) {
if (!board[j][i].equals(playerMark)) {
break;
}
}
if (j == size) {
return true;
}
}
}
// diagonals check
int i;
for (i = 0; i < size; i++) {
if (!board[i][i].equals(playerMark)) {
break;
}
}
if (i == size) {
return true;
}
for (i = 0; i < size; i++) {
if (!board[i][(size - 1) - i].equals(playerMark)) {
break;
}
}
return i == size;
}
/**
* Checks for draws.
*
* #return if this game is a draw.
*/
public static boolean isDraw() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
if (board[i][j] == " ") {
return false;
}
}
}
return true;
}
/**
* Displays the board.
*
*
*/
public static void displayBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
System.out.printf("[%s]", board[i][j]);
}
System.out.println();
}
}
/**
* Displays the board.
*
*
*/
public static void setupBoard() {
for (int i = 0; i < size; i++) {
for (int j = 0; j < size; j++) {
board[i][j] = " ";
}
}
}
/**
* Takes in user input and sends it to isValidPlay.
*
* #return null.
*/
public static int[] getMove() {
Scanner sc = new Scanner(System.in);
System.out.println("Your turn:");
while (true) {
try {
System.out.printf("ROW: [0-%d]: ", size - 1);
int x = Integer.parseInt(sc.nextLine());
System.out.printf("COL: [0-%d]: ", size - 1);
int y = Integer.parseInt(sc.nextLine());
if (isValidPlay(x, y)) {
board[x][y] = "" + PLAYER_MARK;
return new int[]{x, y};
} else { // if input is unallowed
System.out.println("You can't do that");
continue; // after message, give player new try
}
} catch (Exception e) {
System.out.println("You can't do that.");
}
return null;
}
}
/*
* Randomizes computer's turn, where it inputs the mark 'O'.
*
*
*/
public static void computerTurn() {
Random rgen = new Random(); // Random number generator
while (true) {
int x = (int) (Math.random() * size);
int y = (int) (Math.random() * size);
if (isValidPlay(x, y)) {
board[x][y] = "" + COMPUTER_MARK;
break;
}
}
}
/**
* Checks if a move is possible.
*
* #param inX x-move is out of bounds.
* #param inY y-move is out of bounds.
* #return false
*/
public static boolean isValidPlay(int inX, int inY) {
// Play is out of bounds and thus not valid.
if ((inX >= size) || (inY >= size)) {
return false;
}
// Checks if a play have already been made at the location,
// and the location is thus invalid.
return (board[inX][inY] == " ");
}
}
// End of file
Took a quick look, detected the problem and came up with a quick fix:
public static boolean checkDiagonal(String markToLook) {
// how many marks are we looking for in row?
int sizeToWin = Math.min(size, 5);
// running down and right
// don't need to iterate rows that can't be the starting point
// of a winning diagonal formation, thus can exlude some with
// row < (size - (sizeToWin - 1))
for (int row = 0; row < (size - (sizeToWin - 1)); row++) {
for (int col = 0; col < size; col++) {
int countOfMarks = 0;
// down and right
for (int i = row; i < size; i++) {
if (board[i][i] == null ? markToLook == null :
board[i][i].equals(markToLook)) {
countOfMarks++;
if (countOfMarks >= sizeToWin) {
return true;
}
}
}
countOfMarks = 0;
// down and left
for (int i = row; i < size; i++) {
if (board[i][size - 1 - i] == null ? markToLook == null :
board[i][size - 1 - i].equals(markToLook)) {
countOfMarks++;
if (countOfMarks >= sizeToWin) {
return true;
}
}
}
}
}
return false;
}
And call it from your PlayerHasWon method instead of performign the checks there. Basically we iterate each possible starting square on the board for a diagonal winning formation, and run check down+left and down+right for each of the squares.
I am in awful hurry and did not test it much, but will return in couple of hours to improve this solution. Seems to work.
Edit: My previous solution I found lacking in further tests, I've updated the above code to function as desired.
First, I think playerMark should be a char and not a String. That said, let's go for the answer. The "horizontal" case would be:
// This is the number of marks in a row required to win
// Adjust formula if necessary
final int required = size > 4 ? 5 : 3;
for (int i = 0; i < size; i++) {
int currentScore = 0;
for (j = 0; j < size; j++) {
if (board[i][j].equals(playerMark)) {
currentScore++;
if (currentScore >= required)
return true;
}
else {
currentScore = 0;
}
}
}
}
The vertical case would be analogous. The diagonal one is a bit trickier as now it would require board[i][i+k] for the main diagonal and board[i][k-i] for the secondary; and it may not be obvious which values k and i must traverse. Here's my attempt (variable required as in horizontal case):
Note: everything from here down has been completely rewritten on 2015-12-16. The previous versions didn't work and the algorithm was not explained.
After two failed attempts I decided to do my homework and actually sort things out instead of doing everything in my head thinking I can keep track of all variables. The result is this picture:
Main diagonals are painted in blue, secondary diagonals in green.
Each diagonal is identified by a value of k, with k=0 being always being the longest diagonal of each set. Values of k grow as diagonals move down, so diagonals above the longest one have negative k while diagonals below the longest one have positive k.
Things that hold for both diagonals:
Diagonal contains size-abs(k) elements. Diagonals in which size-abs(k) is less than required need not be searched. This means that, for board size size and required length required, we'll search values of k from required-size to size-required. Notice that these have the same absolute value, with the first being <=0 and the second >=0. These values are both zero only when required==size, i.e. when we need the full diagonal to claim a win, i.e. when we only need to search k=0.
For k<=0, possible values of i (row) go from 0 to size+k. Values greater than or equal to size+k cross the right edge of the board and are thus outside the board.
For k>=0, possible values of i (row) go from k to size. Values below k cross the left edge of the board and are thus outside the board.
Only for main (blue) diagonals:
Value of j (column) is k+i.
Only for secondary (green) diagonals:
Value of j (column) is size-1+k-i. In case this is not obvious, just pick the top right corner (k=0,i=0) and notice j=size-1. Then notice that adding 1 to k (keeping i constant) always moves j by 1 right (it would go out of the board if done from k=0,i=0, just think about the intersection of horizontal line i=0 with diagonal k=1), and adding 1 to i (keeping k constant) always moves j by 1 to the left.
The ressulting code would be:
// Main diagonal
for (int k = required - size; k < size - required; k++)
{
int currentScore = 0;
startI = Math.max (0, k);
endI = Math.min (size, size+k);
for (int i = startI, i < endI; i++)
{
if (board[i][k+i].equals (playerMark))
{
currentScore++;
if (currentScore >= required)
return true;
}
else
currentScore = 0;
}
}
// Secondary diagonal
for (int k = required - size; k < size - required; k++)
{
int currentScore = 0;
startI = Math.max (0, k);
endI = Math.min (size, size+k);
for (int i = startI, i < endI; i++)
{
if (board[i][size-1+k-i].equals (playerMark))
{
currentScore++;
if (currentScore >= required)
return true;
}
else
currentScore = 0;
}
}
At this point, the code is nearly identical in both cases, changing only the j index in board[i][j]. In fact, both loops could be merged, taking care only of keeping two currentScore variables, one for the main (blue) diagonal and the other for the secondary (green) diagonal.
(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;
}
So I have this university assignment to solve Sudoku... I read about Algorithm X and Dancing algorithm, but they didn't help me.
I need to make it with backtracking. I hard-coded some of the indexes in the two dimensional array with numbers on places given from Wikipedia (so I am sure that it's solvable).
The code I got is the following:
public void solveSudoku(int row, int col)
{
// clears the temporary storage array that is use to check if there are
// dublicates on the row/col
for (int k = 0; k < 9; k++)
{
dublicates[k] = 0;
}
// checks if the index is free and changes the input number by looping
// until suitable
if (available(row, col))
{
for (int i = 1; i < 10; i++)
{
if (checkIfDublicates(i) == true)
{
board[row][col] = i;
if (row == 8)
solveSudoku(0, col + 1);
else if (col == 8)
solveSudoku(row + 1, 0);
else
solveSudoku(row, col + 1);
board[row][col] = 0;
}
}
}
// goes to the next row/col
else
{
if (row == 8)
solveSudoku(0, col + 1);
else if (col == 8)
solveSudoku(row + 1, 0);
else
solveSudoku(row, col + 1);
}
}
/**
* Checks if the spot on the certain row-col index is free of element
*
* #param row
* #param col
* #return
*/
private boolean available(int row, int col)
{
if (board[row][col] != 0)
return false;
else
return true;
}
/**
* Checks if the number given is not already used in this row/col
*
* #param numberToCheck
* #return
*/
private boolean checkIfDublicates(int numberToCheck)
{
boolean temp = true;
for (int i = 0; i < dublicates.length; i++)
{
if (numberToCheck == dublicates[i])
{
temp = false;
return false;
}
else if (dublicates[i] == 0)
{
dublicates[i] = numberToCheck;
temp = true;
return true;
}
}
return temp;
}
I am getting StackOverflow on
// goes to the next row/col
else
{
if (row == 8)
solveSudoku(0, col + 1);
else if (col == 8)
solveSudoku(row + 1, 0);
else
solveSudoku(row, col + 1);
}
which means that I have to stop the recursion at some point, but I can't figure it out how!
If you find any other mistakes in the solve() function - let me know. Because I am not sure I understand the "backtracking" thing completely...
You can stop recursion for example if you keep track of the current recursion depth
public void solveSudoku(int row, int col, int recursionDepth) {
// get out of here if too much
if (recursionDepth > 15) return;
// regular code...
// at some point call self with increased depth
solveSudoku(0, col + 1, recursionDepth + 1);
}
And if you find any other mistakes in the solve() function - let me know.
Too much code :)
This is roughly the way I've done this in the past.
Whenever all the definite moves have been taken and there is a choice of equally good next moves:
copy your grid data structure and push it onto a stack.
take the first candidate move and continue solving recursively
Whereever you get stuck:
pop the saved grid off the stack
take the next candidate move.
I made it in a more simple way:
public void solve(int row, int col)
{
if (row > 8)
{
printBoard();
System.out.println();
return;
}
if (board[row][col] != 0)
{
if (col < 8)
solve(row, col + 1);
else
solve(row + 1, 0);
}
else
{
for (int i = 0; i < 10; i++)
if (checkRow(row, i) && checkCol(col, i))
//&& checkSquare(row, col, i))
{
board[row][col] = i;
if (col < 8)
solve(row, col + 1);
else
solve(row + 1, 0);
}
board[row][col] = 0;
}
}
private boolean checkRow(int row, int numberToCheck)
{
for (int i = 0; i < 9; i++)
if (board[row][i] == numberToCheck)
return false;
return true;
}
private boolean checkCol(int col, int numberToCheck)
{
for (int i = 0; i < 9; i++)
if (board[i][col] == numberToCheck)
return false;
return true;
}
I'm not sure why you say that Dancing Links and Algorithm X were not useful.
Do you mean that you were not able to map Sudoku to an instance of the Exact Cover problem that Algorithm X is designed to solve?
Or that it is a too complicated approach for what you need??
If the former is the case, you might want to look at: A Sudoku Solver in Java implementing Knuth’s Dancing Links Algorithm. It's quite clear and explains also the reasoning behind.
N.B. Algorithm X is a backtracking algorithm so, if that's your only requirement, you can definitely use this approach.
Hope this can help.