Files
gomoku/src/GomokuGame.java
2025-05-06 16:56:03 +02:00

363 lines
12 KiB
Java

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;
}
}