import java.util.Scanner; /** * Questo è lo stesso programma principale usato per l'esercizio 3, tranne per il fatto che utilizza * il metodo moveComputer di questa classe, invece di quello della classe Tris. Il metodo * moveComputer di questa classe sceglie una mossa in maniera intelligente. * * Se il tutto vi sembra contorto e complicato, avete ragione. Questa non è una soluzione "fatta bene" * al problema di far giocare il computer in modo intelligente. Per una soluzione "fatta bene" vi rimando * al corso di Intelligenza Artificiale del CLEII. */ public class Special { /** * Restituisce una copia di "board", in cui però il giocatore "player" ha eseguito la mossa "(i, * j)". Non modifica la variabile "board" originaria. Si suppone che la mossa "(i, j)" sia * valida. */ public static char[][] simulateMove(char[][] board, char player, int i, int j) { // creo un array bidimensionale di caratteri della dimensione corretta char[][] newBoard = new char[Tris.BOARD_DIMENSION][Tris.BOARD_DIMENSION]; // copio nel nuovo array, una ad una, i valori degli elementi del vecchio array for (int x = 0; x < Tris.BOARD_DIMENSION; x++) for (int y = 0; y < Tris.BOARD_DIMENSION; y++) newBoard[x][y] = board[x][y]; newBoard[i][j] = player; return newBoard; } /** * Restituisce true se la mossa "(i, j)" è vincente per "player" (nel senso che, mettendo lì, * "player" fa tris). Si suppone che la mossa "(i, j)" sia valida. */ private static boolean isWinningMove(char[][] board, char player, int i, int j) { // simulo la mossa con il metodo simulateMove char[][] boardNew = simulateMove(board, player, i, j); // controllo se in questa posizione player risulta vincente return Tris.winner(boardNew) == player; } /** * Restituisce, se presente, una mossa vincente per il giocatore "player". Il primo elemento * dell'array è la riga della mossa vincente, il secondo elemento è la colonna. Se "player" non * ha una mossa vincente restituisce null. * * Utilizzo un array in questo modo perché mi serve restituire due valori e non c'è in Java un * modo diretto per farlo. */ public static int[] winningMove(char[][] board, char player) { for (int i = 0; i < Tris.BOARD_DIMENSION; i++) for (int j = 0; j < Tris.BOARD_DIMENSION; j++) // se la posizione (i, j) è libera, e muovere in quella posizione è vincente per // player, restituisce l'array {i, j} if (board[i][j] == ' ' && isWinningMove(board, player, i, j)) { System.out.println("" + i + " " + j); return new int[] {i, j}; } // non ho trovato nessuna mossa vincente, quindi restituisco null return null; } /** * Conta il numero di posizioni vincenti per il giocatore "player" e restituisce il risultato. */ public static int countWinningMoves(char[][] board, char player) { // dichiaro una variabile usata per contare le mosse vincenti int count = 0; for (int i = 0; i < Tris.BOARD_DIMENSION; i++) for (int j = 0; j < Tris.BOARD_DIMENSION; j++) // se la posizione (i, j) è libera, e muovere in quella posizione è vincente per // player, aggiorno il contatore if (board[i][j] == ' ' && isWinningMove(board, player, i, j)) count++; // restituisco il numero di mosse vincenti trovate return count; } /** * Restituisce true se la mossa "(i, j)" del giocatore "player" è forzante, ovvero se il * giocatore avversario deve rispondere, pena la sconfitta. Si suppone che la mossa "(i, j)" sia * valida e non vincente. */ public static boolean isForcingMove(char[][] board, char player, int i, int j) { // simulo lo svolgimento della mossa (i,j) da parte di player char[][] boardNew = simulateMove(board, player, i, j); // se nella posiziona che si ottiene in questo modo player ha almeno una mossa vincente // mentre l'altro giocatore non ne ha nessuna, allora la mossa è forzante return countWinningMoves(boardNew, player) > 0 && countWinningMoves(boardNew, Tris.nextPlayer(player)) == 0; } /** * Restituisce true se, muovendo in "(i, j)", il giocatore "player" ottiene due mosse vincenti * diverse, mentre l'altro giocatore non ne ha nessuna. In questa situazione, il giocatore * "player" vincerà sicuramente nel momento in cui tocca di nuovo a lui. Si suppone che la mossa * "(i, j)" sia valida e non vincente (nel senso, che non dà la vittoria immediatamente). */ private static boolean isWinningMoveInTwoMoves(char[][] board, char player, int i, int j) { // simulo lo svolgimento della mossa (i,j) da parte di player char[][] boardNew = simulateMove(board, player, i, j); // se nella posiziona che si ottiene in questo modo player ha almeno due mosse vincenti // mentre l'altro giocatore non ne ha nessuna, allora la mossa è vincente in due mosse return countWinningMoves(boardNew, player) > 1 && countWinningMoves(boardNew, Tris.nextPlayer(player)) == 0; } /** * Restituisce, se esiste, una mossa che consente al giocatore "player" di vincere in due mosse, * altrimenti restituisce null. Si suppone che non esista nessuna mossa immediatamente vincente. */ public static int[] winningMoveInTwoMoves(char[][] board, char player) { for (int i = 0; i < Tris.BOARD_DIMENSION; i++) for (int j = 0; j < Tris.BOARD_DIMENSION; j++) // se la posizione (i, j) è libera, e muovere in quella posizione è vincente in due // mosse per player, restituisce l'array {i, j} if (board[i][j] == ' ' && isWinningMoveInTwoMoves(board, player, i, j)) { return new int[] {i, j}; } // non ho trovato nessuna posizione vincente in due mosse, quindi restituisco null return null; } /** * Restituisce true se la mossa "(i, j)" è di disturbo per il giocatore "player". Una mossa di * disturbo è una mossa forzante, che non mette l'avversario nella situazione di poter vincere * in due mosse. Si suppone che la mossa "(i, j)" sia valida, non vincente e non vincente in due * mosse per il giocatore "player". */ public static boolean isDisturbingMove(char[][] board, char player, int i, int j) { // se questa mossa non è forzante, non può essere di disturbo if (!isForcingMove(board, player, i, j)) return false; // simulo lo svolgimento della mossa (i,j) da parte di player char[][] boardNew = simulateMove(board, player, i, j); // determino la mossa vincente che player ottiene in questo modo (esiste di sicuro perché // la mossa (i, j) è forzante int[] move = winningMove(boardNew, player); // la mossa (i, j) è forzante se, nel rispondere ad essa, l'altro giocatore non ottiene // una vittoria in due mosse return !isWinningMoveInTwoMoves(boardNew, Tris.nextPlayer(player), move[0], move[1]); } /** * Restituisce, se esiste, una mossa di disturbi per il giocatore "player", altrimenti * restituisce null. */ public static int[] distrubingMove(char[][] board, char player) { for (int i = 0; i < Tris.BOARD_DIMENSION; i++) for (int j = 0; j < Tris.BOARD_DIMENSION; j++) // se la posizione (i, j) è libera, e muovere in quella posizione è vincente per // player, restituisce l'array {i, j} if (board[i][j] == ' ' && isDisturbingMove(board, player, i, j)) { return new int[] {i, j}; } // non ho trovato nessuna mossa vincente, quindi restituisco null return null; } public static void moveComputer(char[][] board, char player) { // determina, se esiste, una mossa vincente per player int[] move = winningMove(board, player); // se la mossa esiste, la gioco if (move != null) { board[move[0]][move[1]] = player; return; } // determina, se esiste, una mossa vincente per l'altro giocatore move = winningMove(board, Tris.nextPlayer(player)); // se la mossa esiste, la gioco per impedire all'altro di vincere if (move != null) { board[move[0]][move[1]] = player; return; } // determina, se esiste, una mossa vincente in due mosse per player move = winningMoveInTwoMoves(board, player); if (move != null) { board[move[0]][move[1]] = player; return; } move = distrubingMove(board, player); if (move != null) { board[move[0]][move[1]] = player; return; } // determina, se esiste, una mossa vincente in due mosse per l'altro player move = winningMoveInTwoMoves(board, Tris.nextPlayer(player)); if (move != null) { board[move[0]][move[1]] = player; return; } // se il centro è libero, metto nel centro if (board[1][1] == ' ') { board[1][1] = player; return; } // altrimenti muove nella prima posizione libera usando il metodo moveComputer della classe // Tris Tris.moveComputer(board, player); } public static void main(String[] args) { Scanner kbd = new Scanner(System.in); char[][] board = Tris.createEmptyBoard(); char player = 'O'; char winner = ' '; int moves = 0; while (winner == ' ' && moves < Tris.BOARD_DIMENSION * Tris.BOARD_DIMENSION) { Tris.showBoard(board); if (player == 'X') { System.out.print( "Giocatore " + player + ", inserisci riga e colonna dell tua mossa: "); int movei = kbd.nextInt(); int movej = kbd.nextInt(); boolean result = Tris.movePlayer(board, movei, movej, 'X'); if (!result) { System.out.println("MOSSA NON VALIDA!"); continue; } } else { System.out.println("Muove il computer"); // ATTENZIONE: uso "moveComputer" (che è il metodo definito in questa classe) invece // di // Tris.moveComputer (che è il metodo definito nella classe Tris). moveComputer(board, 'O'); } // Aggiorna le variabili player, moves e winne player = Tris.nextPlayer(player); moves += 1; winner = Tris.winner(board); } Tris.showBoard(board); if (winner != ' ') System.out.println("HA VINTO IL GIOCATORE " + winner); else System.out.println("PAREGGIO"); kbd.close(); } }