N-Queens using a stack, cannot find the bug - java

I have attempted to complete my homework project and am seeking help finding a bug. I am using a backtracking algorithm to find all the solutions to an N-queens problem. My main concern is my conflict method-which is inside a stack class. Its purpose is to detect if the Queen object (parameter 1 of conflict method) being passed is in the same row, column, or diagonal as any other queens on the board. The queen object passed into the conflict method is stored inside the Queen class and its location is recorded with the help of an instance of the Point class. My code uses two methods in the Queen class that I created, public int getRow(), and public int getColumn(). Both return an int. Second parameter is a 2d array (or array of arrays) named board. Queens already on the board are denoted in this array with a boolean value of true. Boolean values of false indicate an empty square on the board.
Solution.n is a reference to a static int variable in another class. Its value denotes the edge of the board. Example...for the 8-Queens problem we create a 2d array with size 8. Solution.n is decremented by 1 to equal the last index of the 2d array.
Here is the code:
public boolean conflict(Queen x, boolean [][] board) //conflict method
{
if(checkColumn(x, board) == false)
return true; //conflict
else if(checkRow(x, board) == false)
return true; //conflict
else if(checkDiagonal(x, board) == false )
return true; //conflict
else
return false; //no conflict on board
}
private boolean checkColumn(Queen x, boolean [][] board)//returns true when column is safe
{
int col = x.getColumn();
for(int row = 0; row <= Solution.n; row++)
{
if(board[row][col] == true) //queen is in this column
{
return false;
}
}
return true;
}
private boolean checkRow(Queen x, boolean [][] board) //returns true when row is safe
{
int row = x.getRow();
for(int col = 0; col <= Solution.n; col++)
{
if(board[row][col] == true) //queen is in this row
{
return false;
}
}
return true;
}
private boolean checkDiagonal(Queen location, boolean [][] board) //returns true when diagonal is safe
{
int row, col;
row = location.getRow() - 1;
col = location.getColumn() - 1;
while(row >=0 && col >= 0) //iterate down-left
{
if(board[row][col] == true) //queen found?
{
return false;
}
row--;
col--;
}
row = location.getRow() - 1;
col = location.getColumn() + 1;
while(row != -1 && col <= Solution.n) //iterate down-right
{
if(board[row][col] == true) //queen found?
{
return false;
}
row--;
col++;
}
row = location.getRow() + 1;
col = location.getColumn() + 1;
while(row <= Solution.n && col <= Solution.n) //iterate up-right
{
if(board[row][col] == true) //queen found?
{
return false;
}
row++;
col++;
}
row = location.getRow() +1;
col = location.getColumn()-1;
while(row <= Solution.n && col != -1) //iterate up-left
{
if(board[row][col] == true) //queen found?
{
return false;
}
row++;
col--;
}
return true;
}
I am convinced this snippet of code contains a bug, but if i'm wrong then I apologize for wasting your time :P
Your help would be greatly appreciated. Thanks! :D

You have several small bugs in there - for example, you have loops that go from 0 to Solution.n, inclusive, while they should go to Solution.n-1. Most of the errors, however, can be eliminated by picking a more suitable data structure.
Think about it: you don't need a full NxN board to decide the placement of a queen:
There's one queen per row, so queen's number is its row.
There's one queen per column, so you need an array of boolean[N] to know which rows are taken.
There's one queen per ascending diagonal, so you need an array of boolean[2N-1] to know which ascending diagonals are taken.
There's one queen per descending diagonal, so you need an array of boolean[2N-1] to know which descending diagonals are taken.
boolean[] columns = new boolean[N];
boolean[] ascending = new boolean[2*N-1];
boolean[] descending = new boolean[2*N-1];
At this point you've got all you need: instead of a square boolean[N][N] array you need three linear arrays of boolean. This lets you do your checks much faster, too:
int c = x.getColumn();
int r = x.getRow();
boolean conflict = columns[c]
|| ascending[r+c]
|| descending[N-r+c];
That's it - no loops required! Now you can code your backtracking algorithm using these three arrays instead of a square board.

This answer won't solve your problem, since I'm not convinced your error is in the code you pasted, but here's your code, written a bit closer to how I might write it:
// returns true when column is safe
private boolean checkColumn(Queen x, boolean [][] board)
{
int col = x.getColumn();
for(int row = 0; row <= Solution.n; row++)
{
if(board[row][col]){ return false; }
}
return true;
}
// returns true when row is safe
private boolean checkRow(Queen x, boolean [][] board)
{
int row = x.getRow();
for(int col = 0; col <= Solution.n; col++)
{
if(board[row][col]){ return false; }
}
return true;
}
// returns true if the position is valid given the board size
// (as defined by Solution)
private boolean validPosition(int row, int col)
{
if(0 > row || row > Solution.n){ return false; }
if(0 > col || col > Solution.n){ return false; }
return true;
}
// returns true when diagonal is safe
private boolean checkDiagonal(Queen x, boolean [][] board)
{
int row, col;
// Down Left
row = x.getRow(); // "Start" on current position
col = x.getColumn();
while(true)
{
row--; col--; // Take a step in the direction
if(!validPosition(row, col)){ break; } // Stop if we've left the board
if(board[row][col]){ return false; } // Check whether it's occupied
}
// Down Right
row = x.getRow();
col = x.getColumn();
while(true)
{
row--; col++;
if(!validPosition(row, col)){ break; }
if(board[row][col]){ return false; }
}
// Up Right
row = x.getRow();
col = x.getColumn();
while(true)
{
row++; col++;
if(!validPosition(row, col)){ break; }
if(board[row][col]){ return false; }
}
// Up Left
row = x.getRow();
col = x.getColumn();
while(true)
{
row++; col--;
if(!validPosition(row, col)){ break; }
if(board[row][col]){ return false; }
}
return true;
}
public boolean conflict(Queen x, boolean [][] board) //conflict method
{
if ( checkColumn(x, board) == false){ return true; }
else if( checkRow(x, board) == false){ return true; }
else if(checkDiagonal(x, board) == false){ return true; }
else { return false; }
}
}
It simplifies a lot of the logic, adds a helper function validPosition(), and cleans up some of the tests and loops.

Related

How to find a >3 line of consecutive elements in a 2d array

I'm a game where you have to make a line in a row/column of 3 or more (up to 7) consecutive elements in a 2d array. Every time there is one of those lines you add the score and delete those consecutive elements from the row/column (Similar as to candycrash disappearing candies.
If you have something like this ['a' 'b' 'b' 'b' 'a'] the result should be ['a' '' '' '' 'a']
The problem comes to more complex cases like ['b' 'b' ' ' 'b' 'b'], that ends up like this
['' '' '' '' ''] because its also detected as a >=3 elem. consecutive line.
I know its due to the counter that keeps adding to the value it already has, but I couldnt come to any other solution till now
The code I have up to now is this:
**
public int[] checkRows() {
int[] results = new int[BOARD_SIZE];
for (int i = 0; i < BOARD_SIZE; i++) {
int counter = 1;
int counterHead = 1;
int j = 0;
while (j < BOARD_SIZE) {
if (j+1 < BOARD_SIZE) {
if (getBoard()[i][j].getType() == getBoard()[i][j+1].getType() && getBoard()[i][j].getType() != ' ') {
counter++;
}
}
if (counter >= 3)
deleteRows(i, j);
j++;
}
results[i] = counter;
}
return results;
}
**
So we want to see if there are 3 consecutive elements with the same value, but what it looks like to me is we are simply testing if there are three values in a row of the board that are equal. This works if we find a section of consecutive equivalent elements, but as soon as we find a space or non-equal element the logic fails.
Things we need to consider:
If the counter reaches 3 we can break out of your conditional and delete. This case is covered.
If your counter is updated from its original value (counter > 1) and never reaches 3 we know we must have encountered either a space or a non-equivalent value. In this case we should reset the counter and continue our test for the rest of the row.
Your problem is that you are not counting consecutive values, but rather pairs of consecutive values. For 'b', 'b', 'b', there are three b characters, but only 2 pairs of values, 1) the first and second b, and 2) the second and third b. Your counter should start at 1 rather than 0 because every value is by itself a run of 1 "consecutive" values. This, along with resetting the counter when you see a non-matching letter should give you the result you desire.
I haven't tried this as I don't have your data or the structures that this code uses, but try this:
public int[] checkRows() {
int[] results = new int[BOARD_SIZE];
for (int i = 0; i < BOARD_SIZE; i++) {
int counter = 1;
int counterHead = 1;
int j = 1;
while (j < BOARD_SIZE) {
if (j+1 < BOARD_SIZE) {
if (getBoard()[i][j].getType() == getBoard()[i][j+1].getType() && getBoard()[i][j].getType() != ' ') {
counter++;
}
else {
counter = 1;
}
}
if (counter >= 3)
deleteRows(i, j);
j++;
}
results[i] = counter;
}
return results;
}
First of all, I would suggest that for such complex problems, always try to break your problems and then merge those pieces together.
Below is the algorithm for your problem, just for simplicity and ease of explanation I used an Integer array which you can easily replace with a string or character array and change the condition.
A basic function for performing candyCrush Algorithm.
public void candyCrush(){
int[][] result = new int[][]{{1,2,2,2,2},
{1,2,2,2,1},
{1,2,1,1,1},
{2,1,2,1,1}
};
int count = 0;
for (int i = 0; i < result.length; i++) {
for (int j = 0; j < result[i].length; j++) {
if(result[i][j]==-1){
continue;
}
boolean hasRowElements = checkRowForCurrentIndex(result, i, j);
boolean hasColumnElements = checkColumnForCurrentIndex(result, i, j);
if(hasRowElements || hasColumnElements){
count++;
}
}
}
System.out.println(count);
}
private boolean checkRowForCurrentIndex(int[][] result, int row, int col) {
int forTop = row, forBottom = row;
int topStartingLimit = 0, bottomEndingLimit = 0;
while(forTop>= 0){
if(result[row][col] == result[forTop][col] && result[forTop][col]!=-1){
topStartingLimit = forTop;
}else{
break;
}
forTop--;
}
while(forBottom< result.length){
if(result[row][col] == result[forBottom][col] && result[forBottom][col]!=-1){
bottomEndingLimit = forBottom;
}else{
break;
}
forBottom++;
}
if(topStartingLimit==bottomEndingLimit){
return false;
}
if(bottomEndingLimit-topStartingLimit>=2 && bottomEndingLimit-topStartingLimit<= 6) {
deleteRows(result, topStartingLimit, bottomEndingLimit, row, col);
return true;
}
return false;
}
private void deleteRows(int[][] result, int topStartingLimit, int bottomEndingLimit, int row, int col) {
while(topStartingLimit<= bottomEndingLimit){
result[topStartingLimit][col] = -1;
topStartingLimit++;
}
return;
}
private boolean checkColumnForCurrentIndex(int[][] result, int row, int col) {
int forLeft = col, forRight = col;
int leftStartingLimit = 0, rightEndingLimit = 0;
while(forLeft>= 0){
if(result[row][col] == result[row][forLeft] && result[row][forLeft]!=-1){
leftStartingLimit = forLeft;
}else{
break;
}
forLeft--;
}
while(forRight< result[row].length){
if(result[row][col] == result[row][forRight] && result[row][forRight]!=-1){
rightEndingLimit = forRight;
}else{
break;
}
forRight++;
}
if(leftStartingLimit==rightEndingLimit){
return false;
}
if(rightEndingLimit-leftStartingLimit>=2 && rightEndingLimit-leftStartingLimit<= 6) {
deleteCols(result, leftStartingLimit, rightEndingLimit, row, col);
return true;
}
return false;
}
private void deleteCols(int[][] result, int leftStartingLimit, int rightEndingLimit, int row, int col) {
while(leftStartingLimit<= rightEndingLimit){
result[row][leftStartingLimit] = -1;
leftStartingLimit++;
}
return;
}
Definitely, you can optimize this code better by using the same function again for Rows and Cols, but for sake of simplicity. I tried to break everything so that the solution becomes more clear.
Hope this algorithm would work.

Invalidate an element forming a path if it is surrounded

I'm building a 2D grid game composed of cells in which players have to put tokens and try to contain (encircle) the opponent's tokens. Now each cell can have 3 states: empty, contains a red token or contains a blue token.
All cells that can form a "path" are in a list, and along that path I can draw lines (polygons) passing by the center of cells.
Also there is a list of contained tokens, the one being encircled,
Now I want to find a way to "invalidate" an encircled token so it can be ignored by path calculations
See examples below:
Blue tokens are encircled first, they cannot be apart of any further path calculation.
This cannot be allowed. First to contain, first to win.
All codes below are from the path class:
class Path extends Stack<int[]>{
private Token[][] grid;
//a path shorter than min can not surround any cell
private static final int MIN_PATH_LEGTH = 3;
//a collection of cells that has been tested
private ArrayList<int[]>checked;
//represents the cell where the search starts from
int[] origin;
//represents the token of the origin
Token originToken;
private int rows;
private int cols;
//represents the path bounds: min/max row/col in path
private int minPathRow, maxPathRow, minPathCol, maxPathCol;
Path(Token[][] grid){
this.grid = grid;
rows = grid.length;
cols = grid[0].length;
}
//search for a path
boolean findPath(int[] origin) {
this.origin = origin;
int row = origin[0] , col = origin[1];
//represents the token of the origin
originToken = grid[row][col];
//initialize list of checked items
checked = new CellsList();
boolean found = findPath(row, col);
if(found) {
printPath();
} else {
System.out.println("No path found");
}
return found;
}
//recursive method to find path. a cell is represented by its row, col
//returns true when path was found
private boolean findPath(int row, int col) {
//check if cell has the same token as origin
if(grid[row][col] != originToken) {
return false;
}
int[] cell = new int[] {row, col};
//check if this cell was tested before to avoid checking again
if(checked.contains(cell)) {
return false;
}
//get cells neighbors
CellsList neighbors = getNeighbors(row, col);
//check if solution found. If path size > min and cell
//neighbors contain the origin it means that path was found
if((size() >= MIN_PATH_LEGTH) && neighbors.contains(origin) ) {
add(cell);
return true;
}
//add cell to checked
checked.add(cell);
//add cell to path
add(cell);
//if path was not found check cell neighbors
for(int[] neighbor : neighbors ) {
boolean found = findPath(neighbor[0],neighbor[1]);
if(found) {
return true;
}
}
//path not found
pop(); //remove last element from stack
return false;
}
//use for testing
private void printPath() {
System.out.print("Path : " );
for(int[] cell : this) {
System.out.print(Arrays.toString(cell));
}
System.out.println("");
List<int[]> containedCells = getContainedWithin();
System.out.print(containedCells.size() +" cell contained : " );
for(int[] cell : containedCells) {
System.out.print(Arrays.toString(cell));
}
System.out.println("");
}
CellsList getPath() {
CellsList cl = new CellsList();
cl.addAll(this);
return cl;
}
}
The code below finds the neighbors of a cell (path.java):
//return a list of all neighbors of cell row, col
private CellsList getNeighbors(int row, int col) {
CellsList neighbors = new CellsList();
for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1 ) {
for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1 ) {
if(!((colNum == col) && (rowNum == row))) {
if(isWithinGrid (rowNum, colNum ) ) {
neighbors.add( new int[] {rowNum, colNum});
}
}
}
}
return neighbors;
}
private boolean isWithinGrid(int colNum, int rowNum) {
if((colNum < 0) || (rowNum <0) ) {
return false;
}
if((colNum >= cols) || (rowNum >= rows)) {
return false;
}
return true;
}
}
The below code finds all bounded cell by a path (all contained or encircled tokens) and their token is of the opposite color of the path:
List<int[]> getContainedWithin() {
//find path max and min X values, max and min Y values
minPathRow = grid[0].length; //set min to the largest possible value
maxPathCol = grid.length;
maxPathRow = 0; //set max to the largest possible value
maxPathCol = 0;
//find the actual min max x y values of the path
for (int[] cell : this) {
minPathRow = Math.min(minPathRow, cell[0]);
minPathCol = Math.min(minPathCol, cell[1]);
maxPathRow = Math.max(maxPathRow, cell[0]);
maxPathCol = Math.max(maxPathCol, cell[1]);
}
List<int[]> block = new ArrayList<>(25);
int[] cell = get(0);//get an arbitrary cell in the path
Token pathToken = grid[cell[0]][cell[1]]; //keep a reference to its token
//iterate over all cells within path x, y limits
for (int col = minPathCol; col < (maxPathCol); col++) {
for (int row = minPathRow; row < (maxPathRow); row++) {
//check cell color
Token token = grid[row][col];
if ((token == pathToken) || (token == Token.VIDE)) {
continue;
}
if (isWithinLoop(row,col)) {
block.add(new int[] {row, col});
}
}
}
return block;
}
//check if row, col represent a cell within path by checking if it has a
//path-cell to its left, right, top and bottom
private boolean isWithinLoop(int row, int col) {
if( isPathCellOnLeft(row, col)
&&
isPathCellOnRight(row, col)
&&
isPathCellOnTop(row, col)
&&
isPathCellOnBottom(row, col)
) {
return true;
}
return false;
}
}
If you need more elements, just let me now, I'll update with the necessary.
This requirement means that previous paths, affect current path calculations. It can be achieved in many ways. The easiest, within current program structure could be adding a static collection of contained cells in all paths.
See allContainedWithin and the way it is used in the code.
Also note that I refactored getContainedWithin() to be a getter, and moved its functionality to a new method findContainedWithin().
All changes have no effect on other classes.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Stack;
//a stack representing cells in the path
//each cell represented by [row,col]
class Path extends Stack<int[]>{
private Token[][] grid;
//a path shorter than min can not surround any cell
private static final int MIN_PATH_LEGTH = 3;
//a collection of cells that has been tested
private ArrayList<int[]>checked;
//represents the cell where the search starts from
int[] origin;
//represents the token of the origin
Token originToken;
private int rows;
private int cols;
//represents the path bounds: min/max row/col in path
private int minPathRow, maxPathRow, minPathCol, maxPathCol;
//a collection of all cells that are bounded by the path
//and their token is of the opposite color of the path
private List<int[]> containedWithin;
//a STATIC collection that holds all containedWithin cells, of
//current and previous paths
private static CellsList allContainedWithin = new CellsList();
Path(Token[][] grid){
this.grid = grid;
rows = grid.length;
cols = grid[0].length;
}
//search for a path
boolean findPath(int[] origin) {
this.origin = origin;
int row = origin[0] , col = origin[1];
//represents the token of the origin
originToken = grid[row][col];
//initialize list of checked items
checked = new CellsList();
boolean found = findPath(row, col);
if(found) {
//find bounded cells
findContainedWithin();
//update the collection all
allContainedWithin.addAll(containedWithin);
printPath();
} else {
System.out.println("No path found");
}
return found;
}
//recursive method to find path. a cell is represented by its row, col
//returns true when path was found
private boolean findPath(int row, int col) {
//check if cell has the same token as origin
if(grid[row][col] != originToken) {
return false;
}
int[] cell = new int[] {row, col};
//check if this cell was tested before to avoid checking again
if(checked.contains(cell)) {
return false;
}
//check if this cell was contained in previously calculated paths
if(allContainedWithin.contains(cell)) {
return false;
}
//get cells neighbors
CellsList neighbors = getNeighbors(row, col);
//check if solution found. If path size > min and cell
//neighbors contain the origin it means that path was found
if((size() >= MIN_PATH_LEGTH) && neighbors.contains(origin) ) {
add(cell);
return true;
}
//add cell to checked
checked.add(cell);
//add cell to path
add(cell);
//if path was not found check cell neighbors
for(int[] neighbor : neighbors ) {
boolean found = findPath(neighbor[0],neighbor[1]);
if(found) {
return true;
}
}
//path not found
pop(); //remove last element from stack
return false;
}
//return a list of all neighbors of cell row, col
private CellsList getNeighbors(int row, int col) {
CellsList neighbors = new CellsList();
for (int colNum = col - 1 ; colNum <= (col + 1) ; colNum +=1 ) {
for (int rowNum = row - 1 ; rowNum <= (row + 1) ; rowNum +=1 ) {
if(!((colNum == col) && (rowNum == row))) {
if(isWithinGrid (rowNum, colNum ) ) {
neighbors.add( new int[] {rowNum, colNum});
}
}
}
}
return neighbors;
}
private boolean isWithinGrid(int colNum, int rowNum) {
if((colNum < 0) || (rowNum <0) ) {
return false;
}
if((colNum >= cols) || (rowNum >= rows)) {
return false;
}
return true;
}
//use for testing
private void printPath() {
System.out.print("Path : " );
for(int[] cell : this) {
System.out.print(Arrays.toString(cell));
}
System.out.println("");
List<int[]> containedCells = getContainedWithin();
System.out.print(containedCells.size()+" cell contained : " );
for(int[] cell : containedCells) {
System.out.print(Arrays.toString(cell));
}
System.out.println("");
}
CellsList getPath() {
CellsList cl = new CellsList();
cl.addAll(this);
return cl;
}
//finds all cells that are bounded by the path
//and their token is of the opposite color of the path
private void findContainedWithin() {
containedWithin = new ArrayList<>();
//find path max and min X values, max and min Y values
minPathRow = grid[0].length; //set min to the largest possible value
maxPathCol = grid.length;
maxPathRow = 0; //set max to the largest possible value
maxPathCol = 0;
//find the actual min max x y values of the path
for (int[] cell : this) {
minPathRow = Math.min(minPathRow, cell[0]);
minPathCol = Math.min(minPathCol, cell[1]);
maxPathRow = Math.max(maxPathRow, cell[0]);
maxPathCol = Math.max(maxPathCol, cell[1]);
}
//todo remove after testing
System.out.println("x range: "+minPathRow + "-"
+ maxPathRow + " y range: " + minPathCol + "-" + maxPathCol);
int[] cell = get(0);//get an arbitrary cell in the path
Token pathToken = grid[cell[0]][cell[1]]; //keep a reference to its token
//iterate over all cells within path x, y limits
for (int col = minPathCol; col < (maxPathCol); col++) {
for (int row = minPathRow; row < (maxPathRow); row++) {
//check cell color
Token token = grid[row][col];
if ((token == pathToken) || (token == Token.VIDE)) {
continue;
}
if (isWithinLoop(row,col)) {
containedWithin.add(new int[] {row, col});
}
}
}
}
//returns a collection of all cells that are bounded by the path
//and their token is of the opposite color of the path
List<int[]> getContainedWithin() {
return containedWithin;
}
//check if row, col represent a cell with in path by checking if it has a
//path-cell to its left, right, top and bottom
private boolean isWithinLoop(int row, int col) {
if( isPathCellOnLeft(row, col)
&&
isPathCellOnRight(row, col)
&&
isPathCellOnTop(row, col)
&&
isPathCellOnBottom(row, col)
) {
return true;
}
return false;
}
private boolean isPathCellOnLeft(int cellRow, int cellCol) {
for ( int col = minPathCol; col < cellCol ; col++) {
if(getPath().contains(new int[] {cellRow, col})) {
return true;
}
}
return false;
}
private boolean isPathCellOnRight(int cellRow, int cellCol) {
for ( int col = cellCol; col <= maxPathCol ; col++) {
if(getPath().contains(new int[] {cellRow, col})) {
return true;
}
}
return false;
}
private boolean isPathCellOnTop(int cellRow, int cellCol) {
for ( int row =minPathRow; row < cellRow ; row++) {
if(getPath().contains(new int[] {row, cellCol})) {
return true;
}
}
return false;
}
private boolean isPathCellOnBottom(int cellRow, int cellCol) {
for ( int row = cellRow; row <= maxPathRow; row++) {
if(getPath().contains(new int[] {row, cellCol})) {
return true;
}
}
return false;
}
}
Note that I only run some basic testing like :
In addition to the previous answer, I would like to add an alternative which requires a deeper change in the program.
A better way to handle the requirement would be to change the representation of a cell. Instead of using int[]{row, col} , consider representing it by a Cell class which has attributes like row, col,token, contained etc.
A simple implementation of Cell could be :
public class Cell {
private int row, col;
private Token token;
private boolean isContained;
Cell(int row, int col) {
this(row, col, Token.VIDE);
}
Cell(int row, int col, Token token) {
this.row = Math.abs(row); //to allow only positve addresses
this.col = Math.abs(col);
this.token = (token == null) ? Token.VIDE : token;
}
int getRow() {
return row;
}
int getCol() {
return col;
}
Token getToken() {
return token;
}
boolean isContained() {
return isContained;
}
void setRow(int row) {
this.row = row;
}
void setCol(int col) {
this.col = col;
}
void setToken(Token token) {
this.token = token;
}
void setContained(boolean isContained) {
this.isContained = isContained;
}
int[] getAddress() {
return new int[] {row, col};
}
#Override
public String toString() {
return Arrays.toString(getAddress()) +"-"+ token;
}
#Override
public boolean equals(Object cell) {
if ((cell == null) || !(cell instanceof Cell)) {
return false;
}
return Arrays.equals(getAddress(), ((Cell)cell).getAddress());
}
#Override
public int hashCode() {
return 31*row + 17*col;
}
}
Note: This representation should be changed all across the program.
(Not tested)

Java Connect Four in console - Horizontal and Vertical winning conditions

I'm working on a Connect Four game for the console in Java. I have problems with the winning conditions, as I don't know how to program them. Here is my code my Main:
public class Main {
public static char[] playerNumber = new char[]{'1', '2'};
public static char[] Badge = new char[]{'X', 'O'};
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int moves = 7 * 6;
int whichPlayer = 0;
for (int i = 0; i < 10; i++) {
System.out.println(" FOUR IN A ROW");
System.out.println("-------------------------------------------------------");
System.out.println("Welcome to the amazing game Four In A Row:");
System.out.println("Enter a number between 0 and 6 for choosing a column.");
System.out.println();
Board board = new Board();
board.fillBoard();
board.presentBoard();
do {
// 1. get a badge
char Player = playerNumber[whichPlayer];
char badge = Badge[whichPlayer];
// 2. make a turn
board.makeTurn(badge, Player);
board.presentBoard();
// 3. Tjek om der er vinder
if (board.checkWinHorizontal() || board.checkWinVertical()) {
System.out.println("Player " + Player + " has won!");
break;
}
// 4. change the player
whichPlayer = 1 - whichPlayer;
// 5. decrease moves
--moves;
if (moves == 0) {
System.out.println("Game over, nobody has won.");
System.out.println("Do you want to play again? 'Y' or 'N':");
String newGame = scanner.nextLine();
if (newGame.equals("Y") || newGame.equals("y")) {
break;
}
if (newGame.equals("N") || newGame.equals("n")) {
System.out.println("Thanks for the game!");
return;
}
}
// 6. repeat
} while (true);
}
}
And here is my code for my Board class:
public class Board {
char[][] board = new char[6][7];
int column;
// Fills the empty spaces
public void fillBoard() {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
board[i][j] = ' ';
}
}
}
// Prints the board
public void presentBoard() {
for (int i = 0; i < 6; i++) {
System.out.print(" | ");
for (int j = 0; j < 7; j++) {
System.out.print(board[i][j] + " | ");
}
System.out.println();
System.out.print(" -----------------------------");
System.out.println();
}
}
// Turn
public void makeTurn(char badge, char Player) {
Scanner scanner = new Scanner(System.in);
do {
// 1. Ask for a column
System.out.println("Player " + Player + " turn: ");
column = scanner.nextInt();
// 2. Check if it's between 0 and 6
if (column > 6) {
System.out.println("That is not a valid number. Please enter a number between 0 and 6: ");
continue;
}
// 3. Place a badge
for (int i = 6 - 1; i >= 0; i--) {
if (board[i][column] == ' ') {
board[i][column] = badge;
return;
}
}
// If column is full
System.out.println("Column " + column + " is full. Try another column:");
} while (true);
}
// Check for vertical win
public boolean checkWinVertical() {
return verticalWin(5, column);
}
// Check for horizontal win
public boolean checkWinHorizontal() {
return horizontalWin(5,column);
}
// Conditions for vertical win
private boolean verticalWin(int x, int y) {
char charToCheck = board[x][y];
if (board[x-1][y] == charToCheck &&
board[x-2][y] == charToCheck &&
board[x-3][y] == charToCheck) {
return true;
}
return false;
}
// Conditions for horizontal win
private boolean horizontalWin(int x, int y) {
char charToCheck = board[x][y];
if (board[x][y+1] == charToCheck &&
board[x][y+2] == charToCheck &&
board[x][y+3] == charToCheck) {
return true;
}
return false;
}
I have succeeded in getting the game recognize a win horizontally and vertically at the bottom row of my array, but I don't know how to make the game recognize for the whole array. I'm only concentrating about the horizontal and vertical, as the diagonal is too complicated for me. And I don't know if this is the right approach or there is a better one.
Thanks!
Here's another solution. It's the same general idea as previously mentioned: loop through each row/column, checking for a streak of 4 in a row. Maybe this implementation will provide some other insight. Below, I've shown an example method checking the horizontal streaks. For vertical, you would iterate over the rows in the inner for loop instead.
public boolean checkWin(char badge) {
return checkHorizontalStreaks(board, badge)
|| checkVerticalStreaks(board, badge);
}
private boolean checkHorizontalStreaks(char[][] board, char badge) {
for (int row = 0; row < board.length; row++) {
// loop throught each row
int currentStreak = 0;
for (int col = 0; col < board[row].length; col++) {
// loop through each column in the row
if (board[row][col] == badge) {
// keep the streak of 'badge' going
currentStreak++;
if (currentStreak == 4) {
// winner
return true;
}
} else {
// restart the streak
currentStreak = 0;
}
}
}
return false;
}
And then update your Main class with
if (board.checkWin(badge)) {
System.out.println("Player " + Player + " has won!");
break;
}
I'd wager there is a more efficient way to determine a winner (perhaps by treating the grid as a graph and traversing it with some special logic). However, I suspect this may be enough for what you need. I'll spare you the output, but it worked with a few different test cases.
Possibly you could check all the adjacent fields around the last played field, so after the user did his turn. So for checking upwards you could do this:
public boolean checkUp(int rowPlayed, int columnPlayed){
boolean checked = false;
if(rowplayed + 1 <= maxrows){ //Checks if you didn't hit the top
if(board[rowPlayed+1][columnPlayed] != null){
if(board[rowPlayed+1][columnPlayed].getPlayer() == currentPlayer){
checked = true;
}
}
}
return checked;
}
and for example implemented like this:
public void checkWin(int rowPlayed, int columnPlayed){
boolean checkingWin = true;
int countWin = 0;
while(checkingWin){
if(checkUp(rowPlayed + countWin, columnPlayed)){
countWin++;
}
else{
checkingWin = false;
}
if(countWin == 4){
checkinWin = false;
//Insert win confirmation here
}
}
}
It's partially pseudo code because I don't know exactly how you handle things in your code, nor do I know if this is the best way to do it. But I hope it was of help for you.
This is a long answer and I'll go around the houses a bit so you can see how I reached my solution (which also expands to diagonal checking at the end).
I would use the last piece added as a starting point and work from there since checking all combinations is exhaustive and unnecessary.
Given the row and column of the last piece added I need to decide what I need to achieve.
I already know that the current row and column has the piece of the colour I'm looking for so I can ignore that.
For horizontal matching, I want to check I want to checking pieces to left and right in the same row have the same colour, and stop if the colour is different or there is no piece.
So imagine the following board (# = empty, R = Red piece, Y = Yellow piece:
6 # # # # # # # #
5 # # # # # # # #
4 # # # # # # # #
3 # # # # # # # #
2 # # # # # # # #
1 # # # # # # # #
0 Y R R R Y Y Y R
0 1 2 3 4 5 6 7
The last move was Yellow, row 0, col 4.
So I want to check left and right from [0][4] and see if the total number of consecutive pieces of the colour is 3, (not 4) since I know [0][4] is Yellow and can be discounted.
Based on this I can take a recursive approach where I check the adjacent to one side, then recursively do the same thing as long as I keep matching pieces of the same colour or do not encounter an empty slot.
I'll start of with a check to the right (to demonstrate):
private static final int COLS = 7;
private static final int ROWS = 6;
public enum Piece {RED, YELLOW}; // null is empty
private Piece[][] board = new Piece[ROWS][COLS]; // the board
private int checkRight(Piece piece, int row, int col) {
// assume valid row for now
col++; // moving col to the right
if (col >= COLS || board[row][col] != piece) {
// We're outside the limits of the column or the Piece doesn't match
return 0; // So return 0, nothing to add
} else {
// otherwise return 1 + the result of checkRight for the next col
return 1 + checkRight(piece, row, col);
}
}
Now I can perform the same to the left.
private int checkLeft(Piece piece, int row, int col) {
// assume valid row for now
col--; // moving col to the left
if (col < 0 || board[row][col] != piece) {
// We're outside the limits of the column or the Piece doesn't match
return 0; // So return 0, nothing to add
} else {
// otherwise return 1 + the result of checkLeft for the next col
return 1 + checkLeft(piece, row, col);
}
}
And to check a winner for horizontal, I could do this:
public boolean checkWinner(Piece piece, int row, int col) {
// if the sum is 3, we have a winner (horizontal only).
return checkRight(piece, row, col) + checkLeft(piece, row, col) == 3;
}
Ugh, there's a lot of repetition isn't there?
We can condense the two methods into one by introducing a new parameter direction which can change if we move col positive or negative through the values 1 and -1 respectively:
private int check(Piece piece, int row, int col, int direction) {
col += direction; // direction is either 1 (right) or -1 (left)
if (col < 0 || col >= COLS || board[row][col] != piece) {
return 0;
} else {
return 1 + check(piece, row, col);
}
}
Update checkWinner() for this new parameter:
private static final int POSITIVE = 1; // right at the moment
private static final int NEGATIVE = -1; // left at the moment
public boolean checkWinner(Piece piece, int row, int col) {
// if the sum is 3, we have a winner (horizontal only).
return check(piece, row, col, POSITIVE) + check(piece, row, col, NEGATIVE) == 3;
}
Now I could implement the same sort of logic for vertical, but instead stay on the same col and change the row. I will skip this part in detail and move onto a solution which includes this and diagonal checking.
This has been done using an enum called CheckType storing values for which row and col should change and is used by the check() method. e.g. for HORIZONTAL the column changes by 1 or -1 (depending upon the direction specified when check() is called) and the row remains 0.
public class Board {
public enum Piece {
RED, YELLOW
};
private enum CheckType {
HORIZONTAL(0, 1), VERTICAL(1, 0), DIAGNONAL_UP(1, 1), DIAGNONAL_DOWN(-1, 1);
int row;
int col;
CheckType(int row, int col) {
this.row = row;
this.col = col;
}
}
private static final int POSITIVE = 1;
private static final int NEGATIVE = -1;
private static final int ROWS = 6;
private static final int COLS = 7;
private Piece[][] board = new Piece[ROWS][COLS];
private boolean hasWinner = false;
public boolean hasWinner() {
return hasWinner;
}
private void checkWinner(Piece piece, int row, int col) {
// check all values of enum CheckType for a winner
// so HORIZONTAL, VERTICAL, etc..
int enumIndex = 0;
while (!hasWinner && enumIndex < CheckType.values().length) {
hasWinner = check(piece, row, col, POSITIVE, CheckType.values()[enumIndex])
+ check(piece, row, col, NEGATIVE, CheckType.values()[enumIndex]) == 3;
enumIndex++;
}
}
private int check(Piece piece, int row, int col, int direction, CheckType type) {
row += type.row * direction;
col += type.col * direction;
if (row >= ROWS || row < 0 || col >= COLS || col < 0 || board[row][col] != piece) {
return 0;
} else {
return 1 + check(piece, row, col, direction, type);
}
}
// for completeness, adding a Piece
public boolean add(Piece piece, int col) {
int row = 0;
while (row < ROWS && board[row][col] != null) {
row++;
}
if (row < ROWS) {
board[row][col] = piece;
// check for winner after successful add
checkWinner(piece, row, col);
}
return row < ROWS;
}
}
Hope this helps.

Eight Queens Java exception error

Im trying to make a program to solve the eight Queens problem but i keep getting and exception error every time i run the code this is what i have. im a little confused on what to do. any help to the direction will be greatly appreciated.
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException:-1 at
Queens.isUnderAttack(Queens.java:132) at
Queens.placeQueens(Queens.java:78) at
Queens.main(Queens.java:155)
public class Queens
{
//squares per row or column
public static final int BOARD_SIZE = 8;
//used to indicate an empty square
public static final int EMPTY = 0;
//used to indicate square contains a queen
public static final int QUEEN = 1;
private int board[][]; //chess board
public Queens()
{
//constructor: Creates an empty square board.
board = new int[BOARD_SIZE][BOARD_SIZE];
}
//clears the board
//Precondition: None
//Postcondition: Sets all squares to EMPTY
public void clearBoard()
{
//loops through the rows
for(int row = 0; row < BOARD_SIZE; row++)
{
//loops through the columns
for (int column = 0; column < BOARD_SIZE; column++)
{
board[row][column] = EMPTY;
}
}
}
//Displays the board
//precondition: None
//postcondition: Board is written to standard output;
//zero is an EMPTY square, one is a square containing a queen (QUEEN).
public void displayBoard()
{
for (int row = 0; row < BOARD_SIZE; row++)
{
System.out.println("");
for (int column = 0; column < BOARD_SIZE; column++)
{
System.out.print(board[row][column] + " ");
}
}
}
//Places queens in columns of the board beginning at the column specified.
//Precondition: Queens are placed correctly in columns 1 through column-1.
//Postcondition: If a solution is found, each column of the board contains one queen and
//method returns true; otherwise, returns false (no solution exists for a queen anywhere in column specified).
public boolean placeQueens(int column)
{
if(column >= BOARD_SIZE)
{
return true; //base case
}
else
{
boolean queenPlaced = false;
int row = 1; // number of square in column
while( !queenPlaced && (row < BOARD_SIZE))
{
//if square can be attacked
**if (!isUnderAttack(row, column))**
{
setQueen(row, column); //consider next square in column
queenPlaced = placeQueens(column+1);
//if no queen is possible in next column,
if(!queenPlaced)
{
//backtrack: remover queen placed earlier
//and try next square in column
removeQueen(row, column);
//++row;
}
}
row++;
}
return queenPlaced;
}
}
//Sets a queen at square indicated by row and column
//Precondition: None
//Postcondition: Sets the square on the board in a given row and column to Queen.
private void setQueen(int row, int column)
{
board[row][column] = QUEEN;
}
//removes a queen at square indicated by row and column
//Precondition: None
//Postcondition: Sets the square on the board in a given row and column to EMPTY.
private void removeQueen(int row, int column)
{
board[row][column] = EMPTY;
}
//Determines whether the square on the board at a given row and column is
//under attack by any queens in the columns 1 through column-1.
//Precondition: Each column between 1 and column-1 has a queen paced in a square at
//a specific row. None of these queens can be attacked by any other queen.
//Postcondition: If the designated square is under attack, returns true: otherwise return false.
private boolean isUnderAttack(int row, int column)
{
for (int y=0; y<BOARD_SIZE; y++)
{
if (board[row][y] == QUEEN || // possible horizontal attack
board[row-column+y][y] == QUEEN || // diagonal NW
**board[row+column-y][y] == QUEEN) // diagonal SW**
return true;
}
return false;
}
private int index(int number)
{
//Returns the array index that corresponds to a row or column number.
//Precondition: 1 <= number <= BOARD_SIZE.
//Postcondition: Returns adjusted index value
return number -1 ;
}
//main to test program
public static void main(String[] args)
{
Queens Q = new Queens();
**if(Q.placeQueens(0))**
{
System.out.println(Q);
}
else
{
System.out.println("Not Possible");
}
}
}
private boolean isUnderAttack(int row, int column)
{
for (int y = 0; y < BOARD_SIZE; y++)
{
if (board[row][y] == QUEEN)
return true; // possible horizontal attack
int x1 = row - column + y;
if (0 <= x1 && x1 < BOARD_SIZE && board[x1][y] == QUEEN)
return true; // diagonal NW
int x2 = row + column - y;
if (0 <= x2 && x2 < BOARD_SIZE && board[x2][y] == QUEEN)
return true; // diagonal SW
}
return false;
}
In your loop in 'isUnderAttack()', this is not good:
board[row-column+y][y]
board[row+column-y][y]
As 'y' goes from '0' to 'board size', it will mean indexes out of the bounds of your array (unless row and column is both 0) - as the error message clearly stated.
The loop should be corrected with the appropriate indexes, or by adding conditions to check that the indexing is in bounds:
int rowIndex = row-column+y;
if(rowIndex>=0 && rowIndex<BOARD_SIZE) {
if(board[row-column+y][y] == QUEEN) {
return true;
}
}
And of course the same for the other diagonal...
Damn, it is slow to type code on an android phone, even with a qwerty...
The value of either of these is going below zero
row-column+y
row+column-y
One of the Scenarios which is causing the error
Consider this case,board[row+column-y][y] == QUEEN
assume row=1,column=0 and y is 4 ,the index become -ve which is causing the error

Super Queen Puzzle array exception

I'm trying to implement the N*N queen algorithm with a little twist to it. In this version the queen can also move like a Knight can in chess.
Checking for the diagonals and rows seems to be fine, however when I try to check for a queen in the "L" position away from my current position, I get a "arrayindexoutofboundexception".
I'm not entirely sure if my check_Knight move is right. I can't seem to locate the error causing the issue in my code.
public class QueenGame {
/**
* #param args
*/
static int solution =0;
static boolean check_Queen(int row, int col, int queens[])
{
for(int i =1; i<col; i++)
{
if (queens[col-i] == row ||
queens[col-i] == row-i ||
queens[col-i] == row+i)
{
//flag = false;
return false;
}
}
return true;
}
static boolean check_KnightMove(int row, int col, int queens[])
{
if(queens[col-2] == (row -1) || queens[col-2] == (row+1))
{
return false;
}
return true;
}
static void placement(int col, int queens[], int n){
//int solution =0;
for (int row = 1; row <= n; row++) {
queens[col] = row;
if((check_Queen(row,col,queens)) == true)
{
if((check_KnightMove(row,col,queens)) == true)
{
if(col == n)
{
solution++;
}
else
{
placement(col+1,queens,n);
}
}
}
}
queens[col] = 0;
}
public static void main(String[] args) {
int solution =0;
Scanner scanner=new Scanner(System.in);
//System.out.print("Please enter N");
int n =10;
//int n = scanner.nextInt();// TODO Auto-generated method stub
//System.out.print(n);
int queens[] = new int[n+1];
placement(1,queens,n);
System.out.println("nQueens: solution=" + solution);
}
}
If col is 0 or 1, then you will get an ArrayIndexOutOfBoundsException. Add a check before accessing the array.
if ((col >= 2) && (queens[col-2] == (row-1) || queens[col-2] == (row+1)))

Categories