I have a tic-tac-toe game. I need to check if there are 3 identical characters in a row. The problem for me was that the choice of field size is possible in the game. Yes, and even what happened for the 3x3 field seems to me somehow not beautiful.

private static boolean checkWinner(String[][] desc) { return checkWinnerHorizontal(desc) || checkWinnerVertical(desc) || checkWinnerDiagonals(desc); } private static boolean checkWinnerHorizontal(String[][] desc) { return desc[0][0].equals(desc[1][0]) && desc[0][0].equals(desc[2][0]) || desc[0][1].equals(desc[1][1]) && desc[0][1].equals(desc[2][1]) || desc[0][2].equals(desc[1][2]) && desc[0][2].equals(desc[2][2]); } private static boolean checkWinnerVertical(String[][] desc) { return desc[0][0].equals(desc[0][1]) && desc[0][0].equals(desc[0][2]) || desc[1][0].equals(desc[1][1]) && desc[1][0].equals(desc[1][2]) || desc[2][0].equals(desc[2][1]) && desc[2][0].equals(desc[2][2]); } private static boolean checkWinnerDiagonals(String[][] desc) { return desc[0][0].equals(desc[1][1]) && desc[0][0].equals(desc[2][2]) || desc[2][0].equals(desc[1][1]) && desc[2][0].equals(desc[0][2]); } 

Somehow this is all clumsy, so please help shortly and beautifully, not with the code as an idea as it can be done, given that we do not know the size of the field, it’s just known that this is a square. Somehow cycles need ...

  • 3
    Well, yes, there are cycles :) - Sublihim
  • 2
    and you can ask why you use an array of String, and not char? - Real KEK
  • >>> but you can ask why you are using the String array, and not the char? <<< because it slowed down))) - Pavel
  • one
    @Pavel, how can you really help true or false in general (especially considering that a row of empty cells also gives true )? Is it not necessary to know exactly who won, and that not empty cells “won”? - Regent
  • 2
    ))) yes, empty cells should not win ... - Pavel

3 answers 3

Initial design questions

  1. Why keep an array of strings if there is enough byte array? And when drawing, draw the character you need
  2. Why transfer an array from a method to a method if it can be made a field of a class?

Total

 class Desc { private int size; private byte[][] desc; public Desc(int ASize) { size = ASize; desc = new byte[size][size]; } private boolean checkWinner() { return checkWinnerHorizontal() || checkWinnerVertical() || checkWinnerDiagonals(); } private boolean checkWinnerHorizontal() { for (int i = 0; i < size; i++) { boolean res = true; for (int j = 1; j < size && res; j++) res = desc[i][j] == desc[i][0]; if (res) return true; } return false; } private boolean checkWinnerVertical() { for (int i = 0; i < size; i++) { boolean res = true; for (int j = 1; j < size && res; j++) res = desc[j][i] == desc[0][i]; if (res) return true; } return false; } private boolean checkWinnerDiagonals() { boolean res = true; for (int i = 1; j < size && res; i++) res = desc[i][i] == desc[0][0]; if (res) return true; res = true; for (int i = 1; j < size && res; i++) res = desc[size - i - 1][i] == desc[size - 1][0]; return res; } 
  • Thank! You have the easiest and most intuitive option. Made it based on it. - Pavel
  • @Pavel, in that case, "say hello" to the problems, "empty cells won" and "someone won, but it is not known who" who are pulling back from your question here and, apparently, now back to you. - Regent
  • Nah, everything's fine. I did not copy this code and redid the string to fill it with blank lines. I didn't need the code. I needed an idea. But something simple. - Pavel

Cycle for checking one line (in the check method) and cycles for checking groups of lines (in the checkAll method):

 private static class GotWinnerException extends Exception { private final char winnerType; public GotWinnerException(char winnerType) { this.winnerType = winnerType; } } private static final int FIELD_SIZE = 4, WIN_COUNT = 3; private static final char EMPTY_CELL = ' '; public static void main(String[] args) { char[][] desc = new char[FIELD_SIZE][FIELD_SIZE]; desc[0] = new char[] { '0', 'x', '0', EMPTY_CELL }; desc[1] = new char[] { EMPTY_CELL, 'x', '0', EMPTY_CELL }; desc[2] = new char[] { EMPTY_CELL, '0', 'x', EMPTY_CELL }; desc[3] = new char[] { EMPTY_CELL, '0', EMPTY_CELL, 'x' }; char result = checkAll(desc); if (result != EMPTY_CELL) { System.out.println("Winner: " + checkAll(desc)); } } public static char checkAll(char[][] desc) { char winner = EMPTY_CELL; try { for (int i = 0; i < FIELD_SIZE; i++) { check(desc, 1, 0, 0, i); //columns } for (int i = 0; i < FIELD_SIZE; i++) { check(desc, 0, i, 1, 0); //rows } check(desc, 1, 0, 1, 0); //first diagonal check(desc, -1, FIELD_SIZE - 1, 1, 0); //second diagonal } catch (GotWinnerException e) { winner = e.winnerType; } return winner; } public static void check(char[][] desc, int xCoef, int xShift, int yCoef, int yShift) throws GotWinnerException { char savedSymbol = EMPTY_CELL; int savedSymbolCount = 0; for (int i = 0; i < FIELD_SIZE; i++) { int x = i * xCoef + xShift; int y = i * yCoef + yShift; char symbol = desc[x][y]; if (symbol == savedSymbol) { savedSymbolCount++; } else { savedSymbol = symbol; savedSymbolCount = 1; } if (symbol != EMPTY_CELL && savedSymbolCount == WIN_COUNT) { throw new GotWinnerException(symbol); } } } 

FIELD_SIZE sets the size of the side of the field, WIN_COUNT is the number of consecutive cells required to win on one line.

For the test, a 4x4 field and 3 symbols are used to win. In this case, x wins diagonally.

    I would write like this:

     class Position { public int x, y; public Position(int x, int y) { this.x = x; this.y = y; } } Position[][] winningTriples = { { new Position(0, 0), new Position(1, 0), new Position(2, 0) }, { new Position(0, 1), new Position(1, 1), new Position(2, 1) }, { new Position(0, 2), new Position(1, 2), new Position(2, 2) }, { new Position(0, 0), new Position(0, 1), new Position(0, 2) }, { new Position(1, 0), new Position(1, 1), new Position(1, 2) }, { new Position(2, 0), new Position(2, 1), new Position(2, 2) }, { new Position(0, 0), new Position(1, 1), new Position(2, 2) }, { new Position(0, 2), new Position(1, 1), new Position(2, 0) } }; boolean checkWinner(String[][] desc) { return Arrays.stream(winningTriples).anyMatch(triple -> Arrays.stream(triple).allMatch( item -> desc[triple[0].x][triple[0].y].equals(desc[item.x][item.y]))); } 

    (I hope I did not mess up with the Stream API.)

    • one
      In theory, Position[][] winningTriples must be created dynamically under a field of any size. Concerning Stream: you have a triple - this is String[] (one line). Accordingly, triple[0] , as well as item , is a String with no x and y fields. - Regent
    • one
      @Regent: Strange, isn't the triple Position[] ? - VladD
    • @Regent: Oh yeah, ugh, typo. Corrected the answer. Thank! - VladD
    • one
      Wow! and what is this new Position (0, 0) a special class in java for all actions on the plane? - Pavel
    • @Pavel this class is declared a little higher in the same code. - Regent