Java Connect Four in console - Horizontal and Vertical winning conditions - java

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.

Related

Determining a winner on a grid of 8x8 tic tac toe

I'm trying to get the diagonals of a matrix that is at least 5x5; trying to loop through new columns in multiple rows. The example I have is 8x8. I can't get it to trigger the middle section as recorded "hits" in a sequence diagonally. Here is my code. This is regarding button presses that then register a persons choice, based on a player color. It loops through fine on the edges like this:
JButton[][] buttons = new JButton[8][8];
String red = "";
String green = "";
string blue = "";
int col = 0;
for (int row = 0; row < 8; row++, col++) {
if (buttons[row][col].getBackground() == Color.RED) {
red = red.concat("+");
} else if (buttons[row][col].getBackground() == Color.GREEN) {
green = green.concat("+");
} else if (buttons[row][col].getBackground() == Color.BLUE) {
blue = blue.concat("+");
} else {
red = red.concat(",");
green = green.concat(",");
blue = blue.concat(",");
}
}
I am trying to solve it like this:
JButton[][] buttons = new JButton[8][8];
String red = "";
String green = "";
string blue = "";
int col = 0;
for (int loop = 0; loop < 5; loop++) {
for (int row = (0 + loop); row < 8; row++, col++) {
if (buttons[row][col].getBackground() == Color.RED) {
red = red.concat("+");
} else if (buttons[row][col].getBackground() == Color.GREEN) {
green = green.concat("+");
} else if (buttons[row][col].getBackground() == Color.BLUE) {
blue = blue.concat("+");
} else {
red = red.concat(",");
green = green.concat(",");
blue = blue.concat(",");
}
}
}
What I came up with is that I am always checking only from top to bottom and
Only to the right
Only to the bottom
Bottom down diagonally
Bottom left diagonally
So in my opinion this covers all possible wining configurations if the search is done field by field from the top.
I use 1D array to represent the gameboard and I always check if any of those checks can be performed or not, and skipping the one that.
Running example tries to randomly fill the board of given dimensions step by step and stops execution when winning condition is met (or it is a tie).
For checking winning conditions I just define the "field index progression" for the next field so I can do the ckeck as simple for loop.
public class TheGame {
static int side = 8;
static int fieldsCount = side * side;
static int[] fields = new int[fieldsCount];
static int toWin = 5;
public static void main(String[] args) {
//generate list of random picks
List<Integer> fieldsToBet = IntStream.range(0, fields.length).boxed().collect(Collectors.toList());
Collections.shuffle(fieldsToBet);
Iterator<Integer> it = fieldsToBet.iterator();
Arrays.fill(fields, ' ');
//place bets using list above
char nextBet = 0;
while (it.hasNext()) {
nextBet = nextBet == 'o' ? 'x' : 'o';
Integer idx = it.next();
fields[idx] = nextBet;
char winner=(char) gameOver(idx); //check the condition, if game is done, return the winner
if (winner > 0) {
System.out.println();
System.out.println("Winner is: " + nextBet);
break;
}
}
printout();
}
private static int gameOver(Integer i) {
if (fields[i] == ' ') {
return 0;
}
int column = i % side; //this can be ommited if using nested for loops
int row = i / side;
boolean canCheckRight = column + toWin <= side;
boolean canCheckDown = row <= side - toWin;
boolean canCheckLeft = column + 1 - toWin >= 0;
boolean canCheckRightDownDiagonal = canCheckDown && canCheckRight;
boolean canCheckLeftDownDiagonal = canCheckDown && canCheckLeft;
boolean itsEnd =
canCheckRight && check(i, toWin, idx -> idx += 1) || //every field to check is just "next field"
canCheckDown && check(i, toWin, idx -> idx += side) || // every field to check is in next row
canCheckRightDownDiagonal && check(i, toWin, idx -> idx += side + 1) || // next field to check is in next row skewed by 1 to the right
canCheckLeftDownDiagonal && check(i, toWin, idx -> idx += side - 1);// next field to check is in next row skewed by 1 to the left
return itsEnd ? fields[i]:0;
}
private static boolean check(int idx, int iterations, Function<Integer, Integer> nextCoordinateProducer) {
int figure = fields[idx];
if (figure == ' ') {
return false;
}
for (int i = 1; i < iterations; i++) {
idx = nextCoordinateProducer.apply(idx);
if (figure != fields[idx]) {
return false;
}
}
return true;
}
public static void printout() {
for (int i = 0; i < fieldsCount; i += side) {
System.out.println(Arrays.stream(fields)
.skip(i)
.limit(side)
.boxed()
.map(v -> Character.valueOf((char) v.intValue()).toString())
.collect(Collectors.joining("|")));
}
}
}

TicTacToe.java issues with checkWinner method

So I created a checkWinner method, using 'row' and 'col' private variables so I can locate the 'curPlayer' position in the 2D array.
import java.util.Scanner;
public class TicTacBoard
{
private char[][] board; // 2-D array of characters
private char curPlayer; // the player whose turn it is (X or O)
// added so I can locate the current player location in the board
private int row;
private int col;
// Constructor: board will be size x size
public TicTacBoard(int size)
{
board = new char[size][size];
// initialize the board with all spaces:
for(row=0; row < board.length; row++)
for(col=0; col < board[row].length; col++)
board[row][col] = ' ';
curPlayer = 'X'; // X gets the first move
}
public void playGame()
{
display();
do
{
takeTurn();
display();
}while(!checkWinner(row, col));
}
/////// display ////////
// Display the current status of the board on the
// screen, using hyphens (-) for horizontal lines
// and pipes (|) for vertical lines.
public void display()
{
System.out.println();
dispRow(0);
System.out.println("-----");
dispRow(1);
System.out.println("-----");
dispRow(2);
System.out.println();
}
// Display the current status of row r of the board
// on the screen, using hyphens (-) for horizontal
// lines and pipes (|) for vertical lines.
private void dispRow(int r)
{
System.out.println(board[r][0] + "|" + board[r][1]
+ "|" + board[r][2]);
}
/////// takeTurn ////////
// Allow the curPlayer to take a turn.
// Send output to screen saying whose turn
// it is and specifying the format for input.
// Read user's input and verify that it is a
// valid move. If it's invalid, make them
// re-enter it. When a valid move is entered,
// put it on the board.
public void takeTurn()
{
Scanner scan = new Scanner(System.in);
int row, col;
boolean invalid;
do{
invalid = false; // assume correct entry
System.out.println("It is now " + curPlayer + "'s turn.");
System.out.println("Please enter your move in the form row column.");
System.out.println("So 0 0 would be the top left, and 0 2 would be the top right.");
row = scan.nextInt();
col = scan.nextInt();
if(row < 0 || col < 0 || row > 2 || col > 2)
{
System.out.println("Invalid entry: row and column must both be between 0 and 2 (inclusive).");
System.out.println("Please try again.");
invalid = true;
}
else if(board[row][col] != ' ')
{
System.out.println("Invalid entry: Row " + row + " at Column " + col
+ " already contains: " + board[row][col]);
System.out.println("Please try again.");
invalid = true;
}
}while(invalid);
// Now that input validation loop is finished, put the move on the board:
board[row][col] = curPlayer;
// Switch to the other player (take turns):
if(curPlayer == 'X')
curPlayer = 'O';
else
curPlayer = 'X';
}
// If the game is over, print who won (if anyone),
// and return true. If the game is not over, return false.
public boolean checkWinner(int row, int col)
{
// YOUR CODE GOES HERE
int x = row;
int y = col;
// board length is always 3 here
// check winner on column
for (int i = 0; i < board.length; i++) {
if (board[x][i] != curPlayer)
break;
if (i == board.length - 1)
System.out.println("Player " + curPlayer + " wins!");
return true;
}
//check winner on row
for (int i = 0; i < board.length; i++) {
if (board[i][y] != curPlayer)
break;
if (i == board.length - 1)
System.out.println("Player " + curPlayer + " wins!");
return true;
}
// checks winner on diagonal up
if (x == y) {
for (int i = 0; i < board.length; i++) {
if (board[i][i] != curPlayer)
break;
if (i == board.length - 1)
System.out.println("Player " + curPlayer + " wins!");
return true;
}
}
// check winner on diagonal down
if (x + y == board.length - 1){
for (int i = 0; i < board.length; i++) {
if (board[i][(board.length-1)-i] != curPlayer)
break;
if (i == board.length - 1)
System.out.println("Player " + curPlayer + " wins!");
return true;
}
}
// checks if board is full
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board.length; j++) {
if (board[i][j] == '-')
System.out.println("Nobody won, game ends in a draw!");
return true;
}
}
return false;
}
}
The code works but I while I was checking I got this:
| |
-----
| |
-----
| |
It is now X's turn.
Please enter your move in the form row column.
So 0 0 would be the top left, and 0 2 would be the top right.
2 0
| |
-----
| |
-----
X| |
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at TicTacBoard.checkWinner(TicTacBoard.java:126)
at TicTacBoard.playGame(TicTacBoard.java:43)
at Main.main(Main.java:14)
I thought the board length is always 3 with the location ranging from 0 to 3. Any solutions to this error? Any more efficient ways to do this? Please let me know!
You have a "shadowing" problem - that is, you're shadowing the instance fields row and col with local variables in your takeTurn method.
In it's current state...
// Constructor: board will be size x size
public TicTacBoard(int size) {
board = new char[size][size];
// initialize the board with all spaces:
for (row = 0; row < board.length; row++) {
for (col = 0; col < board[row].length; col++) {
board[row][col] = ' ';
}
}
curPlayer = 'X'; // X gets the first move
}
after the constructor has run, row and col will be 3, but in takeTurn, you define row and col as local variables...
public void takeTurn() {
Scanner scan = new Scanner(System.in);
int row, col;
boolean invalid;
This means, that when you call checkWinner in the playGame method...
public void playGame() {
display();
do {
takeTurn();
display();
} while (!checkWinner(row, col));
}
You're passing the instance field values (of 3/3) and everything breaks.
So, the "quick" solution would be to remove the local declaration of row/col from takeTurn
public void takeTurn() {
Scanner scan = new Scanner(System.in);
//int row, col;
boolean invalid;
You could also fix this in the constructor, but making row/col local variables
for (int row = 0; row < board.length; row++) {
for (int col = 0; col < board[row].length; col++) {
board[row][col] = ' ';
}
}
but at some point, you need to update the row/col value for the player, but I might consider passing this information back from takeTurn rather than trying to use instance fields.
You also have a subtle, but common bug in your if statements. Without brackets, { and }, only the line IMMEDIATELY following the if statement will be executed when the conditional statement above is true. Your INDENTATION, however, indicates that you expected a different behavior.
For instance, your very first for loop is:
for (int i = 0; i < board.length; i++) {
if (board[x][i] != curPlayer)
break;
if (i == board.length - 1)
System.out.println("Player " + curPlayer + " wins!");
return true;
}
Here, only the System.out.println() line is executed when the if statement is true. The indentation of the return true; statement indicates that you expect it to only run with the println(), only when the conditional is true.
The return true; line is NOT dependent upon the preceding if statement, though, because it is not within brackets and the if statement only runs the line immediately following it. This means that the for loop is only ever running ONE ITERATION because the return line is STAND-ALONE and executes every single time, regardless of how those if statements evaluate.
You should ALWAYS, ALWAYS, ALWAYS add brackets to your if statements, even if they are "one-liners". With that in mind, I'd expect it to look more like:
for (int i = 0; i < board.length; i++) {
if (board[x][i] != curPlayer) {
break;
}
if (i == board.length - 1) {
System.out.println("Player " + curPlayer + " wins!");
return true;
}
}
Now the return line is only executed when the preceding if statement is true.

N-Queens using a stack, cannot find the bug

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.

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

TicTacToe using two dimensional arrays

I am trying to create a program that does the game TicTacToe. I have finished creating
all the methods and I just need to create the driver program. Before creating the
driver program, I tried to just print the board along with a character but I don't
think my methods are correct. Here is what my error looks like:
java.lang.ArrayIndexOutOfBoundsException: 3
at TicTacToeBoard.move(TicTacToeBoard.java:75)
at TicTacToe.main(TicTacToe.java:24)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at edu.rice.cs.drjava.model.compiler.JavacCompiler.runCommand(JavacCompiler.java:271)
Here are my two programs:
This is my driver program that I can't seem to complete. The last thing that will be shown is the
template so that you have the idea of how each program works.
class TicTacToe
{
public static void main(String [] args)
{
//System.out.println("Welcome! Tic-Tac-Toe is a two player game.");
//System.out.println("Enter player one's name: ");
TicTacToeBoard game = new TicTacToeBoard();
System.out.println(game.toString());
//int count = 0;
game.move('x', 1, 3);
// game.move('o', 1, 1);
/* while (game.gameWon() !true || count != 9)
{
System.out.print(game.move());
System.out.print(game.isEmpty());
}*/
}
}
This is where all the methods are......
class TicTacToeBoard
{
private char [][] board = new char[3][3];
String b;
// This a new constructor that creates a tic-tac-toe board
public TicTacToeBoard()
{
for (int rows = 0; rows < board.length; rows++)// creates rows
{
for (int columns = 0; columns <board[rows].length;columns++)// creates columns
{
//System.out.println("| ");
board[rows][columns] = ' ';
//System.out.println(" |\n" );
}
}
}
// creates a string form of the tic-tac-toe board and allows the user
// to access it during the game.
public String toString()
{
String b = "";
// creates a vertical bar at the beginning and the end of each row
for (int rows = 0; rows < board.length; rows++)
{
b += "| ";
// adds a space for each row and column character in tic-tac-toe board.
for (int columns = 0; columns < board[rows].length; columns++)
{
b += board[rows][columns] + " ";
}
b += "|\n";// prints a | space space space | and breaks off to create two new lines.
}
return b; // prints the tic-tac-toe board to be accessed by the user.
}
String move(char x, int rows, int columns)
{
String b = "";
// creates a vertical bar at the beginning and the end of each row
for (int r = 0; r < board.length; r++)
{
b += "| ";
for (int c = 0; c < board[r].length; c++)
{
b += board[r][c] + " "; //prints 3 spaces on each line.
// prints string character from user input if row and column not equal to zero
if (board[rows - 1][columns - 1] >= 0 && board[rows - 1][columns - 1] <= 2 )
{
board[rows - 1][columns - 1] = x;// prints character in the specified index from user input
b += board[rows - 1][columns - 1];// prints out the board and the new character in specified space.
}
else if (board[rows - 1][columns - 1] < 0) // makes user pick another choice
return "ILLEGAL MOVE, TRY AGAIN!";
// adds a space for each row and column character in tic-tac-toe board.
}
b += "|\n";// prints a | space space space | and breaks off to create two new lines.
}
return b; // prints the tic-tac-toe board to be accessed by the user.
}
// checks if a space character is empty
void isEmpty(char x, int row, int col)
{
if (board [row - 1][col - 1] == ' ')
board[row - 1][col - 1] = x;
else // makes user pick another row and column if space character is not empty
System.out.println("ILLEGAL CHOICE, PICK AGAIN!");
}
// checks if game is won
public boolean gameWon(int row, int col)
{
if ((board[2][0] == board[1][1]) && (board[2][0] == board[0][2]))
return true;
else if ((board[2][0] != board[1][1]) && (board[2][0] != board[0][2]))
return false;
if ((board[2][2] == board[1][1])&& (board[2][2] == board[0][0]))
return true;
else if ((board[2][2] != board[1][1])&& (board[2][2] != board[0][0]))
return false;
if ((board[0][0] == board[1][0]) && (board[0][0] == board[2][0]))
return true;
else if ((board[0][0] != board[1][0]) && (board[0][0] != board[2][0]))
return false;
if ((board[0][1] == board[1][1]) && (board[0][1] == board[2][1]))
return true;
else if ((board[0][1] != board[1][1]) && (board[0][1] != board[2][1]))
return false;
if ((board[0][2] == board[1][2]) && (board[0][2] == board[2][2]))
return true;
else if ((board[0][2] != board[1][2]) && (board[0][2] != board[2][2]))
return false;
if ((board[0][0] == board[0][1]) && (board[0][0] == board[0][2]))
return true;
else if ((board[0][0] != board[0][1]) && (board[0][0] != board[0][2]))
return false;
if ((board[1][0] == board[1][1]) && (board[1][0] == board[1][2]))
return true;
else if ((board[1][0] != board[1][1]) && (board[1][0] != board[1][2]))
return false;
if ((board[2][0] == board[2][1]) && (board[2][0] == board[2][2]))
return true;
else
return false;
}
}
Here is the template for the whole thing!!!!!
class TicTacToe
{
public static void main (String [] args)
{
TicTacToeBoard b = new TicTacToeBoard();
while (game not over)
{
swtich player
increment turn counter
until user enters a valid move
{
prompt for move
}
make move
b.makeMove (player, row, col);
print board
System.out.println(b);
}
print outcome
}
}
class TicTacToeBoard
{
private char [][] board = ...;
public TicTacToeBoard()
{
initialize board with spaces
}
public void makeMove (char c, int row, int col)
{
store symbol in specified position
}
public boolean isEmpty(int row, int col)
{
return true if square is unfilled
}
public boolean gameWon()
{
check board for a win
}
public String toString ()
{
return String representation of board
}
}
Programming languages are unforgiving for errors and force rigor and care on us.
Your code is quite difficult for us to read and thus for both us and you to debug, starting with your indentation which is all over the place, but there are also careless errors, especially this one:
for (int r = 0; r < board.length; rows++)
Do you see what is wrong here? r is not the same as rows, and you can't use one as the index for the loop and then increment the other. You're using both of these variables inside of the loop. There are several other careless errors in the code as well.
I recommend that you start over but be much more careful with your code and be especially careful with your indentation. If you don't line up your curly braces correctly, you will not see when one code block ends and another begins (nor will we!).
Oh, and next time, please let us know which lines of your code are causing your error. It will be much easier to help you if we don't have to guess this information.
Edit
Your new code indentation is some better, but still is off. This is what you have:
String move(char x, int rows, int columns)
{
String b = "";
// creates a vertical bar at the beginning and the end of each row
for (int r = 0; r < board.length; r++)
{
b += "| ";
for (int c = 0; c < board[r].length; c++)
{
b += board[rows][columns] + " ";
and this is what I recommend:
String move(char x, int rows, int columns) {
String b = "";
// creates a vertical bar at the beginning and the end of each row
for (int r = 0; r < board.length; r++) {
b += "| ";
for (int c = 0; c < board[r].length; c++) {
// let's check to see what the variables hold!
System.out.printf("rows: %d, columns %d, r: %d, c: %d%n", rows, columns, r, c);
b += board[rows][columns] + " "; // **** the offending line ****
Even more important, note the result from the printf statement followed immediately by the exception:
rows: 1, columns 3, r: 0, c: 0
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
So here it is the column variable that holds the value of 3 and causes your array to explode. You will need to track back to see how you call this method and why it is passing a 3 into the column parmaeter.
Edit 2
On re-review of your latest post, you're still hard coding your move method to accept a 3 as the column parameter:
game.move('x', 1, 3);
Fix that first and foremost. That parameter can't be 3.

Categories