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
Related
Within a game on Gomoku a player has to get 5 in a row to win. Detecting diagonal win is a problem.
I have tried the code below which searches a 2d matrix from top right until it finds a player token we are looking for e.g. 1, it then proceeds to search from that point diagonally to find a winning row. This works fine providing the first '1' the algorithm comes across is a part of the winning line. If it is not, and just a random piece, the algorithm returns false as it does not continue searching.
How would Itake the last played move of the game and only search the diagonals relating to that move? Or possibly edit provided code to search the whole board.
public boolean is_diagonal_win_left(int player) {
int i = 0;
for (int col = board_size-1; col > (win_length - 2); col--) {
for (int row = 0; row < board_size-(win_length-1); row++) {
while (board_matrix[row][col] == player) {
i++;
row++;
col--;
if (i == win_length) return true;
}
i = 0;
}
}
return false;
}
//solved
public boolean is_diagonal_win_right(int player, int r, int c) {
int count = 0;
int row = r;
int col = c;
while ((row != 0) && (col != 0)) {
row--;
col--;
}
while ((row <= board_size - 1) && (col <= board_size - 1)) {
if (board_matrix[row][col] == player) count++;
row++;
col++;
}
return count == win_length;
}
You are correct: searching the board for the first counter is invalid; searching the entire board is a waste of time. Start at the most recent move. Let's call that position (r, c); the player's token is still player. Check in each of the eight functional directions to see how long is the string of player. For instance, you check the NW-SE diagonal like this:
count = 1 // We just placed one counter
row = r-1; col = c-1
while ( (row >= 0) and (col >= 0) and
(board_matrix[row][col] == player) )
count += 1
row = r+1; col = c+1
while ( (row < board_size) and (col < board_size) and
(board_matrix[row][col] == player) )
count += 1
// Note: gomoku rules require exactly 5 in a row;
// if you're playing with a"at least 5", then adjust this to >=
if (count == win_length) {
// Process the win
}
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.
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.
My question is boolean isLive = false; why is this assigned as false? I have seen very similer examples but I never quet understand it. could anyone explain what this line is doing?
/**
* Method that counts the number of live cells around a specified cell
* #param board 2D array of booleans representing the live and dead cells
* #param row The specific row of the cell in question
* #param col The specific col of the cell in question
* #returns The number of live cells around the cell in question
*/
public static int countNeighbours(boolean[][] board, int row, int col)
{
int count = 0;
for (int i = row-1; i <= row+1; i++) {
for (int j = col-1; j <= col+1; j++) {
// Check all cells around (not including) row and col
if (i != row || j != col) {
if (checkIfLive(board, i, j) == LIVE) {
count++;
}
}
}
}
return count;
}
/**
* Returns if a given cell is live or dead and checks whether it is on the board
*
* #param board 2D array of booleans representing the live and dead cells
* #param row The specific row of the cell in question
* #param col The specific col of the cell in question
*
* #returns Returns true if the specified cell is true and on the board, otherwise false
*/
private static boolean checkIfLive (boolean[][] board, int row, int col) {
boolean isLive = false;
int lastRow = board.length-1;
int lastCol = board[0].length-1;
if ((row >= 0 && row <= lastRow) && (col >= 0 && col <= lastCol)) {
isLive = board[row][col];
}
return isLive;
}
That's simply the default value, which may be changed if the test (if clause) is verified.
It defines the convention that cells outside the board aren't live.
This could have been written as :
private static boolean checkIfLive (boolean[][] board, int row, int col) {
int lastRow = board.length-1;
int lastCol = board[0].length-1;
if ((row >= 0 && row <= lastRow) && (col >= 0 && col <= lastCol)) {
return board[row][col];
} else {
return false;
}
}
First we assign boolean value as 'false' (ensuring default condition)
Then if valid condition is found we change the value, else default false will be returned.
boolean isLive = false;
It is a default value assigned to the boolean variable.
Just like:
int num = 0;
boolean isLive = false;
This is the default value of a boolean variable. If you declare as an instance variable it is automatically initialized to false.
why is this assigned as false?
Well, we do this, just to start with a default value, then we can change later on to true value, based on certain condition.
if ((row >= 0 && row <= lastRow) && (col >= 0 && col <= lastCol)) {
isLive = board[row][col];
}
return isLive;
So, in above code, if your if condition is false, then it is similar to returning a false value. Because, the variable isLive is not changed.
But if your condition is true, then the return value will depend upon the value of board[row][col]. If it is false, return value will still be false, else true.
Going through some past Java exercises so I can improve my programming in general and I've been stuck on this for quite a while. I'm going to post all of the source that's required, since this project is quite large and there's a lot of interfaces and subclasses that just offer to confuse.
public class Board {
public final static char NOUGHT = 'O';
public final static char CROSS = 'X';
public final static char EMPTY = ' ';
// Each cell is indexed as follows:
// 1 2 3
// 4 5 6
// 7 8 9
private char[][] grid; // a matrix to store the positions of the board
private int numOfMarks; // number of moves made on the board
private int lastMarkPosition; //position of last move maode in the board
public Board() {
grid = new char[3][3];
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
grid[row][col] = EMPTY;
}
}
numOfMarks = 0;
lastMarkPosition = 0;
}
//post: Returns true if the board is finished.
// A board is finished because either there is a winner or the board is full.
public boolean isFinished() {
return numOfMarks == 9 || getWinnerMark() != EMPTY;
}
//post: Records the position of the last mark made on the board.
public void setLastMarkPosition(int lastPosition){
lastMarkPosition = lastPosition;
}
//post: Returns the position of the last mark
public int getLastMarkPosition(){
return lastMarkPosition;
}
//post: Returns the mark ('X' or 'O') of the winner player if a winning condition exists in the board,
// returns EMPTY otherwise.
public char getWinnerMark() {
for (int i = 0; i < 3; i++) {
// check if there are three in a horizontal row
if (grid[i][0] != EMPTY && grid[i][0] == grid[i][1]
&& grid[i][1] == grid[i][2]) {
return grid[i][0];
}
// check if there are three in a vertical row
if (grid[0][i] != EMPTY && grid[0][i] == grid[1][i]
&& grid[1][i] == grid[2][i]) {
return grid[0][i];
}
}
// check if there are three in a diagonal row
if (grid[1][1] != EMPTY
&& (grid[1][1] == grid[0][0] && grid[1][1] == grid[2][2] || grid[1][1] == grid[0][2]
&& grid[1][1] == grid[2][0])) {
return grid[1][1];
}
// otherwise, return EMPTY as there is no winner
return EMPTY;
}
//post: Sets the given mark at the given position in the board
public void setMark(int pos, char mark) throws GameException {
if (numOfMarks == 9) {
throw new GameException("attempted to set mark on a full board.");
}
if (pos < 1 || pos > 9) {
throw new GameException(
"attempted to set mark in a wrong position: " + pos);
}
if (mark != NOUGHT && mark != CROSS) {
throw new GameException("attempted to set an invalid mark: "
+ String.valueOf(mark));
}
// perform board update
int row = (pos - 1) / 3;
int col = (pos - 1) % 3;
if (grid[row][col] != EMPTY) {
throw new GameException(
"attempted to set mark on a non-empty position: "
+ pos);
} else {
grid[row][col] = mark;
numOfMarks++;
}
}
//post: Returns the mark that is at a given position in the board
public char getMark(int pos) {
return grid[(pos-1)/3][(pos-1)%3];
}
//post: If the grid is not full, calculates whose turn is, based on the marks in the grid.
// Returns EMPTY if the board is already full.
public char getTurn() {
if (numOfMarks == 9) {
return EMPTY;
} else if (numOfMarks % 2 == 0) {
// by default, CROSS should go first
return CROSS;
} else {
return NOUGHT;
}
}
//post: Copy the board and returns it
public Board makeCopy() {
Board copy = new Board();
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
copy.grid[row][col] = this.grid[row][col];
}
}
copy.numOfMarks = this.numOfMarks;
return copy;
}
//post: Prints the given board
public static void display(Board board) {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
System.out.print(" ");
char mark = board.grid[row][col];
if (mark != EMPTY) {
System.out.print(mark);
} else {
System.out.print((row)*3+(col+1));
}
System.out.print(" ");
if (col < 2) {
System.out.print("|");
}
}
System.out.println();
if (row < 2) {
System.out.println("-----------");
}
}
}
GameTreeInterface
public interface GameTreeInterface {
//post: Returns the board at the root of the game tree.
public Board getRootItem();
//post: Expands the game tree fully by adding all possible boards in the game.
// It uses a recursive auxiliary method.
public void expand();
//pre: The game tree is fully expanded.
//post: Assigns a score to each board in the game tree.
// It uses a recursive auxiliary method.
public void assignScores();
//pre: Each board in the game tree has a score.
//post: Computes an array of positions (1..9) optimal available moves.
// These are the last mark positions in the children boards that have the highest score.
public int[] BestMoves();
//post: Returns the number of boards stored in a game tree.
// It uses a recursive auxiliary method.
public int size();
}
GameTree
public class GameTree implements GameTreeInterface {
private GameTreeNode root; // reference to the root board of a game tree
public GameTree(Board board) {
this.root = new GameTreeNode(board);
}
// post: Returns the board at the root of a game tree.
public Board getRootItem() {
return root.getBoard();
}
// post: Returns the number of boards stored in a game tree.
// It uses a recursive auxiliary method.
public int size() {
return sizeTree(root) + 1;
}
// post: Returns the number of boards stored in a game tree, excluded
// the root.
private int sizeTree(GameTreeNode node) {
int total = 0;
for (int i = 1; i < node.numberOfChildren(); i++) {
if (node.getChild(i).getBoard() != null)
total++;
}
return total;
}
// post: Expands the game tree fully by adding all possible boards in
// the game.
// It uses a recursive auxiliary method.
public void expand() {
expandTree(root);
}
// post: Expands the game tree from the given node by adding
// all the possible moves that the computer and the user player
// can make, until the game is finished, from the given node onwards.
private void expandTree(GameTreeNode node) {
if (!node.getBoard().isFinished()) {
char c = node.getBoard().getTurn();
for (int i = 1; i < 9; i++) {
if (node.getBoard().getMark(i) == Board.EMPTY) {
GameTreeNode n = new GameTreeNode(node.getBoard());
n.getBoard().setMark(i, c);
n.getBoard().setLastMarkPosition(i);
node.getChildren().add(2, n);
expandTree(n);
}
}
}
}
// pre: The game tree is fully expanded.
// post: Assigns a score to each board in the game tree.
// It uses a recursive auxiliary method.
public void assignScores() {
char player = (root.getBoard()).getTurn();
assignScoresTree(root, player);
}
// post: Assigns scores to each board in a game tree for the computer
// player.
private void assignScoresTree(GameTreeNode node, char player) {
Board board = node.getBoard();
if (board.isFinished()) {
// base case of recursion
// score 3 for a winning board for the given player,
// score 2 for a draw baord,
// score 1 for a losing board for the given player
char winner = board.getWinnerMark();
if (winner == Board.EMPTY) {
// this is a draw!
node.setScore(2);
} else {
node.setScore(winner == player ? 3 : 1);
}
}
else {
// tries to assign the scores to all the children boards
// first, recursively
int minScore = Integer.MAX_VALUE;
int maxScore = Integer.MIN_VALUE;
GenericList<GameTreeNode> children = node.getChildren();
for (Iterator<GameTreeNode> it = children.iterator(); it
.hasNext();) {
GameTreeNode child = it.next();
assignScoresTree(child, player);
// keeps track of the maximum and minimum scores
// of the children boards so far
int childScore = child.getScore();
if (childScore > maxScore)
maxScore = childScore;
if (childScore < minScore)
minScore = childScore;
}
// Assigns score to the current board in the recursion
// according to the player's turn
if (board.getTurn() == player) {
// get the maximum score as the player wants to
// win
node.setScore(maxScore);
} else {
// get the minimum score (as the player wants
// the opponent to lose;)
node.setScore(minScore);
}
}
}
// pre: Each board in the game tree has a score.
// post: Computes an array of positions (1..9) optimal available moves.
// These are the last mark positions in the children boards that have
// the highest score.
public int[] BestMoves() {
int maxScore = Integer.MIN_VALUE;
GenericList<GameTreeNode> highestScoreBoards = new GenericList<GameTreeNode>();
GenericList<GameTreeNode> children = root.getChildren();
for (Iterator<GameTreeNode> it = children.iterator(); it
.hasNext();) {
GameTreeNode nextBoard = it.next();
int curScore = nextBoard.getScore();
if (maxScore < curScore) {
maxScore = curScore;
highestScoreBoards.clear();
highestScoreBoards.add(1, nextBoard);
} else if (maxScore == curScore) {
highestScoreBoards.add(1, nextBoard);
}
}
int[] moves = new int[highestScoreBoards.size()];
for (int i = 0; i < moves.length; i++) {
Board board = (highestScoreBoards.get(i + 1))
.getBoard();
moves[i] = board.getLastMarkPosition();
}
return moves;
}
}
GameTreeNode
public class GameTreeNode{
private GameTreeItem item; // includes the board object and the score
private GenericList<GameTreeNode> children; //list of gameTreeNodes of possible next moves.
public GameTreeNode(Board newBoard) {
this.item = new GameTreeItem(newBoard);
this.children = new GenericList<GameTreeNode>();
}
//post: Returns the board stored in a GameTreeNode
public Board getBoard() {
return item.board;
}
//post: Returns the children stored in a GameTreeNode, as a list of GameTreeNodes
public GenericList<GameTreeNode> getChildren(){
return children;
}
//post: Returns the score of the board
public int getScore() {
return item.score;
}
//post: Sets the score of the board to be equal to the given score
public void setScore(int score) {
item.score = score;
}
//post: Removes all the children
public void removeChildren(){
children = null;
}
//post: Returns the number of children
public int numberOfChildren(){
return children.size();
}
//post: Returns the child at a given position in the list of children, as a
GameTreeNode
public GameTreeNode getChild(int i){
return children.get(i);
}
//Inner class for storing a board and the score
private class GameTreeItem{
private Board board;
private int score;
public GameTreeItem(Board newBoard) {
this.board = newBoard;
score = 0;
}
}
Apologies for the rather large amount of code but I don't think I could explain the problem without having everything here. There's still a lot of extra code for the GenericList but it's a pretty straightforward implementation for a generic linked list.
If anyone does go through this code, my problem is in the GameTree class with the sizeTree() method. I offered a solution but I think it's far too simple to be correct. As I understand it, a GameTreeNode includes a GameTreeItem containing the current state of play and a reference to a linked list of GameTreeNode. However, my specification says that I have to use recursion to implement this method but I'm not sure how. Would my method work since it goes through every single child of the root and checks for the boards?
I know it's a long shot but if anyone can offer any help I'd really appreciate it!