HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavaMinor

Detecting Tic-Tac-Toe win: Enumerate all possibilities or use nested loops?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
winpossibilitiesalltoeticloopstacnestedusedetecting

Problem

Currently I have a game that does this and then checks for victory conditions in victoryTable

victoryTable[0] = rows[0][0] + rows[0][1] + rows[0][2];
victoryTable[1] = rows[1][0] + rows[1][1] + rows[1][2];
victoryTable[2] = rows[2][0] + rows[2][1] + rows[2][2];
victoryTable[3] = rows[0][0] + rows[1][0] + rows[2][0];
victoryTable[4] = rows[0][1] + rows[1][1] + rows[2][1];
victoryTable[5] = rows[0][2] + rows[1][2] + rows[2][2];
victoryTable[6] = rows[0][0] + rows[1][1] + rows[2][2];
victoryTable[7] = rows[0][2] + rows[1][1] + rows[2][0];


But I am considering changing it to read this way:

for (int i = 0; i < 8; i++ ) {
    for (int n = 0; n < 3; n++) {
        for (int t = 0; t < 3; t++) {
            victoryTable[i] = rows[n][t] + // based on some combination of n,t and 
                                       // if statement to filter out garbage/duplicates, 
                                       // the more I think about this implementation the 
                                       // more complicated it seems.
        }
    }
}


Would this be a step in the wrong direction? It seems like I should put the things that change in a method or loop or something, but it also looks like this will make it harder to read what the code does, and have more potential for bugs (undesired conditions, etc) but also more expandability (if game board became larger in future version, for instance.

The larger context is this: This is part of a method that determines the victory state of a Tic Tac Toe game. In a 3x3 int[][], player X is represented by a 3, player O by a 30. The code in this post adds each column, row, and diagonal, and stores them in an array. Another method loops through that array and if any element equals 90, O wins, if it equals 9, X wins, otherwise nobody wins (yet).

Solution

Without an advanced math degree, I think this is a perfectly reasonable solution. Here's a sample implementation. It's not that complicated. Basically, for every WIN_CONDITION that exists (each of which is a set of coordinates on the board), we check that sequence and see if the Marks are all the same. If so, that player has won.

I don't think that's a step in the wrong direction. It's perfectly logical and functional.

Now if it was a more vast game like Connect Four... looping through each variation might be a better option that explicitly listing each win condition, obviously. But since Tic-Tac-Toe has such a small set, this is fine and makes the code pretty readable.

private static Mark[][] board = new Mark[3][3];

private static final int[][][] WIN_CONDITIONS = {
    //Rows
    {{ 0, 0 }, { 0, 1 }, { 0, 2 }},
    {{ 1, 0 }, { 1, 1 }, { 1, 2 }},
    {{ 2, 0 }, { 2, 1 }, { 2, 2 }},

    //Columns
    {{ 0, 0 }, { 1, 0 }, { 2, 0 }},
    {{ 0, 1 }, { 1, 1 }, { 2, 1 }},
    {{ 0, 2 }, { 1, 2 }, { 2, 2 }},

    //Diagonals
    {{ 0, 0 }, { 1, 1 }, { 2, 2 }},
    {{ 2, 0 }, { 1, 1 }, { 0, 2 }}
};

// other stuff, like an enum for Mark (Mark.X and Mark.O) and the GameState

private GameState checkWinConditions() {

    GameState won = null;

    for(int[][] condition : WIN_CONDITIONS) {
        Mark comparator = board[condition[0][0]][condition[0][1]];
        if(comparator == null) continue;

        won = comparator.getWinState();
        for(int[] coordinate : condition) {
            if(board[coordinate[0]][coordinate[1]] != comparator) {
                won = null;
                break;
            }
        }
        if(won != null) break;
    }
    return won;
}

Code Snippets

private static Mark[][] board = new Mark[3][3];

private static final int[][][] WIN_CONDITIONS = {
    //Rows
    {{ 0, 0 }, { 0, 1 }, { 0, 2 }},
    {{ 1, 0 }, { 1, 1 }, { 1, 2 }},
    {{ 2, 0 }, { 2, 1 }, { 2, 2 }},

    //Columns
    {{ 0, 0 }, { 1, 0 }, { 2, 0 }},
    {{ 0, 1 }, { 1, 1 }, { 2, 1 }},
    {{ 0, 2 }, { 1, 2 }, { 2, 2 }},

    //Diagonals
    {{ 0, 0 }, { 1, 1 }, { 2, 2 }},
    {{ 2, 0 }, { 1, 1 }, { 0, 2 }}
};

// other stuff, like an enum for Mark (Mark.X and Mark.O) and the GameState

private GameState checkWinConditions() {

    GameState won = null;

    for(int[][] condition : WIN_CONDITIONS) {
        Mark comparator = board[condition[0][0]][condition[0][1]];
        if(comparator == null) continue;

        won = comparator.getWinState();
        for(int[] coordinate : condition) {
            if(board[coordinate[0]][coordinate[1]] != comparator) {
                won = null;
                break;
            }
        }
        if(won != null) break;
    }
    return won;
}

Context

StackExchange Code Review Q#31292, answer score: 3

Revisions (0)

No revisions yet.