import java.util.Arrays; import java.util.Scanner; /** * Programma per giocare a Forza 4 tra due giocatori umani. * * Lo stato attuale della griglia del gioco viene memorizzato in un array bidimensionale di * caratteri (che chiamiamo board). Ogni cella dell'array corrisponde ad una posizione nella griglia * del gioco. Essa può contenere uno spazio ' ', per indicare che la cella è vuota, oppure una X o * una O (i simboli dei due giocatori che si sfidano) per indicare la presenza di un gettone di quel * giocatore. La dimensione del campo da gioco può variare da una partita all'altra. */ public class Forza4Completo { /** * Visualizza il contenuto dell'array "board" sullo schermo in un formato quanto più possibile * simile ad una griglia di gioco di Forza 4. */ public static void showBoard(char[][] board) { /* * Ricordiamo che non possiamo semplicemente scrivere "System.out.println(board)", perché * quello che viene visualizzato in questo caso è l'indirizzo in memoria dell'array. Anche * il comando "System.out.println(Arrays.deepToString(board)" non va bene: visualizza * effettivamente il contenuto di board, ma in un formato che non ricorda minimamente la * griglia di Forza 4. */ for (var row : board) { System.out.print('|'); for (var token : row) { System.out.print(token); System.out.print('|'); } System.out.println(); } } /** * Crea un array rappresentante una griglia di gioco con "numRows" righe e "numCols" colonne, * completamente vuota (ovvero tutti gli elementi dell'array devono contenere uno spazio). */ public static char[][] createEmptyBoard(int numRows, int numCols) { char[][] board = new char[numRows][numCols]; /* * Quando viene creato un array di char con il costrutto new, le celle vengono inizializzate * con il caratter NUL, che ha codice ascii 0. Noi dobbiamo rimpiazzarle con spazi. // Uso * il metodo fill della classe Arrays che riempie tutti gli elementi di un array valore * specifico. Siccome funziona solo per array 1D, riempio una riga alla volta di board */ for (var row : board) Arrays.fill(row, ' '); return board; } /** * Controlla che, data la griglia di gioco rappresentana dall'array "board", la mossa di mettere * un gettone nella colonna "col" sia valida. Più precisamente, la mossa è valida se "col" è * compreso tra 0 e il numero di colonna massimo possibile, e se nella colonna "col" c'è spazio * per inserire un nuovo gettone. */ public static boolean checkMove(char[][] board, int col) { /* * Il numero di colonne di "board" lo possiamo ottenere come numero di elementi della prima * riga di "board" (board[0].length). Per controllare se la colonna "col" è piena oppure no, * basta controllare se, nella prima riga della griglia, la posizione "col" è libera (da cui * il test board[0][col] == ' ') */ return col >= 0 && col < board[0].length && board[0][col] == ' '; } /** * Modifica l'array "board", con il nuovo stato del gioco che consegue all'inserimento di un * gettone del giocatore "player" nella colonna "col". Notare che questo metodo viene chiamato * solo se la mossa di inserire un gettone nella colonna "col" è valida. */ public static void move(char[][] board, int col, char player) { /* * Prima di tutto dobbiamo capire in quale riga della griglia va a finire il gettone che * mettiamo nella colonna "col". Per far ciò, controlliamo se c'è uno spazio nella riga 0 * colonna "col". Se c'è uno spazio, andiamo avanti e controlliamo la riga 1, quindi la riga * 2, etc... finché incontriamo un cella occupata (che non contiene spazi) o finiamo la * griglia. Tutto ciò è fatto del ciclo while che segue. */ int i = 0; while (i < board.length && board[i][col] == ' ') { i += 1; } /* * A questo punto la variabile "i" punta alla prima riga occuata della colonna "col" (se questa * riga esiste) o alla riga "board.length" (che in realtà non esiste, peché le righe vanno da 0 * a board.length -1). In entrambi i casi, il gettone del giocatore va inserito nella riga i-1, * cosa che fa la prossima istruzione. */ board[i - 1][col] = player; } /** * Dato in input il simbolo del giocatore corrente, restituisce il simbolo del prossimo * giocatore. In sostanza, se "player" è 'X' restituisce 'O', se "player" è 'O' restituisce 'X'. */ public static char nextPlayer(char player) { if (player == 'X') return 'O'; else return 'X'; } /** * Considera la cella in riga "r" e colonna "c" della griglia rappresentata in "board". Se da * quella cella parte una sequenza di gettoni che rende vincente il giocatore g, allora * restituisce g. In caso contrario, restituisce uno spazio. */ public static char winnerFrom(char[][] board, int r, int c) { char player = board[r][c]; if (c <= board[0].length - 4 && board[r][c + 1] == player && board[r][c + 2] == player && board[r][c + 3] == player) return player; if (r <= board.length - 4 && board[r + 1][c] == player && board[r + 2][c] == player && board[r + 3][c] == player) return player; if (r <= board.length - 4 && c <= board[0].length - 4 && board[r + 1][c + 1] == player && board[r + 2][c + 2] == player && board[r + 3][c + 3] == player) return player; if (r <= board.length - 4 && c >= 3 && board[r + 1][c - 1] == player && board[r + 2][c - 2] == player && board[r + 3][c - 3] == player) return player; return ' '; } /** * Restituisce il giocatore che è vincente nella griglia rappresentata da "board". Se non c'è * nessun giocatore vincente, restituisce spazio. Se più di un giocatore è vincente, restituisce * uno qualunque tra 'X' ed 'O'. */ public static char winner(char[][] board) { for (int i = 0; i < board.length; i++) for (int j = 0; j < board[0].length; j++) { var winner = winnerFrom(board, i, j); if (winner != ' ') return winner; } return ' '; } /** * Programma principale del gioco di Forza 4. */ public static void main(String[] args) { Scanner kbd = new Scanner(System.in); // Chiede ai giocatori la dimensione del campo da gioco. System.out.print("Inserisci numero di righe e colonne della matrice di gioco: "); var n = kbd.nextInt(); var m = kbd.nextInt(); // Usiamo la variabile "board" per memorizzare la situazione attuale del campo da gioco. // Inizialmente creiamo un campo da gioco completamente vuoto usando il metodo // "createEmptyBoard". var board = createEmptyBoard(n, m); // Usiamo la variabile "player" per memorizzare il simbolo del giocatore che deve fare la // prossima mossa. Si inizia con il giocatore 'X'. var player = 'X'; // Usiamo la variabile "winner" per memorizzare il simbolo del giocatore che ha vinto. // Inizialmente contiene uno spazio perché non ha vinto nessuno. var winner = ' '; // Usiamo la variabile "moves" per contare il numero di mosse effettuare var moves = 0; // Continua finché non c'è un vincitore oppure è stato riempito tutto il campo da gioco. while (winner == ' ' && moves < n * m) { // Visualizza il campo da gioco attuale. showBoard(board); // Chiede la mossa al giocatore corrente. System.out.print("Giocatore " + player + ", scrivi la tua mossa: "); var choice = kbd.nextInt() - 1; // Controlla se la mossa è valida. if (checkMove(board, choice)) { // Se la mossa è valida, la esegue. move(board, choice, player); // Aggiorna la variabile "winner", nel caso ci sia un vincitore. winner = winner(board); // Aggiorna il giocatore corrente. player = nextPlayer(player); // Incrementa il numero di mosse effettuate. moves += 1; } else { // Se la mossa non è valida, visualizza un messaggio di errore. System.out.println("MOSSA NON VALIDA, RIPROVA"); } } // Mostra l'ultima volta il campo da gioco e comunica il risultato della partita. showBoard(board); if (winner != ' ') System.out.println("HA VINTO IL GIOCATORE " + winner); else System.out.println("PAREGGIO"); kbd.close(); } }