Nikolaj Egholk Jakobsen Nikolaj Egholk Jakobsen - 22 days ago 15
Java Question

Java Connect Four in console - Horizontal and Vertical winning conditions

I'm working on a Connect Four game for the console in Java. I have problems with the winning conditions, as I don't know how to program them. Here is my code my Main:

public class Main {

public static char[] playerNumber = new char[]{'1', '2'};
public static char[] Badge = new char[]{'X', 'O'};

public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int moves = 7 * 6;
int whichPlayer = 0;

for (int i = 0; i < 10; i++) {
System.out.println(" FOUR IN A ROW");
System.out.println("-------------------------------------------------------");
System.out.println("Welcome to the amazing game Four In A Row:");
System.out.println("Enter a number between 0 and 6 for choosing a column.");
System.out.println();

Board board = new Board();
board.fillBoard();
board.presentBoard();

do {
// 1. get a badge
char Player = playerNumber[whichPlayer];
char badge = Badge[whichPlayer];

// 2. make a turn
board.makeTurn(badge, Player);
board.presentBoard();

// 3. Tjek om der er vinder
if (board.checkWinHorizontal() || board.checkWinVertical()) {
System.out.println("Player " + Player + " has won!");
break;
}

// 4. change the player
whichPlayer = 1 - whichPlayer;

// 5. decrease moves
--moves;

if (moves == 0) {
System.out.println("Game over, nobody has won.");
System.out.println("Do you want to play again? 'Y' or 'N':");
String newGame = scanner.nextLine();
if (newGame.equals("Y") || newGame.equals("y")) {
break;
}
if (newGame.equals("N") || newGame.equals("n")) {
System.out.println("Thanks for the game!");
return;
}
}
// 6. repeat
} while (true);
}
}


And here is my code for my Board class:

public class Board {

char[][] board = new char[6][7];

int column;

// Fills the empty spaces
public void fillBoard() {
for (int i = 0; i < 6; i++) {
for (int j = 0; j < 7; j++) {
board[i][j] = ' ';
}
}
}

// Prints the board
public void presentBoard() {
for (int i = 0; i < 6; i++) {
System.out.print(" | ");
for (int j = 0; j < 7; j++) {
System.out.print(board[i][j] + " | ");
}
System.out.println();
System.out.print(" -----------------------------");
System.out.println();
}
}

// Turn
public void makeTurn(char badge, char Player) {
Scanner scanner = new Scanner(System.in);
do {
// 1. Ask for a column
System.out.println("Player " + Player + " turn: ");
column = scanner.nextInt();

// 2. Check if it's between 0 and 6
if (column > 6) {
System.out.println("That is not a valid number. Please enter a number between 0 and 6: ");
continue;
}

// 3. Place a badge
for (int i = 6 - 1; i >= 0; i--) {
if (board[i][column] == ' ') {
board[i][column] = badge;
return;
}
}

// If column is full
System.out.println("Column " + column + " is full. Try another column:");

} while (true);
}

// Check for vertical win
public boolean checkWinVertical() {
return verticalWin(5, column);
}

// Check for horizontal win
public boolean checkWinHorizontal() {
return horizontalWin(5,column);
}

// Conditions for vertical win
private boolean verticalWin(int x, int y) {
char charToCheck = board[x][y];
if (board[x-1][y] == charToCheck &&
board[x-2][y] == charToCheck &&
board[x-3][y] == charToCheck) {
return true;
}

return false;
}

// Conditions for horizontal win
private boolean horizontalWin(int x, int y) {
char charToCheck = board[x][y];
if (board[x][y+1] == charToCheck &&
board[x][y+2] == charToCheck &&
board[x][y+3] == charToCheck) {
return true;
}
return false;
}


I have succeeded in getting the game recognize a win horizontally and vertically at the bottom row of my array, but I don't know how to make the game recognize for the whole array. I'm only concentrating about the horizontal and vertical, as the diagonal is too complicated for me. And I don't know if this is the right approach or there is a better one.
Thanks!

Answer

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.