import java.io.BufferedWriter; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.util.Random; import java.io.BufferedWriter; 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; private boolean playRenderer = true; Color colorP1; int currentPlayerInt; Coordinate cellCoor = null; public static void main(String[] args) { int sizeX = 15; int sizeY = 15; int nbToken = 50; int nbJetonsAligne = 5; boolean renderer = false; 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 "--size": if (i + 2 < args.length) { sizeX = Integer.parseInt(args[++i]); sizeY = Integer.parseInt(args[++i]); } break; case "--nbToken": if (i + 1 < args.length) { nbToken = Integer.parseInt(args[++i]); } break; case "--nbTokenToWin": if (i + 1 < args.length) { nbJetonsAligne = Integer.parseInt(args[++i]); } break; case "--renderer": if (i + 1 < args.length) { String bool = args[++i]; if (bool == "true") { renderer = true; } else { renderer = false; } } 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(false);// metre true ou fals si in veut l'affichage ou non g.renderer = new ConsoleRenderer(); g.NB_CELL_PLAY = nbJetonsAligne; g.renderer.init(g); g.board = new GomokuBoard(sizeX, sizeY); g.board.get(sizeX / 2, sizeY / 2).setState(Color.BLACK); g.player1 = new Human("un", Color.WHITE, nbToken); g.player2 = new GomokuAI("deux", Color.BLACK, nbToken - 1); // System.out.println(g.board); // g.renderer.update(); g.startGame(); } /** * This method init the game with these parameters. * * @param bot If the player want to play with a bot it's true. * @param name1 Name of player one. * @param name2 Name of player two. * @param tokens Number of tokens for each player. */ public void newGame(boolean bot, String name1, String name2, int tokens) { Color[] possible = { Color.WHITE, Color.BLACK }; int rnd = new Random().nextInt(possible.length); Color colorPlayer1 = possible[rnd]; Color colorPlayer2 = colorPlayer1.inverse(); this.player1 = new Human(name1, colorPlayer1, tokens); if (bot) { this.player2 = new GomokuAI(name2, colorPlayer2, tokens); } else { this.player2 = new Human(name2, colorPlayer2, tokens); } this.board = new GomokuBoard(15, 15); this.colorP1 = colorPlayer1; currentPlayer = this.player1.color == Color.WHITE ? this.player1 : this.player2; this.currentPlayerInt = this.player1.color == Color.WHITE ? 1 : 2; } /** * 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; if (this.playRenderer) { // If we play the game with the renderer. while (this.cellCoor == null) { try { wait(16); } catch (InterruptedException e) { this.save(null); System.out.println(e); } } cellToPlay = this.board.get(this.cellCoor); if (cellToPlay == null || !cellToPlay.isPlayable()) { // If the cell is not playable we return null to not // play. return null; } cellToPlay.setState(player.color); this.cellCoor = null; } else { 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; } }