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.
Related
I have been trying to make a Lines of Action board with recursive. I have already done the multidimensional array (8x8). Now, the only problem I have is how to put the pieces in their correct place (especially the black ones). What's my problem? when I'm trying to print the first row of the pieces it prints something like this: "-b-b-b-b-b--", instead of "-bbbbbb-". This is also true for the last row.
public static void im (int[][]m, int r, int c){//Matriz
if (r==m.length-1 && c==m[0].length-1) {
System.out.print("-");
} else {
if(r==0 && c>0 && c<m[0].length-1){
System.out.print("b");
}
if(c==0 && r>0 && r<m.length-1){
System.out.print("w");
}
if (c==m[0].length-1) {
if(r>0){
System.out.print("w");
System.out.println("");
im(m,r+1,0);
}else{
System.out.print("-");
System.out.println("");
im(m,r+1,0);
}
} else {
System.out.print("-");
im(m,r,c+1);
}
}
}
The logic in your if-statements is flawed, you could try again and comment in every block what you know upto now. Like this:
if (a) {
// I know that a == true
if (b) {
// I know that a == true && b == true
} else {
// I know that a == true && b == false
}
}
This way you can easily spot the mistakes yourself, if this doesn't help just comment and I'll reply the correct code.
EDIT: Here is the correct code:
if (r == 0 || r == m[0].length - 1) {
// top or bottom row
if (c == 0 || c == m.length - 1) {
// top or bottom row and leftmost column or rightmost column
System.out.print("-");
} else {
// top or bottom row and not leftmost column or rightmost column
System.out.print("b");
}
} else {
// not top or bottom row
if (c == 0 || c == m.length - 1) {
// not top or bottom row and leftmost column or rightmost column
System.out.print("w");
} else {
// not top or bottom row and not leftmost column or rightmost column
System.out.print("-");
}
}
I don't really know how to fix your original code since it doesn't really make sense to me, so I just completely rewrote it, a good thing to remember is that you want every function call to output only 1 character so at least try to gaurantee that it can only reach one of the print statements at a time.
EDIT2:
public static void im(int[][] m, int r, int c) {
if (r == 0 || r == m.length - 1) {
// top or bottom row
if (c == 0 || c == m[0].length - 1) {
// top or bottom row and leftmost column or rightmost column
System.out.print("-");
} else {
// top or bottom row and not leftmost column or rightmost column
System.out.print("b");
}
} else {
// not top or bottom row
if (c == 0 || c == m[0].length - 1) {
// not top or bottom row and leftmost column or rightmost column
System.out.print("w");
} else {
// not top or bottom row and not leftmost column or rightmost column
System.out.print("-");
}
}
if (c == m[0].length - 1) {
// rightmost column
System.out.println();
if (r != m.length - 1) {
// not bottom row
im(m, r + 1, 0);
}
} else {
// not rightmost column
im(m, r, c + 1);
}
}
if(pl.y+pl.height >= a.y && pl.x+pl.width >= a.x+1 && pl.x <= a.x+a.width-1 && pl.y<=a.y) { //TOP
colUP=true;
}
else colUP=false;
if(pl.y <= a.y+a.height && pl.x+pl.width >= a.x+1 && pl.x <= a.x+a.width-1 && pl.y+pl.height>=a.y+a.height) { //BOTTOM
colDOWN=true;
}
else colDOWN=false;
if(pl.x <= a.x+a.width && pl.x+pl.width>a.x+a.width && pl.y+pl.height >= a.y && pl.y <= a.y+a.height){ //RIGHT
colRIGHT=true;
}
else colRIGHT=false;
if(pl.x+pl.width >= a.x && pl.x<a.x && pl.y+pl.height >= a.y && pl.y <= a.y+a.height){ //LEFT
colLEFT=true;
}
else colLEFT=false;
I setup a debug that will tell me which of the 4 Booleans is being set to true, and they don't show that when I put the box 'pl' on top of box 'a' colUP is not equal to true, and they will only come true in weird instances where box 'pl' is colliding with several box 'a's , and the collision for a certain side might be true when it isn't but if colUP is true then colRIGHT is true for some reason. (This code is inside a for loop that goes through an array list of Rectangles and sets the current Rectangle equal to the variable 'a' so that a.x is the box's x position)
You have right logic but you set false for each condition separately. In reality all conditions should be true. So, use one boolean variable - isInRectangle=true; then check all conditions - left,right,top,bottom. If any is not true then isInRectangle=false;
It is simple AND logical operation for all 4 conditions.
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.
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...