Algoritmo per la determinazione del gioco Tic Tac Toe Over

Ho scritto un gioco di tic-tac-toe in Java, e il mio metodo attuale per determinare la fine del gioco rappresenta i seguenti possibili scenari per il gioco finito:

  1. Il tabellone è pieno e nessun vincitore è stato ancora dichiarato: il gioco è un pareggio.
  2. Cross ha vinto.
  3. Circle ha vinto.

Sfortunatamente, per farlo, legge un insieme predefinito di questi scenari da una tabella. Questo non è necessariamente negativo considerando che ci sono solo 9 spazi su una tavola, e quindi la tabella è piuttosto piccola, ma c’è un modo algoritmico migliore per determinare se il gioco è finito? Determinare se qualcuno ha vinto o no è la carne del problema, dal momento che controllare se 9 spazi sono pieni è banale.

Il metodo di tabella potrebbe essere la soluzione, ma in caso contrario, che cos’è? Inoltre, cosa succede se la tavola non fosse la dimensione n=9 ? Cosa accadrebbe se fosse una scheda molto più grande, ad esempio n=16 , n=25 e così via, facendo sì che il numero di oggetti posti consecutivamente vincesse fosse x=4 , x=5 , ecc.? Un algoritmo generale da utilizzare per tutti n = { 9, 16, 25, 36 ... } ?

    Sai che una mossa vincente può avvenire solo dopo che X o O ha effettuato la loro mossa più recente, quindi puoi cercare solo una riga / colonna con diag opzionale che sono contenuti in quella mossa per limitare lo spazio di ricerca quando cerchi di determinare una tavola vincente. Inoltre dal momento che ci sono un numero fisso di mosse in un gioco di tiro a segno tic-tac-toe una volta che l’ultima mossa è stata fatta se non era una mossa vincente è di default un gioco di estrazione.

    modifica: questo codice è per una scheda n on con n in una riga per vincere (3×3 richieste di banco 3 di seguito, ecc.)

    edit: aggiunto codice per controllare anti diag, non sono riuscito a capire un modo non loop per determinare se il punto era sull’anti diag, ecco perché manca quel passaggio

     public class TripleT { enum State{Blank, X, O}; int n = 3; State[][] board = new State[n][n]; int moveCount; void Move(int x, int y, State s){ if(board[x][y] == State.Blank){ board[x][y] = s; } moveCount++; //check end conditions //check col for(int i = 0; i < n; i++){ if(board[x][i] != s) break; if(i == n-1){ //report win for s } } //check row for(int i = 0; i < n; i++){ if(board[i][y] != s) break; if(i == n-1){ //report win for s } } //check diag if(x == y){ //we're on a diagonal for(int i = 0; i < n; i++){ if(board[i][i] != s) break; if(i == n-1){ //report win for s } } } //check anti diag (thanks rampion) if(x + y == n - 1){ for(int i = 0; i < n; i++){ if(board[i][(n-1)-i] != s) break; if(i == n-1){ //report win for s } } } //check draw if(moveCount == (Math.pow(n, 2) - 1)){ //report draw } } } 

    puoi usare un quadrato magico http://mathworld.wolfram.com/MagicSquare.html se una qualsiasi riga, colonna o diag aggiunge fino a 15, allora un giocatore ha vinto.

    Questo è simile alla risposta di Osama ALASSIRY , ma scambia lo spazio costante e il tempo lineare per lo spazio lineare e il tempo costante. Cioè, non c’è il ciclo dopo l’inizializzazione.

    Inizializza una coppia (0,0) per ogni riga, ogni colonna e le due diagonali (diagonale e anti-diagonale). Queste coppie rappresentano la (sum,sum) dei pezzi nella riga, colonna o diagonale corrispondente, dove

     Un pezzo del giocatore A ha valore (1,0)
     Un pezzo del giocatore B ha un valore (0,1)
    

    Quando un giocatore piazza un pezzo, aggiorna la corrispondente coppia di righe, coppie di colonne e coppie diagonali (se sulle diagonali). Se una coppia di righe, colonne o diagonali appena aggiornate è uguale a (n,0) o (0,n) allora A o B hanno vinto, rispettivamente.

    Analisi asintotica:

     O (1) tempo (per mossa)
     O (n) spazio (totale)
    

    Per l’uso della memoria, si usano numeri interi 4*(n+1) .

     two_elements * n_rows + two_elements * n_columns +
     two_elements * two_diagonals = 4 * n + 4 interi = 4 (n + 1) numeri interi
    

    Esercizio: puoi vedere come testare un pareggio in tempo O (1) per-mossa? Se è così, puoi terminare la partita in anticipo su un pareggio.

    Che ne dici di questo pseudocodice:

    Dopo che un giocatore mette giù un pezzo in posizione (x, y):

     col=row=diag=rdiag=0 winner=false for i=1 to n if cell[x,i]=player then col++ if cell[i,y]=player then row++ if cell[i,i]=player then diag++ if cell[i,n-i+1]=player then rdiag++ if row=n or col=n or diag=n or rdiag=n then winner=true 

    Userei un array di caratteri [n, n], con O, X e lo spazio vuoto.

    1. semplice.
    2. Un ciclo.
    3. Cinque semplici variabili: 4 numeri interi e un valore booleano.
    4. Scala a qualsiasi dimensione di n.
    5. Controlla solo il pezzo corrente.
    6. Nessuna magia 🙂

    Ecco la mia soluzione che ho scritto per un progetto su cui sto lavorando in javascript. Se non ti interessa il costo della memoria di alcuni array, è probabilmente la soluzione più rapida e semplice che troverai. Presume che tu conosca la posizione dell’ultima mossa.

     /* * Determines if the last move resulted in a win for either player * board: is an array representing the board * lastMove: is the boardIndex of the last (most recent) move * these are the boardIndexes: * * 0 | 1 | 2 * ---+---+--- * 3 | 4 | 5 * ---+---+--- * 6 | 7 | 8 * * returns true if there was a win */ var winLines = [ [[1, 2], [4, 8], [3, 6]], [[0, 2], [4, 7]], [[0, 1], [4, 6], [5, 8]], [[4, 5], [0, 6]], [[3, 5], [0, 8], [2, 6], [1, 7]], [[3, 4], [2, 8]], [[7, 8], [2, 4], [0, 3]], [[6, 8], [1, 4]], [[6, 7], [0, 4], [2, 5]] ]; function isWinningMove(board, lastMove) { var player = board[lastMove]; for (var i = 0; i < winLines[lastMove].length; i++) { var line = winLines[lastMove][i]; if(player === board[line[0]] && player === board[line[1]]) { return true; } } return false; } 

    Ho appena scritto questo per la mia class di programmazione C.

    Lo sto pubblicando perché nessuno degli altri esempi qui funzionerà con una griglia rettangular di qualsiasi dimensione, e qualsiasi numero di marchi consecutivi N -in-a-row per vincere.

    Troverai il mio algoritmo, ad esempio, nella funzione checkWinner() . Non usa numeri magici o niente di fantasia per cercare un vincitore, semplicemente usa quattro cicli per loops – Il codice è ben commentato quindi lascerò che parli da solo.

     // This program will work with any whole number sized rectangular gameBoard. // It checks for N marks in straight lines (rows, columns, and diagonals). // It is prettiest when ROWS and COLS are single digit numbers. // Try altering the constants for ROWS, COLS, and N for great fun! // PPDs come first #include  #define ROWS 9 // The number of rows our gameBoard array will have #define COLS 9 // The number of columns of the same - Single digit numbers will be prettier! #define N 3 // This is the number of contiguous marks a player must have to win #define INITCHAR ' ' // This changes the character displayed (a ' ' here probably looks the best) #define PLAYER1CHAR 'X' // Some marks are more aesthetically pleasing than others #define PLAYER2CHAR 'O' // Change these lines if you care to experiment with them // Function prototypes are next int playGame (char gameBoard[ROWS][COLS]); // This function allows the game to be replayed easily, as desired void initBoard (char gameBoard[ROWS][COLS]); // Fills the ROWSxCOLS character array with the INITCHAR character void printBoard (char gameBoard[ROWS][COLS]); // Prints out the current board, now with pretty formatting and #s! void makeMove (char gameBoard[ROWS][COLS], int player); // Prompts for (and validates!) a move and stores it into the array int checkWinner (char gameBoard[ROWS][COLS], int player); // Checks the current state of the board to see if anyone has won // The starting line int main (void) { // Inits char gameBoard[ROWS][COLS]; // Our gameBoard is declared as a character array, ROWS x COLS in size int winner = 0; char replay; //Code do // This loop plays through the game until the user elects not to { winner = playGame(gameBoard); printf("\nWould you like to play again? Y for yes, anything else exits: "); scanf("%c",&replay); // I have to use both a scanf() and a getchar() in replay = getchar(); // order to clear the input buffer of a newline char // (http://cboard.cprogramming.com/c-programming/121190-problem-do-while-loop-char.html) } while ( replay == 'y' || replay == 'Y' ); // Housekeeping printf("\n"); return winner; } int playGame(char gameBoard[ROWS][COLS]) { int turn = 0, player = 0, winner = 0, i = 0; initBoard(gameBoard); do { turn++; // Every time this loop executes, a unique turn is about to be made player = (turn+1)%2+1; // This mod function alternates the player variable between 1 & 2 each turn makeMove(gameBoard,player); printBoard(gameBoard); winner = checkWinner(gameBoard,player); if (winner != 0) { printBoard(gameBoard); for (i=0;i<19-2*ROWS;i++) // Formatting - works with the default shell height on my machine printf("\n"); // Hopefully I can replace these with something that clears the screen for me printf("\n\nCongratulations Player %i, you've won with %i in a row!\n\n",winner,N); return winner; } } while ( turn < ROWS*COLS ); // Once ROWS*COLS turns have elapsed printf("\n\nGame Over!\n\nThere was no Winner :-(\n"); // The board is full and the game is over return winner; } void initBoard (char gameBoard[ROWS][COLS]) { int row = 0, col = 0; for (row=0;row ROWS-1 || col > COLS-1) // We are not using a do... while because { // I wanted the prompt to change printBoard(gameBoard); for (i=0;i<20-2*ROWS;i++) printf("\n"); printf("\nPlayer %i, please enter a valid move! Column first, then row.\n",player); scanf("%i %i",&col,&row); row--; // See above ^^^ col--; } gameBoard[row][col] = currentChar; // Finally, we store the correct mark into the given location return; // And pop back out of this function } int checkWinner(char gameBoard[ROWS][COLS], int player) // I've commented the last (and the hardest, for me anyway) { // check, which checks for backwards diagonal runs below >>> int row = 0, col = 0, i = 0; char currentChar; if (player == 1) currentChar = PLAYER1CHAR; else currentChar = PLAYER2CHAR; for ( row = 0; row < ROWS; row++) // This first for loop checks every row { for ( col = 0; col < (COLS-(N-1)); col++) // And all columns until N away from the end { while (gameBoard[row][col] == currentChar) // For consecutive rows of the current player's mark { col++; i++; if (i == N) { return player; } } i = 0; } } for ( col = 0; col < COLS; col++) // This one checks for columns of consecutive marks { for ( row = 0; row < (ROWS-(N-1)); row++) { while (gameBoard[row][col] == currentChar) { row++; i++; if (i == N) { return player; } } i = 0; } } for ( col = 0; col < (COLS - (N-1)); col++) // This one checks for "forwards" diagonal runs { for ( row = 0; row < (ROWS-(N-1)); row++) { while (gameBoard[row][col] == currentChar) { row++; col++; i++; if (i == N) { return player; } } i = 0; } } // Finally, the backwards diagonals: for ( col = COLS-1; col > 0+(N-2); col--) // Start from the last column and go until N columns from the first { // The math seems strange here but the numbers work out when you trace them for ( row = 0; row < (ROWS-(N-1)); row++) // Start from the first row and go until N rows from the last { while (gameBoard[row][col] == currentChar) // If the current player's character is there { row++; // Go down a row col--; // And back a column i++; // The i variable tracks how many consecutive marks have been found if (i == N) // Once i == N { return player; // Return the current player number to the } // winnner variable in the playGame function } // If it breaks out of the while loop, there weren't N consecutive marks i = 0; // So make i = 0 again } // And go back into the for loop, incrementing the row to check from } return 0; // If we got to here, no winner has been detected, } // so we pop back up into the playGame function // The end! // Well, almost. // Eventually I hope to get this thing going // with a dynamically sized array. I'll make // the CONSTANTS into variables in an initGame // function and allow the user to define them. 

    Se il tabellone è n × n allora ci sono n righe, n colonne e 2 diagonali. Controlla ciascuno di quelli per tutto-X o tutti-O per trovare un vincitore.

    Se solo vince x < n quadrati consecutivi per vincere, allora è un po ‘più complicato. La soluzione più ovvia è controllare ogni x × x quadrato per un vincitore. Ecco un codice che lo dimostra.

    (Non ho provato questo * cough *, ma è stato compilato al primo tentativo, yay me!)

     public class TicTacToe { public enum Square { X, O, NONE } /** * Returns the winning player, or NONE if the game has * finished without a winner, or null if the game is unfinished. */ public Square findWinner(Square[][] board, int lengthToWin) { // Check each lengthToWin x lengthToWin board for a winner. for (int top = 0; top < = board.length - lengthToWin; ++top) { int bottom = top + lengthToWin - 1; for (int left = 0; left <= board.length - lengthToWin; ++left) { int right = left + lengthToWin - 1; // Check each row. nextRow: for (int row = top; row <= bottom; ++row) { if (board[row][left] == Square.NONE) { continue; } for (int col = left; col <= right; ++col) { if (board[row][col] != board[row][left]) { continue nextRow; } } return board[row][left]; } // Check each column. nextCol: for (int col = left; col <= right; ++col) { if (board[top][col] == Square.NONE) { continue; } for (int row = top; row <= bottom; ++row) { if (board[row][col] != board[top][col]) { continue nextCol; } } return board[top][col]; } // Check top-left to bottom-right diagonal. diag1: if (board[top][left] != Square.NONE) { for (int i = 1; i < lengthToWin; ++i) { if (board[top+i][left+i] != board[top][left]) { break diag1; } } return board[top][left]; } // Check top-right to bottom-left diagonal. diag2: if (board[top][right] != Square.NONE) { for (int i = 1; i < lengthToWin; ++i) { if (board[top+i][right-i] != board[top][right]) { break diag2; } } return board[top][right]; } } } // Check for a completely full board. boolean isFull = true; full: for (int row = 0; row < board.length; ++row) { for (int col = 0; col < board.length; ++col) { if (board[row][col] == Square.NONE) { isFull = false; break full; } } } // The board is full. if (isFull) { return Square.NONE; } // The board is not full and we didn't find a solution. else { return null; } } } 

    Non conosco bene Java, ma conosco C, quindi ho provato l’idea quadrata magica di adk (insieme alla restrizione di ricerca di Hardwareguy ).

     // tic-tac-toe.c // to compile: // % gcc -o tic-tac-toe tic-tac-toe.c // to run: // % ./tic-tac-toe #include  // the two types of marks available typedef enum { Empty=2, X=0, O=1, NumMarks=2 } Mark; char const MarkToChar[] = "XO "; // a structure to hold the sums of each kind of mark typedef struct { unsigned char of[NumMarks]; } Sum; // a cell in the board, which has a particular value #define MAGIC_NUMBER 15 typedef struct { Mark mark; unsigned char const value; size_t const num_sums; Sum * const sums[4]; } Cell; #define NUM_ROWS 3 #define NUM_COLS 3 // create a sum for each possible tic-tac-toe Sum row[NUM_ROWS] = {0}; Sum col[NUM_COLS] = {0}; Sum nw_diag = {0}; Sum ne_diag = {0}; // initialize the board values so any row, column, or diagonal adds to // MAGIC_NUMBER, and so they each record their sums in the proper rows, columns, // and diagonals Cell board[NUM_ROWS][NUM_COLS] = { { { Empty, 8, 3, { &row[0], &col[0], &nw_diag } }, { Empty, 1, 2, { &row[0], &col[1] } }, { Empty, 6, 3, { &row[0], &col[2], &ne_diag } }, }, { { Empty, 3, 2, { &row[1], &col[0] } }, { Empty, 5, 4, { &row[1], &col[1], &nw_diag, &ne_diag } }, { Empty, 7, 2, { &row[1], &col[2] } }, }, { { Empty, 4, 3, { &row[2], &col[0], &ne_diag } }, { Empty, 9, 2, { &row[2], &col[1] } }, { Empty, 2, 3, { &row[2], &col[2], &nw_diag } }, } }; // print the board void show_board(void) { size_t r, c; for (r = 0; r < NUM_ROWS; r++) { if (r > 0) { printf("---+---+---\n"); } for (c = 0; c < NUM_COLS; c++) { if (c > 0) { printf("|"); } printf(" %c ", MarkToChar[board[r][c].mark]); } printf("\n"); } } // run the game, asking the player for inputs for each side int main(int argc, char * argv[]) { size_t m; show_board(); printf("Enter moves as \" \" (no quotes, zero indexed)\n"); for( m = 0; m < NUM_ROWS * NUM_COLS; m++ ) { Mark const mark = (Mark) (m % NumMarks); size_t c, r; // read the player's move do { printf("%c's move: ", MarkToChar[mark]); fflush(stdout); scanf("%d %d", &r, &c); if (r >= NUM_ROWS || c >= NUM_COLS) { printf("illegal move (off the board), try again\n"); } else if (board[r][c].mark != Empty) { printf("illegal move (already taken), try again\n"); } else { break; } } while (1); { Cell * const cell = &(board[r][c]); size_t s; // update the board state cell->mark = mark; show_board(); // check for tic-tac-toe for (s = 0; s < cell->num_sums; s++) { cell->sums[s]->of[mark] += cell->value; if (cell->sums[s]->of[mark] == MAGIC_NUMBER) { printf("tic-tac-toe! %c wins!\n", MarkToChar[mark]); goto done; } } } } printf("stalemate... nobody wins :(\n"); done: return 0; } 

    Compila e verifica bene.

     % gcc -o tic-tac-toe tic-tac-toe.c
     % ./tic-tac-toe
          |  |
       --- + --- + ---
          |  |
       --- + --- + ---
          |  |
       Inserisci mosse come "" (nessuna virgoletta, zero indicizzato)
       La mossa di X: 1 2
          |  |
       --- + --- + ---
          |  |  X
       --- + --- + ---
          |  |
       La mossa di O: 1 2
       mossa illegale (già presa), riprova
       La mossa di O: 3 3
       mossa illegale (fuori dal tabellone), riprova
       La mossa di O: 2 2
          |  |
       --- + --- + ---
          |  |  X
       --- + --- + ---
          |  |  O
       Spostamento di X: 1 0
          |  |
       --- + --- + ---
        X |  |  X
       --- + --- + ---
          |  |  O
       La mossa di O: 1 1
          |  |
       --- + --- + ---
        X |  O |  X
       --- + --- + ---
          |  |  O
       Spostamento di X: 0 0
        X |  |
       --- + --- + ---
        X |  O |  X
       --- + --- + ---
          |  |  O
       La mossa di O: 2 0
        X |  |
       --- + --- + ---
        X |  O |  X
       --- + --- + ---
        O |  |  O
       La mossa di X: 2 1
        X |  |
       --- + --- + ---
        X |  O |  X
       --- + --- + ---
        O |  X |  O
       Spostamento di O: 0 2
        X |  |  O
       --- + --- + ---
        X |  O |  X
       --- + --- + ---
        O |  X |  O
       tic-tac-toe!  O vince!
     % ./tic-tac-toe
          |  |
       --- + --- + ---
          |  |
       --- + --- + ---
          |  |
       Inserisci mosse come "" (nessuna virgoletta, zero indicizzato)
       Spostamento di X: 0 0
        X |  |
       --- + --- + ---
          |  |
       --- + --- + ---
          |  |
       Spostamento di O: 0 1
        X |  O |
       --- + --- + ---
          |  |
       --- + --- + ---
          |  |
       Spostamento di X: 0 2
        X |  O |  X
       --- + --- + ---
          |  |
       --- + --- + ---
          |  |
       La mossa di O: 1 0
        X |  O |  X
       --- + --- + ---
        O |  |
       --- + --- + ---
          |  |
       La mossa di X: 1 1
        X |  O |  X
       --- + --- + ---
        O |  X |
       --- + --- + ---
          |  |
       La mossa di O: 2 0
        X |  O |  X
       --- + --- + ---
        O |  X |
       --- + --- + ---
        O |  |
       La mossa di X: 2 1
        X |  O |  X
       --- + --- + ---
        O |  X |
       --- + --- + ---
        O |  X |
       La mossa di O: 2 2
        X |  O |  X
       --- + --- + ---
        O |  X |
       --- + --- + ---
        O |  X |  O
       La mossa di X: 1 2
        X |  O |  X
       --- + --- + ---
        O |  X |  X
       --- + --- + ---
        O |  X |  O
       stallo ... nessuno vince :(
     %
    

    E ‘stato divertente, grazie!

    In realtà, a pensarci bene, non hai bisogno di un quadrato magico, solo un conteggio per ogni riga / colonna / diagonale. Questo è un po ‘più facile della generalizzazione di un quadrato magico in n × n matrici, dal momento che devi solo contare su n .

    Mi è stata fatta la stessa domanda in una delle mie interviste. I miei pensieri: Inizializza la matrice con 0. Mantieni 3 matrici 1) sum_row (taglia n) 2) sum_column (taglia n) 3) diagonale (taglia 2)

    Per ogni mossa di (X) decrementa il valore di casella di 1 e per ogni mossa di (0) incrementalo di 1. In qualsiasi momento se la riga / colonna / diagonale che è stata modificata nello spostamento corrente ha sum o -3 o + 3 significa che qualcuno ha vinto la partita. Per un pareggio possiamo usare l’approccio sopra per mantenere la variabile moveCount.

    Pensi che mi manchi qualcosa?

    Modifica: Lo stesso può essere usato per la matrice nxn. La sum dovrebbe essere pari a +3 o -3.

    un modo non ciclico per determinare se il punto era sull’anti diag:

     `if (x + y == n - 1)` 

    Ho fatto un po ‘di ottimizzazione nei controlli di riga, col, diagonale. È deciso principalmente nel primo ciclo annidato se è necessario controllare una particolare colonna o diagonale. Quindi, evitiamo di controllare colonne o diagonali risparmiando tempo. Ciò ha un grande impatto quando la dimensione della scheda è maggiore e un numero significativo di celle non viene riempito.

    Ecco il codice java per quello.

      int gameState(int values[][], int boardSz) { boolean colCheckNotRequired[] = new boolean[boardSz];//default is false boolean diag1CheckNotRequired = false; boolean diag2CheckNotRequired = false; boolean allFilled = true; int x_count = 0; int o_count = 0; /* Check rows */ for (int i = 0; i < boardSz; i++) { x_count = o_count = 0; for (int j = 0; j < boardSz; j++) { if(values[i][j] == x_val)x_count++; if(values[i][j] == o_val)o_count++; if(values[i][j] == 0) { colCheckNotRequired[j] = true; if(i==j)diag1CheckNotRequired = true; if(i + j == boardSz - 1)diag2CheckNotRequired = true; allFilled = false; //No need check further break; } } if(x_count == boardSz)return X_WIN; if(o_count == boardSz)return O_WIN; } /* check cols */ for (int i = 0; i < boardSz; i++) { x_count = o_count = 0; if(colCheckNotRequired[i] == false) { for (int j = 0; j < boardSz; j++) { if(values[j][i] == x_val)x_count++; if(values[j][i] == o_val)o_count++; //No need check further if(values[i][j] == 0)break; } if(x_count == boardSz)return X_WIN; if(o_count == boardSz)return O_WIN; } } x_count = o_count = 0; /* check diagonal 1 */ if(diag1CheckNotRequired == false) { for (int i = 0; i < boardSz; i++) { if(values[i][i] == x_val)x_count++; if(values[i][i] == o_val)o_count++; if(values[i][i] == 0)break; } if(x_count == boardSz)return X_WIN; if(o_count == boardSz)return O_WIN; } x_count = o_count = 0; /* check diagonal 2 */ if( diag2CheckNotRequired == false) { for (int i = boardSz - 1,j = 0; i >= 0 && j < boardSz; i--,j++) { if(values[j][i] == x_val)x_count++; if(values[j][i] == o_val)o_count++; if(values[j][i] == 0)break; } if(x_count == boardSz)return X_WIN; if(o_count == boardSz)return O_WIN; x_count = o_count = 0; } if( allFilled == true) { for (int i = 0; i < boardSz; i++) { for (int j = 0; j < boardSz; j++) { if (values[i][j] == 0) { allFilled = false; break; } } if (allFilled == false) { break; } } } if (allFilled) return DRAW; return INPROGRESS; } 

    Sono in ritardo per la festa, ma volevo sottolineare un vantaggio che ho trovato usando un quadrato magico , vale a dire che può essere usato per ottenere un riferimento al quadrato che causerebbe la vittoria o la perdita nel turno successivo, piuttosto che appena usato per calcolare quando una partita è finita.

    Prendi questo quadrato magico:

     4 9 2 3 5 7 8 1 6 

    Innanzitutto, imposta un array di scores che viene incrementato ogni volta che viene effettuata una mossa. Vedi questa risposta per i dettagli. Ora, se giochiamo illegalmente a X due volte di fila a [0,0] e [0,1], la matrice dei scores appare così:

     [7, 0, 0, 4, 3, 0, 4, 0]; 

    E la scheda si presenta così:

     X . . X . . . . . 

    Quindi, tutto ciò che dobbiamo fare per ottenere un riferimento su quale casella vincere / bloccare è:

     get_winning_move = function() { for (var i = 0, i < scores.length; i++) { // keep track of the number of times pieces were added to the row // subtract when the opposite team adds a piece if (scores[i].inc === 2) { return 15 - state[i].val; // 8 } } } 

    In realtà, l'implementazione richiede alcuni trucchi aggiuntivi, come la gestione delle chiavi numerate (in JavaScript), ma l'ho trovato piuttosto semplice e ho apprezzato la matematica ricreativa.

    Mi piace questo algoritmo in quanto utilizza una rappresentazione 1×9 vs 3×3 della scheda.

     private int[] board = new int[9]; private static final int[] START = new int[] { 0, 3, 6, 0, 1, 2, 0, 2 }; private static final int[] INCR = new int[] { 1, 1, 1, 3, 3, 3, 4, 2 }; private static int SIZE = 3; /** * Determines if there is a winner in tic-tac-toe board. * @return {@code 0} for draw, {@code 1} for 'X', {@code -1} for 'Y' */ public int hasWinner() { for (int i = 0; i < START.length; i++) { int sum = 0; for (int j = 0; j < SIZE; j++) { sum += board[START[i] + j * INCR[i]]; } if (Math.abs(sum) == SIZE) { return sum / SIZE; } } return 0; } 

    Un’altra opzione: generare la tabella con il codice. Fino alla simmetria, ci sono solo tre modi per vincere: bordo, riga centrale o diagonale. Prendi questi tre e girali in ogni modo ansible:

     def spin(g): return set([g, turn(g), turn(turn(g)), turn(turn(turn(g)))]) def turn(g): return tuple(tuple(g[y][x] for y in (0,1,2)) for x in (2,1,0)) X,s = 'X.' XXX = X, X, X sss = s, s, s ways_to_win = ( spin((XXX, sss, sss)) | spin((sss, XXX, sss)) | spin(((X,s,s), (s,X,s), (s,s,X)))) 

    Queste simmetrie possono avere più usi nel tuo codice di gioco: se arrivi a una bacheca hai già visto una versione ruotata di, puoi semplicemente prendere il valore memorizzato nella cache o la mossa migliore memorizzata nella cache da quella (e ritriggersrla). Questo di solito è molto più veloce della valutazione della sottostruttura del gioco.

    Il ribaltamento a sinistra e a destra può essere d’aiuto allo stesso modo: non era necessario qui perché l’insieme delle rotazioni dei modelli vincenti è simmetrico a specchio.)

    Ecco una soluzione che ho trovato, questo memorizza i simboli come caratteri e usa il valore int del char per capire se X o O ha vinto (guarda il codice dell’Arbitro)

     public class TicTacToe { public static final char BLANK = '\u0000'; private final char[][] board; private int moveCount; private Referee referee; public TicTacToe(int gridSize) { if (gridSize < 3) throw new IllegalArgumentException("TicTacToe board size has to be minimum 3x3 grid"); board = new char[gridSize][gridSize]; referee = new Referee(gridSize); } public char[][] displayBoard() { return board.clone(); } public String move(int x, int y) { if (board[x][y] != BLANK) return "(" + x + "," + y + ") is already occupied"; board[x][y] = whoseTurn(); return referee.isGameOver(x, y, board[x][y], ++moveCount); } private char whoseTurn() { return moveCount % 2 == 0 ? 'X' : 'O'; } private class Referee { private static final int NO_OF_DIAGONALS = 2; private static final int MINOR = 1; private static final int PRINCIPAL = 0; private final int gridSize; private final int[] rowTotal; private final int[] colTotal; private final int[] diagonalTotal; private Referee(int size) { gridSize = size; rowTotal = new int[size]; colTotal = new int[size]; diagonalTotal = new int[NO_OF_DIAGONALS]; } private String isGameOver(int x, int y, char symbol, int moveCount) { if (isWinningMove(x, y, symbol)) return symbol + " won the game!"; if (isBoardCompletelyFilled(moveCount)) return "Its a Draw!"; return "continue"; } private boolean isBoardCompletelyFilled(int moveCount) { return moveCount == gridSize * gridSize; } private boolean isWinningMove(int x, int y, char symbol) { if (isPrincipalDiagonal(x, y) && allSymbolsMatch(symbol, diagonalTotal, PRINCIPAL)) return true; if (isMinorDiagonal(x, y) && allSymbolsMatch(symbol, diagonalTotal, MINOR)) return true; return allSymbolsMatch(symbol, rowTotal, x) || allSymbolsMatch(symbol, colTotal, y); } private boolean allSymbolsMatch(char symbol, int[] total, int index) { total[index] += symbol; return total[index] / gridSize == symbol; } private boolean isPrincipalDiagonal(int x, int y) { return x == y; } private boolean isMinorDiagonal(int x, int y) { return x + y == gridSize - 1; } } } 

    Anche qui ci sono i miei test unitari per validarlo funziona davvero

     import static com.agilefaqs.tdd.demo.TicTacToe.BLANK; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import org.junit.Test; public class TicTacToeTest { private TicTacToe game = new TicTacToe(3); @Test public void allCellsAreEmptyInANewGame() { assertBoardIs(new char[][] { { BLANK, BLANK, BLANK }, { BLANK, BLANK, BLANK }, { BLANK, BLANK, BLANK } }); } @Test(expected = IllegalArgumentException.class) public void boardHasToBeMinimum3x3Grid() { new TicTacToe(2); } @Test public void firstPlayersMoveMarks_X_OnTheBoard() { assertEquals("continue", game.move(1, 1)); assertBoardIs(new char[][] { { BLANK, BLANK, BLANK }, { BLANK, 'X', BLANK }, { BLANK, BLANK, BLANK } }); } @Test public void secondPlayersMoveMarks_O_OnTheBoard() { game.move(1, 1); assertEquals("continue", game.move(2, 2)); assertBoardIs(new char[][] { { BLANK, BLANK, BLANK }, { BLANK, 'X', BLANK }, { BLANK, BLANK, 'O' } }); } @Test public void playerCanOnlyMoveToAnEmptyCell() { game.move(1, 1); assertEquals("(1,1) is already occupied", game.move(1, 1)); } @Test public void firstPlayerWithAllSymbolsInOneRowWins() { game.move(0, 0); game.move(1, 0); game.move(0, 1); game.move(2, 1); assertEquals("X won the game!", game.move(0, 2)); } @Test public void firstPlayerWithAllSymbolsInOneColumnWins() { game.move(1, 1); game.move(0, 0); game.move(2, 1); game.move(1, 0); game.move(2, 2); assertEquals("O won the game!", game.move(2, 0)); } @Test public void firstPlayerWithAllSymbolsInPrincipalDiagonalWins() { game.move(0, 0); game.move(1, 0); game.move(1, 1); game.move(2, 1); assertEquals("X won the game!", game.move(2, 2)); } @Test public void firstPlayerWithAllSymbolsInMinorDiagonalWins() { game.move(0, 2); game.move(1, 0); game.move(1, 1); game.move(2, 1); assertEquals("X won the game!", game.move(2, 0)); } @Test public void whenAllCellsAreFilledTheGameIsADraw() { game.move(0, 2); game.move(1, 1); game.move(1, 0); game.move(2, 1); game.move(2, 2); game.move(0, 0); game.move(0, 1); game.move(1, 2); assertEquals("Its a Draw!", game.move(2, 0)); } private void assertBoardIs(char[][] expectedBoard) { assertArrayEquals(expectedBoard, game.displayBoard()); } } 

    Full solution: https://github.com/nashjain/tictactoe/tree/master/java

    How about a following approach for 9 slots? Declare 9 integer variables for a 3×3 matrix (a1,a2….a9) where a1,a2,a3 represent row-1 and a1,a4,a7 would form column-1 (you get the idea). Use ‘1’ to indicate Player-1 and ‘2’ to indicate Player-2.

    There are 8 possible win combinations: Win-1: a1+a2+a3 (answer could be 3 or 6 based on which player won) Win-2: a4+a5+a6 Win-3: a7+a8+a9 Win-4: a1+a4+a7 …. Win-7: a1+a5+a9 Win-8: a3+a5+a7

    Now we know that if player one crosses a1 then we need to reevaluate sum of 3 variables: Win-1, Win-4 and Win-7. Whichever ‘Win-?’ variables reaches 3 or 6 first wins the game. If Win-1 variable reaches 6 first then Player-2 wins.

    I do understand that this solution is not scalable easily.

    Constant time O(8), on average 4 short AND’s. Player = short number. Needs additional checks for making sure move is valid.

     // O(8) boolean isWinner(short X) { for (int i = 0; i < 8; i++) if ((X & winCombinations[i]) == winCombinations[i]) return true; return false; } short[] winCombinations = new short[]{ 7, 7 << 3, 7 << 6, // horizontal 73, 73 << 1, 73 << 2, // vertical 273, // diagonal 84 // anti-diagonal }; for (short X = 0; X < 511; X++) System.out.println(isWinner(X)); 

    This is a really simple way to check.

      public class Game() { Game player1 = new Game('x'); Game player2 = new Game('o'); char piece; Game(char piece) { this.piece = piece; } public void checkWin(Game player) { // check horizontal win for (int i = 0; i < = 6; i += 3) { if (board[i] == player.piece && board[i + 1] == player.piece && board[i + 2] == player.piece) endGame(player); } // check vertical win for (int i = 0; i <= 2; i++) { if (board[i] == player.piece && board[i + 3] == player.piece && board[i + 6] == player.piece) endGame(player); } // check diagonal win if ((board[0] == player.piece && board[4] == player.piece && board[8] == player.piece) || board[2] == player.piece && board[4] == player.piece && board[6] == player.piece) endGame(player); } 

    }

    If you have boarder field 5*5 for examle, I used next method of checking:

     public static boolean checkWin(char symb) { int SIZE = 5; for (int i = 0; i < SIZE-1; i++) { for (int j = 0; j  

    I think, it's more clear, but probably is not the most optimal way.

    I developed an algorithm for this as part of a science project once.

    You basically recursively divide the board into a bunch of overlapping 2×2 rects, testing the different possible combinations for winning on a 2×2 square.

    It is slow, but it has the advantage of working on any sized board, with fairly linear memory requirements.

    I wish I could find my implementation