import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Random; import java.util.Random; public class GomokuGame { public static final int DEFAULT_BOARD_WIDTH = 15; // largeur du plateau public static final int DEFAULT_BOARD_HEIGHT = 15; // hauteur du plateau public static final int DEFAULT_TOKENS_COUNT = 60; // nb de jetons public int NB_CELL_PLAY = 5; // nb de jetons private Player player1; private Player player2; private Player currentPlayer; private GomokuBoard board; private GomokuRenderer renderer; Color colorP1; int currentPlayerInt; Coordinate cellCoor = null; GomokuGame(int nbToken, int jtToWin, int x, int y, boolean pvp, int difficulty) { Random r = new Random(); this.renderer = new ConsoleRenderer(); this.NB_CELL_PLAY = jtToWin; this.renderer.init(this); this.board = new GomokuBoard(x, y); this.board.get(x / 2, y / 2).setState(Color.BLACK); if (r.nextInt() % 2 == 0) { this.player1 = new Human("Premier joueur", Color.WHITE, nbToken); if (pvp) { this.player2 = new Human("Deuxieme joueur", Color.WHITE, nbToken); } this.player2 = new GomokuAI("deux", Color.BLACK, nbToken - 1, difficulty); } else { this.player2 = new Human("Premier joueur", Color.WHITE, nbToken); if (pvp) { this.player1 = new Human("Deuxieme joueur", Color.WHITE, nbToken); } this.player1 = new GomokuAI("deux", Color.BLACK, nbToken - 1, difficulty); } } public static void main(String[] args) { int sizeX = 15; int sizeY = 15; int nbToken = 50; int nbJetonsAligne = 5; int difficulty = 2; boolean pvp = true; String path_a_load = ""; for (int i = 0; i < args.length; i++) { switch (args[i]) { case "--save": if (i + 1 < args.length) { path_a_load = args[++i]; } break; case "--pvp": pvp = true; break; case "--size": if (i + 2 < args.length) { try { sizeX = Integer.parseInt(args[++i]); sizeY = Integer.parseInt(args[++i]); } catch (Exception e) { System.out.println("Arguments de --size invalides, config par défaut a été appliquée."); } } break; case "--nbToken": if (i + 1 < args.length) { try { nbToken = Integer.parseInt(args[++i]); } catch (Exception e) { System.out.println("Argument de --nbToken invalide, config par défaut a été appliquée."); } } break; case "--difficulty": if (i + 1 < args.length) { try { difficulty = Integer.parseInt(args[++i]); if (difficulty > 3 || difficulty < 1) { System.out.println( "Argument de --difficluty invalide, config par défaut a été appliquée."); difficulty = 2; } } catch (Exception e) { System.out.println("Argument de --difficluty invalide, config par défaut a été appliquée."); } } break; case "--nbTokenToWin": if (i + 1 < args.length) { try { nbJetonsAligne = Integer.parseInt(args[++i]); } catch (Exception e) { System.out .println("Argument de --nbTokenToWin invalide, config par défaut a été appliquée."); } } break; default: System.out.println("Invalid option : " + args[i]); break; } } if (sizeY < 3 || sizeX < 3) { System.out.println("Board is too small !"); return; } GomokuGame g = new GomokuGame(nbToken, nbJetonsAligne, sizeX, sizeY, pvp, difficulty); g.startGame(); } /** * This method is the main loop of the game */ public void startGame() { /* * GomokuCell actualPlayedCell = play(player1); * if( NB_CELL_PLAY <= * board.countMax(board.countAlignedCells(actualPlayedCell))) * { * System.out.println("c'est gangée !"); * } */ this.currentPlayer = player1; renderer.update(); while (this.player1.tokens > 0 || this.player2.tokens > 0) { GomokuCell currentPlay = null; while (currentPlay == null) { currentPlay = this.play(this.currentPlayer); } // Expand the board if one of the neighbours of the cell is null // Only check for North, South, East and West neighbours for (Cardinal cardinal : Cardinal.NS_EW) { GomokuCell neighbour = currentPlay.getNeighbour(cardinal); if (neighbour == null) { this.board.expandBoard(cardinal); } } if (NB_CELL_PLAY <= board.countMax(board.countAlignedCells(currentPlay))) { this.renderer.updateStatus("Le joueur " + this.currentPlayer + "a gagné !"); return; } this.currentPlayer.tokens -= 1; this.currentPlayer = this.nextPlayer(); renderer.update(); } this.renderer.updateStatus("Match nul, il ne reste plus de jetons."); return; } /** * Place the token on the cell where the player play. * * @param Player get player to play the cell. */ public GomokuCell play(Player player) { GomokuCell cellToPlay = null; cellToPlay = player.chooseMove(this.board); cellToPlay.setState(player.color); return cellToPlay; } /** * This method save the game on a file. * * @param filename The file to save. * @return True if successful and false if not. */ public boolean save(Path filename) { // save the game state to the file // 1. Open the file // 2. Write the first line with: // w h colorP1 currentPlayerInt nbTokens1 nbTokens2 // 3. Write the next h lines with the board // 4. Close the file // 5. Return true if successful, false otherwise // if filename is null, save to default file in ".cache/save.dat" if (filename == null) { filename = Path.of(".cache/save.dat"); } try (BufferedWriter writer = Files.newBufferedWriter(filename)) { // Write the first line writer.write(String.format("%d %d %s %d %d %d%n", this.getBoard().getWidth(), this.getBoard().getHeight(), colorP1, currentPlayerInt, this.player1.tokens, this.player2.tokens)); // Write the board for (int i = 0; i < this.getBoard().getHeight(); ++i) { for (int j = 0; j < this.getBoard().getWidth(); ++j) { char c; switch (board.get(i, j).getState()) { case BLACK: c = 'X'; break; case WHITE: c = 'O'; break; case NIL: c = '.'; break; default: throw new IllegalStateException( String.format("Unexpected value at cell (%d, %d): %s", i, j, board.get(i, j).getState())); } writer.write(c); } writer.newLine(); } } catch (IOException e) { System.err.println("Error writing file: " + e.getMessage()); return false; } catch (Exception e) { System.err.println("Unexpected error: " + e.getMessage()); return false; } return true; } /** * This method load a game on the file. * * @param filename The file to load. * @return True if successful and False if not. */ public boolean load(Path filename) { // load the game state from the file // 1. Open the file // 2. Read the first line to get: // w h colorP1 currentPlayerInt nbTokens1 nbTokens2 // 3. Read the next h lines to get the board // 4. Close the file // 5. Initialize the board with the read values int w, h; Color[][] colors; try (BufferedReader reader = Files.newBufferedReader(filename)) { String line = reader.readLine(); if (line == null) { throw new IOException("Empty save file"); } String[] parts = line.split(" "); if (parts.length != 6) { throw new IOException("Invalid save file format"); } w = Integer.parseInt(parts[0]); h = Integer.parseInt(parts[1]); colorP1 = Color.valueOf(parts[2]); currentPlayerInt = Integer.parseInt(parts[3]); this.player1.tokens = Integer.parseInt(parts[4]); this.player2.tokens = Integer.parseInt(parts[5]); if (w <= 0 || h <= 0) { throw new IllegalArgumentException("Invalid board dimensions"); } colors = new Color[h][w]; for (int i = 0; i < h; ++i) { line = reader.readLine(); if (line == null) { throw new IOException("Unexpected end of file"); } if (line.length() != w) { throw new IOException("Invalid board line format"); } for (int j = 0; j < w; ++j) { switch (line.charAt(j)) { case 'X': colors[i][j] = Color.BLACK; break; case 'O': colors[i][j] = Color.WHITE; break; case '.': colors[i][j] = Color.NIL; break; default: throw new IllegalArgumentException("Invalid color: " + line.charAt(j)); } } } } catch (IOException e) { System.err.println("Error reading file: " + e.getMessage()); return false; } catch (NumberFormatException e) { System.err.println("Invalid number format: " + e.getMessage()); return false; } catch (IllegalArgumentException e) { System.err.println("Invalid color format: " + e.getMessage()); return false; } catch (Exception e) { System.err.println("Unexpected error: " + e.getMessage()); return false; } // Initialize the board with the read values this.board = new GomokuBoard(w, h, colors); return true; } /** * This method return the next player to play. * * @return The next player. */ private Player nextPlayer() { if (this.currentPlayer == this.player1) { return this.player2; } return player1; } /** * Get the board. */ public GomokuBoard getBoard() { return this.board; } }