javamultidimensional-arraygame-development2d-games

Detect diagonal in matrix in Java (victory in Connect Four game)


I'm making a Connect 4 game. The objective is to align four items the same way.

I have a double array : Player[6][7] which is filled by the player that played on that slot.

I'm working on victory conditions, there is:

And diagonals are not working.

Here is my code that check for victories :

private final Player[][] board = new Player[6][7]; // the board filled

public Player hasWinner() {
    // Lines (rows)
    for(int y = 0; y < board.length; y++) { // simple lines
        Player last = null;
        int nb = 0;
        Player[] line = board[y];
        for(int x = 0; x < line.length; x++) {
            Player played = line[x];
            if((last == null || played == last) && played != null) { // new player or same as before
                nb++;
                if(nb == 4) // it's this !
                    return played;
            } else { // else reset
                nb = (played == null ? 0 : 1);
            }
            last = played;
        }
    }

    // Columns
    for(int x = 0; x < board[0].length; x++) { // simple columns
        Player last = null;
        int nb = 0;
        for(int y = 0; y < board.length; y++) { // for each columns
            Player played = board[y][x];
            if((last == null || played == last) && played != null) { // new player or same as before
                nb++;
                if(nb == 4) // it's this !
                    return played;
            } else { // else reset
                nb = (played == null ? 0 : 1);
            }
            last = played;
        }
    }
   
    // ➡️ HERE IS THE INTERESTING PART
    // Diagonals
    for(int i = -board.length; i < board[0].length; i++) { // diagonals
        Player last = null;
        int nb = 0;
        for(int j = 0; j < 9; j++) {
            if(board.length <= j || board[j].length <= j)
                continue;
            Player played = board[j][j];
            if((last == null || played == last) && played != null) { // new player or same as before
                nb++;
                if(nb == 4) // it's this !
                    return played;
            } else { // else reset
                nb = (played == null ? 0 : 1);
            }
            last = played;
        }
        for(int j = 9; j < 0; j--) {
            if(board.length <= j || board[j].length <= j)
                continue;
            Player played = board[j][board[j].length - j];
            if((last == null || played == last) && played != null) { // new player or same as before
                nb++;
                if(nb == 4) // it's this !
                    return played;
            } else { // else reset
                nb = (played == null ? 0 : 1);
            }
            last = played;
        }
    }
    return null;
}

How can I make the victory with diagonals works?


Solution

  • Avoid magic numbers like 9. Use the length of the matrix rows and columns as your upper-bounds. Specify a constant for the size of a match i.e. 4.

    For diagonals, compare the current player to the [row + dy, col + dx] direction.

    public class Connect4 {
        private static final int ROWS = 6;
        private static final int COLS = 7;
        private static final int MATCH_SIZE = 4; // Number of pieces needed to win
    
        private final Player[][] board = new Player[ROWS][COLS]; // the board
    
        public Player hasWinner() {
            // Check horizontal lines
            for (int y = 0; y < board.length; y++) {
                Player last = null;
                int count = 0;
                for (int x = 0; x < board[y].length; x++) {
                    Player current = board[y][x];
                    if (current != null && current == last) {
                        count++;
                        if (count == MATCH_SIZE) {
                            return current;
                        }
                    } else {
                        count = 1;
                        last = current;
                    }
                }
            }
    
            // Check vertical lines
            for (int x = 0; x < board[0].length; x++) {
                Player last = null;
                int count = 0;
                for (int y = 0; y < board.length; y++) {
                    Player current = board[y][x];
                    if (current != null && current == last) {
                        count++;
                        if (count == MATCH_SIZE) {
                            return current;
                        }
                    } else {
                        count = 1;
                        last = current;
                    }
                }
            }
    
            // Check diagonal lines (top-left to bottom-right)
            for (int y = 0; y <= board.length - MATCH_SIZE; y++) {
                for (int x = 0; x <= board[y].length - MATCH_SIZE; x++) {
                    Player current = board[y][x];
                    if (current != null &&
                        current == board[y + 1][x + 1] &&
                        current == board[y + 2][x + 2] &&
                        current == board[y + 3][x + 3]) {
                        return current;
                    }
                }
            }
    
            // Check diagonal lines (bottom-left to top-right)
            for (int y = MATCH_SIZE - 1; y < board.length; y++) {
                for (int x = 0; x <= board[y].length - MATCH_SIZE; x++) {
                    Player current = board[y][x];
                    if (current != null &&
                        current == board[y - 1][x + 1] &&
                        current == board[y - 2][x + 2] &&
                        current == board[y - 3][x + 3]) {
                        return current;
                    }
                }
            }
    
            return null; // No winner found
        }
    }
    

    Refactored logic

    There is a lot of repetition, we can use directions to scan rows, cols, etc...

    public class Connect4 {
        private static final int ROWS = 6;
        private static final int COLS = 7;
        private static final int MATCH_SIZE = 4; // Number of pieces needed to win
    
        private final Player[][] board = new Player[ROWS][COLS]; // the board
    
        public Player hasWinner() {
            for (int y = 0; y < ROWS; y++) {
                for (int x = 0; x < COLS; x++) {
                    Player current = board[y][x];
                    if (current == null) {
                        continue;
                    }
    
                    // Check right (horizontal)
                    if (x <= COLS - MATCH_SIZE && checkDirection(y, x, 0, 1)) {
                        return current;
                    }
    
                    // Check down (vertical)
                    if (y <= ROWS - MATCH_SIZE && checkDirection(y, x, 1, 0)) {
                        return current;
                    }
    
                    // Check down-right (diagonal)
                    if (x <= COLS - MATCH_SIZE && y <= ROWS - MATCH_SIZE && checkDirection(y, x, 1, 1)) {
                        return current;
                    }
    
                    // Check down-left (anti-diagonal)
                    if (x >= MATCH_SIZE - 1 && y <= ROWS - MATCH_SIZE && checkDirection(y, x, 1, -1)) {
                        return current;
                    }
                }
            }
    
            return null; // No winner found
        }
    
        private boolean checkDirection(int y, int x, int dy, int dx) {
            Player player = board[y][x];
            for (int i = 1; i < MATCH_SIZE; i++) {
                if (board[y + i * dy][x + i * dx] != player) {
                    return false;
                }
            }
            return true;
        }
    }