I am creating a boardgame which has two different coloured pieces, black and red.
If a player surrounds the opponents pieces with his own, either horizontally or vertically, the pieces are removed. Here are some pictures to demonstrate this:
My board is a simple 2d integer array, with values of 0, 1 or 2 depending on whether the space is empty, has a red piece or a black piece.
This is the code I have come up with so far, however I am getting issues with out of bounds errors and it also does not account for capturing more than one piece.
static final int EMPTY = 0, BLACK = 1, RED = 2, ROW = 9, COLUMN = 9;
board = new int[ROW][COLUMN];
public void captureRedPieces() {
for(int i = 0; i < ROW; i++) {
for(int j = 0; j < COLUMN; j++) {
if(i <= ROW - 2) {
if(board[i][j] == 1 && board[i + 1][j] == 2 && board[i + 2][j] == 1) {
board[i + 1][j] = EMPTY;
}
}
if(i <= COLUMN - 2) {
if(board[i][j] == 1 && board[i][j + 1] == 2 && board[i][j + 2] == 1) {
board[i][j + 1] = EMPTY;
}
}
}
}
}
Could anyone help me come up with a better solution for capturing pieces?
You are testing whether i <= ROW - 2, but then you are using i+2 as an index for your check. This means that if i is 7, which is equal to ROW - 2 and passes your if test, then i+2 will be 9, which is out of bounds because the array only goes 0 to 8.
So you should correct this to i < ROW - 2 rather than <=.
Also, you have this code:
if(i <= COLUMN - 2) {
if(board[i][j] == 1 && board[i][j + 1] == 2 && board[i][j + 2] == 1) {
board[i][j + 1] = EMPTY;
}
}
This should be a condition on j, not on i - and it has to be fixed in the same way I told you about i - < instead of <=.
Note: since your rules state that a "surrounding" state is a capture only after the surrounding color makes a move, perhaps you should change your approach: you should only check the position where black made a move, to match with positions around it. Of course, you'll still have to make sure you don't go out of bounds. Your current approach might mark places that are "surrounded" since previous moves, and that would be wrong per the rules.
So your method should be declared like so:
public void captureRedPieces(int blackMoveRow, int blackMoveCol )
Probably you have to replace i <= COLUMN - 2 with j <= COLUMN - 2. Seems that capturing occurs after the new move. If you know the exact position of the new piece, then no need to iterate over the whole board. You just need to check nearby pieces in the same row and in the same column where the new piece was placed.
Related
I am creating a simple Connect Four program in Kotlin using a 2D array to print the board. Everything works fine except for my horizontal win-checker function. Because of the logic I am using, I get (index out of bounds) errors when trying to put your piece on the first column, because it is trying to check the next column in the array, but there is none. Is there a better way to handle checking for a winner? This is my function:
fun checkWinsHorizontal() {
for(row in 0 until gameBoard.size) {
for(col in 0 until gameBoard.size){
// if this spot is taken up by an "X", and the horizontally adjacent spaces are the same, declare winner
if (gameBoard[row][col] == "X" && (gameBoard[row][col] == gameBoard[row][col - 1] && gameBoard[row][col] == gameBoard[row][col - 2] && gameBoard[row][col] == gameBoard[row][col - 3]) ){
printBoard()
println("YOU WIN")
winner = true
return
}
// same thing as above but for a "computer" opponent
else if (gameBoard[row][col] == "O" && gameBoard[row][col] == gameBoard[row][col - 1] && gameBoard[row][col] == gameBoard[row][col - 2] && gameBoard[row][col] == gameBoard[row][col - 3]){
printBoard()
println("COMPUTER WINS")
winner = true
return
}
}
}
}
The comments above explain why you're getting the index out of bounds (you have hard coded values like gameBoard[row][col - 1] but col might be 0). I suggest a few fixes:
First, there's no need to check every cell on the board after each move. The only way a player can win is if the piece they just placed completes a row, column, or diagonal. So I suggest you check only potential wins involving that cell.
To do that for a row you could have something like:
fun completesRow(row: Int, col: Int) : Boolean {
var count: Int = 1
val symbol = gameBoard[row][col]
// First move left - now we check that the symbols (X or O) match
// AND that we're within bounds.
var curCol = col - 1
while (curCol >= 0 && gameBoard[row][curCol] == symbol) {
++count
if (count == 4) {
return true
}
--curCol
}
// same thing to the right; numColumns is assumed to be the number of
// columns in the board.
curCol = col + 1
while (curCol < numColumns && gameBoard[row][curCol] == symbol) {
++count
if (count == 4) {
return true
}
++curCol
}
// if you got here there weren't 4 in a row
return false
}
Note: the above is untested - I doubt it even compiles, but hopefully it's instructive.
You can also further generalize this if you want. Instead of having different functions that move left/right, up/down, and diagonally you could create Iterator instances for those movements and then have a single function that takes 2 iterators (one moving left and one right for example) and it can do the checks using that. That way you can use the same exact method to check a horizontal, vertical, or diagonal win.
I have some code that is supposed to find the smallest of the 8 neighboring cells in a 2D array. When this code runs, the smallest is then moved to, and the code run again in a loop. However when it is run the code ends up giving a stack overflow error as it keeps jumping between two points. This seems to be a logical paradox as if Y < X then X !< Y. So it think it is my code at fault, rather than my logic. Here's my code:
private Point findLowestWeight(Point current) {
float lowest = Float.MAX_VALUE;
Point ret = new Point(-1, -1);
LinkedList<Point> pointList = new LinkedList<Point>();
for (int i = -1; i <= 1; i++) {
for (int j = -1; j <= 1; j++) {
if (!(i == 0 && j == 0)) {
if ((current.x + i >= 0 && current.x + i <= imageX - 2)
&& (current.y + j >= 0 && current.y + j <= imageY - 2)) {
pointList.add(new Point(current.x + i, current.y + j));
}
}
}
}
for (Point p : pointList){
if (map[p.x][p.y] < lowest){
lowest = map[p.x][p.y];
ret = p;
}
}
return ret;
}
You need a stopping case.
find the smallest of the 8 neighboring cells in a 2D array. When this code runs, the smallest is then moved to, and the code run again in a loop
is a fine way to start but says nothing about stopping.
Do you care about the value of the current cell? If so you need to check 9 not 8. If you simply want to move down hill then you need to check where you've been or any flat multi-cell valley will put you into an infinite loop. Consider only moving if moving down.
If you truly don't care where you are then even a single cell valley will put you into an infinite loop as you bounce in and out of it. In which case you'd need some other stopping condition. Consider stopping after imageX * imageY iterations.
Do you move even if the smallest neighbour is greater than the value in the center?
Example:
2 2 2 2
2 0 1 2
2 2 2 2
You start with center cell 0. The smallest neighbour is 1. If you move to 1, the smallest neighbour is 0. You can continue endless.
Probably you should not move, if the smallest neighbour is greater than the current cell.
I am trying to search up to the right diagonally in my multidimensional array of characters in java. I added in an if statement to make sure it stays within the bounds of the array, but I am getting an index out of bounds error. Can someone tell me what is wrong with my code?
// search diagonal up to right
if ((row - 1 >=0) && (col + 1 <= board[col].length)) {
boolean foundWord = true;
for (int letters = 0; letters < word.length(); letters++) {
if (word.charAt(letters) != board[row - 1][col + 1]) {
foundWord = false;
break;
}
}
if(foundWord) {
return word + " Found at: " + Arrays.toString(new int[] {row,col});
}
} // end search diagonal up to right
Array numeration starts from 0, so here col + 1 <= board[col].length you should use board[col].length-1
Arrays in java are zero-based, that is their indexes start from zero and therefore the highest index is length - 1.
Change the comparison operator from <= to <, so it doesn't reach length:
if ((row - 1 >=0) && (col + 1 = board[col].length)) {
// ^---- change here
I figured it out. This is my solution.
This line:
if ((row - 1 >=0) && (col + 1 <= board[col].length)) {
Was changed to this:
if((row - word.length() <= 0) && (col + word.length() <= board[row].length)) {
I took a programming class, and I'm revisiting old programs that I did not quite get right. This one is a Game Of Life program, and I have a question about code cleanup.
I need to make sure that an array element is in bounds before checking whether its neighbor's boolean value is true or false. I have a statement to check if firstGen[0][0]'s top-left (up one row, left one column) is in bounds. Is there an easier or more elegant way to check if an element is in bounds or to restrict the element checks to the boundaries of a given array without using four && conditionals per if statement?
Note that I have only changed the first if statement thus far, so there may be errors elsewhere. I also excluded the boundary checks for the other neighbors.
public static boolean[][] generation(boolean[][] firstGen)
{
int length = firstGen.length;
boolean[][] newGen = new boolean[length][length];
for (int j = 0; j < firstGen[0].length; j++)
{ for (int i = 1; i < firstGen.length; i++)
{
int count = 0;
if ((i-1 >= 0) && (i-1 < length) && (j-1 >= 0) && (j-1 < length)) //top-left element exists
{ if (newGen[i-1][j-1] == true) count++; } //increment `count` if top-left element is true
if ((newGen[i][j] == false) && (count == 3)) newGen[i][j] = true;
else if ((newGen[i][j] == true) && (count == 1)) newGen[i][j] = false;
else if ((newGen[i][j] == true) && (count > 3)) newGen[i][j] = false;
else break;
}
}
return newGen;
}
If i and j are in bounds, then you know for sure that i - 1 < length and j - 1 < length are both true.
Also:
i - 1 >= 0 can be written i > 0
if (condition == true) can be rewritten if (cond)
So you could replace:
if ((i-1 >= 0) && (i-1 < length) && (j-1 >= 0) && (j-1 < length)) //top-left element exists
{ if (newGen[i-1][j-1] == true) count++; } //increment `count` if top-left element is true
by:
//increment `count` if top-left element is true
if (i > 0 && j > 0 && newGen[i-1][j-1]) count++;
That's the best way I can think of to check if its out of bounds, but an alternative method in general, and one that I think gives programs like the Game of Life more exciting outcomes, is adding periodic boundaries. Basically this means that if you walk off one edge, you end up on the other side (like in pac-man). It sounds complicated, but really all it takes is the % function, which returns the remainder of division between the two numbers given.
So:
27 % 5 = 2;
So for adding periodic boundries you would update x and y positions like this:
x = (x + xStep + horizontalSize) % horizontalSize;
y = (y + yStep + verticalSize) % verticalSize;
Where xStep and yStep are +1 or -1 depending on what direction you want to go. (this works nicely with a for loop) The addition of the size is to make sure you go below zero when you get close to borders.
Then you never have to worry about messy border conditions, everything simply overlaps. No need to check each and every border. I hope this makes sense. Please ask for clarification if not. I've used this more for random walker programs but the idea is the same.
I'm writing code for a minesweeper project for class and one method is numAdjMines, which counts the mines around a cell in the array, each type of cell has a different value, like mines are -2, while mines with a flag on them are -4. I want to just write one if statement, but I end up having to just write the same code twice, with different values at the end.
if (row >= 1 && col >= 1 && boardArray[row - 1][col - 1] == MINE)
{
adjMines = adjMines + 1;
}
if (row >= 1 && col >= 1 &&
boardArray[row - 1][col - 1] == FLAGGED_MINE)
{
adjMines = adjMines + 1;
}
I tried using || for or and writing || boardArray[row-1][col-1] == FLAGGED_MINE at the end of the first one, but that then ignored the beginning with checking the row and column. Is there a short compact way for me to write this code?
Your above code can actually be compressed into a single IF statement, however I presume your actual code contains more statements otherwise you would have done this already.
The easiest way to simplify such code would be to break it into two layers of IF statements. The outer one contains the common condition, and the inner ones contain the specific conditions.
if (row >= 1 && col >= 1 ){
int cell = boardArray[row - 1][col - 1];
if( cell == MINE ){
// Code here
}
else if( cell == FLAGGED_MINE )
{
// Code here
}
}
To avoid repetition you can use nested if statements, ie both conditions rely on row & col being >= 1, so pull that out into it's own statement.
Then i'm guessing you want to avoid pulling the value out of the array multiple times, so the best thing to do is assign it to a variable. this probably isn't more efficient at runtime, however is nicer to look at.
if (row >= 1 && col >= 1)
{
int value = boardArray[row - 1][col - 1];
if (value == MINE || value == FLAGGED_MINE)
{
adjMines = adjMines + 1;
}
}
You can use parentheses to group the conditions
if(row >= 1 && col >= 1 &&
(boardArray[row - 1][col - 1] == MINE
|| boardArray[row - 1][col - 1] == FLAGGED_MINE))
{
adjMines = adjMines + 1;
}
I'm not sure which part you're trying to avoid repeating, but you can avoid the repetion on the first part by nesting the if statements.
if( row >= 1 && col >= 1 ) {
if( boardArray[row - 1][col - 1] == MINE ||
boardArray[row - 1][col - 1] == FLAGGED_MINE ) {
adjMines = adjMines + 1;
}
}
I'd take it even one step further and make the inner if statement a method call.
I am not sure if I entirely understand the question, but the following code is more compact and accomplish the same goal:
if (row >= 1 && col >= 1 &&
(boardArray[row - 1][col - 1] == MINE || boardArray[row - 1][col - 1] == FLAGGED_MINE)) {
adjMines++;
}
I am not sure if writing to your main array is a good idea for the flagged mines. I suppose a player can flag a free cell by mistake and that would mark the cell as a flagged mine and you won't know if a mine was actually there. Maybe you have logic for handling this, but it's always good to have your original board (the data structure) safe for future reference.
for( int r : array( row-1, row, row+1 ))
for(int c : array( col-1, col, col+1 ))
{
if(r==row && c==col) // center
continue;
if(0<=r&&r<ROWS && 0<=c&&c<COLS) // within bounds
{
int state = boardArray[r][c];
if(state==MINE||state==FLAGGED_MINE)
adjMines++;
}
}
int[] array(int... ints){ return ints; }
So you don't have to write 8 seperate cases. Not a very big deal here, but if you have a 3D mine...