Home > Mobile >  Tic Tac Toe Win condition check with variable grid size
Tic Tac Toe Win condition check with variable grid size

Time:05-29

I have a 2d array of variable length for a tic-tac-toe game. Each entry in the array corresponds to a square in the grid and can have a value of 0 (empty) 1(X) or 2(O).

I'm writing a method to check if EITHER player has one and return true if so but can't figure out how to do so. The current implementation compares everything to an "Anchor point" that is slightly different for rows, columns, and diagonals.

here is my current method (only checks rows):

public static boolean hasWon(int[][] gridStatus){
    for (int row = 0; row < size; row  ) {
        for (int col = 0; col < size; col  ) {
            if (gridStatus[row][col]!=gridStatus[row][0]){

            }
        }
    }

any help would be greatly appreciated.

CodePudding user response:

I recently made a 3x3 Tic Tac Toe but my hasWon() method is generic and should work in your case.

Codesandbox Link for my Demo : Tic Tac Toe Demo

// board is NxN 2D array where board[i][j] can be "O" or "X"
// ch is the player we want to check for winner, can be "O" or "X"
const isWinner = (board, ch) => {
  // Check rows and columns
  for (let i = 0; i < board.length; i  ) {
    let row_count = 0;
    let col_count = 0;
    for (let j = 0; j < board.length; j  ) {
      if (board[i][j] === ch) row_count  ;
      if (board[j][i] === ch) col_count  ;
    }
    if (row_count === board.length || col_count === board.length) return true;
    row_count = 0;
    col_count = 0;
  }

  // Check diagonals
  let count = 0;
  let anti_count = 0;
  for (let i = 0; i < board.length; i  ) {
    if (board[i][i] === ch) count  ;
    if (board[i][board.length - i - 1] === ch) anti_count  ;
    if (count === board.length || anti_count === board.length) return true;
  }

  return false;
};

CodePudding user response:

I think you could check if a player has won or not by controlling each possible way of winning and the OR-ing their result. Also, I would pass to your checking method the player's value to establish if the player has won or not.

By using streams, you can drastically reduce the code length and make it much more readable.

public static boolean isTopLeftDiagonalWin(int[][] grid, int player) {
    //NOTE: Assuming this is an N x N matrix
    //Checks if every element on the diagonal starting from top left is equal to the given player's value
    return IntStream.range(0, grid.length).allMatch(i -> grid[i][i] == player);
}

public static boolean isTopRightDiagonalWin(int[][] grid, int player) {
    //NOTE: Assuming this is an N x N matrix
    //Checks if every element on the diagonal starting from top right is equal to the given player's value
    return IntStream.range(0, grid.length).allMatch(i -> grid[i][(grid.length - 1) - i] == player);
}

public static boolean isHorizontalWin(int[][] grid, int player) {
    //NOTE: Assuming this is an N x N matrix
    //Checks if any row has all columns with the same player's value
    return IntStream.range(0, grid.length).anyMatch(
            i -> IntStream.range(0, grid[i].length).allMatch(j -> grid[i][j] == player)
    );
}

public static boolean isVerticalWin(int[][] grid, int player) {
    //NOTE: Assuming this is an N x N matrix
    //Checks if any column has all rows with the same player's value
    return IntStream.range(0, grid.length).anyMatch(
            i -> IntStream.range(0, grid.length).allMatch(j -> grid[j][i] == player)
    );
}

public static boolean hasWon(int[][] grid, int player) {
    return isTopLeftDiagonalWin(grid, player) || isTopRightDiagonalWin(grid, player) || isHorizontalWin(grid, player) || isVerticalWin(grid, player);
}

Here is a sample project to test the code above:

https://www.jdoodle.com/iembed/v0/rv3

CodePudding user response:

I am assuming that in your grid, 1 means X, 2 means O and 0 means nobody.

I think of tic tac toe in terms of lines, so let's put that idea into the program. Here is a line that let's you get the player at any position:

interface Line {
    int get(int n);
}

Now we can write methods that return Line implementations for rows, columns and diagonals.

Line row(int[][] grid, int rowNumber) {
    return columnNumber -> grid[rowNumber][columnNumber];
}

Line column(int[][] grid, int columnNumber) {
    return rowNumber -> grid[rowNumber][columnNumber];
}

Line rightDiagonal(int[][] grid) {
    return i -> grid[i][i];
}

Line leftDiagonal(int[][] grid, int length) {
    return i -> grid[i][length - 1 - i];
}

and a method to get all the lines in the grid:

private List<Line> makeLines(int[][] grid) {
    List<Line> result = new ArrayList<>();
    for (int i = 0; i < grid.length; i  ) {
        result.add(row(grid, i));
        result.add(column(grid, i));
    }
    result.add(rightDiagonal(grid));
    result.add(leftDiagonal(grid, grid.length));
    return result;
}

And a method to see who won in a line:

int getWinnerFromLine(Line line, int length) {
    int first = line.get(0);
    if (first == 0) return 0;
    for (int i = 0; i < length; i  ) {
        if (line.get(i) != first) return 0;
    }
    return first;
}

Then finally it is easy to find out who won in the case of a draw, or zero if nobody won.

int getWinningPlayer(int[][] grid) {
    List<Line> lines = makeLines(grid); // This could be created once at the start of the game.
    
    for (Line line : lines) {
        int winner = getWinnerFromLine(line, grid.length);
        if (winner != 0) return winner;
    }
    return 0;
}
  • Related