This commit is contained in:
Cyprien111
2025-04-07 09:03:09 +02:00
6 changed files with 235 additions and 224 deletions

View File

@@ -1,7 +1,28 @@
public enum Color { public enum Color {
NIL, NIL(-1),
WHITE, WHITE(0),
BLACK; BLACK(1);
private final int val;
Color(int i) {
this.val = i;
}
public Color fromInt(int i) {
for (Color c : Color.values()) {
if (c.val == val) {
return c;
}
}
throw new IllegalArgumentException("Invalid value: " + this);
}
public Color inverse() {
if (this.val == -1)
throw new IllegalArgumentException("No inverse of NIL");
return fromInt((this.val + 1) % 2);
}
} }

View File

@@ -1,16 +1,48 @@
public class ConsoleRenderer extends GomokuRenderer { public class ConsoleRenderer extends GomokuRenderer {
public ConsoleRenderer() {} public ConsoleRenderer() {
}
@Override @Override
public void init(GomokuGame game) { public void init(GomokuGame game) {
System.out.println("Console Renderer initialized."); this.game = game;
System.out.println("ConsoleRenderer initialized");
} }
@Override @Override
public void update() { public void update() {
// TODO: draw the board // Print the board to the console
String[] board = game.getBoard().toString().split("\n");
String horizontalLine = getHorizontalLine();
int width = game.getBoard().getWidth();
int height = game.getBoard().getHeight();
// Print a separator line, followed by the game infos
System.out.println(horizontalLine);
System.out.println("|" + String.format(" %-" + (width - 1) + "s", "Gomoku Game!") + "|");
// System.out.println("Current player: " + game.getCurrentPlayer().getName());
// System.out.println("Number of tokens left: " + game.getCurrentPlayer().getTokensLeft());
// Print the board
System.out.println(horizontalLine);
for (int i = 0; i < height; i++) {
System.out.print("|");
System.out.print(board[i]);
System.out.println("|");
}
System.out.println(horizontalLine);
}
private String getHorizontalLine() {
StringBuilder sb = new StringBuilder();
sb.append("+");
for (int i = 0; i < game.getBoard().getWidth(); i++) {
sb.append("-");
}
sb.append("+");
return sb.toString();
} }
} }

View File

@@ -3,22 +3,22 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
public class GomokuAI extends Player{ public class GomokuAI extends Player {
// ------------------Constructors--------------------------
//------------------Constructors--------------------------
/** /**
* The constructor of the GomokuAI. * The constructor of the GomokuAI.
* @param name The name of the player. * @param name The name of the player.
* @param color The color of the player. * @param color The color of the player.
*/ */
public GomokuAI(String name,Color color){ public GomokuAI(String name, Color color) {
super(name,color); super(name, color);
} }
/** /**
* This class is an extends of the class Player, this allows the GomokuAI to choose his move. * This class is an extends of the class Player, this allows the GomokuAI to
* choose his move.
*/ */
/** The random initialization */ /** The random initialization */
@@ -26,17 +26,19 @@ public class GomokuAI extends Player{
/** The difficulty of the GomokuAI */ /** The difficulty of the GomokuAI */
int difficulty = 0; int difficulty = 0;
//------------------Methods-------------------------- // ------------------Methods--------------------------
/** /**
* Return the coordinate of the move played by the Gomoku AI. * Return the coordinate of the move played by the Gomoku AI.
*
* @param board The actual Gomoku board. * @param board The actual Gomoku board.
* @return The Cell of the move played. * @return The Cell of the move played.
*/ */
public GomokuCell chooseMove(GomokuBoard board){ @Override
public GomokuCell chooseMove(GomokuBoard board) {
List<GomokuCell> playableCell = board.getPlayableCells(); List<GomokuCell> playableCell = board.getPlayableCells();
GomokuCell playCell; GomokuCell playCell;
int x=0,y=0; int x = 0, y = 0;
switch (difficulty) { switch (difficulty) {
case 0: case 0:
@@ -44,9 +46,9 @@ public class GomokuAI extends Player{
break; break;
case 1: case 1:
Map<GomokuCell, Integer> map = GetCellPoint(board); Map<GomokuCell, Integer> map = GetCellPoint(board);
int max=0; int max = 0;
for (Map.Entry<GomokuCell, Integer> entry : map.entrySet()) { for (Map.Entry<GomokuCell, Integer> entry : map.entrySet()) {
if(entry.getValue() > max){ if (entry.getValue() > max) {
max = entry.getValue(); max = entry.getValue();
playCell = entry.getKey(); playCell = entry.getKey();
} }
@@ -55,25 +57,25 @@ public class GomokuAI extends Player{
throw new AssertionError(); throw new AssertionError();
} }
System.out.println("L'IA à choisi : "+ x+", "+ y); System.out.println("L'IA à choisi : " + x + ", " + y);
return playCell; return playCell;
} }
/** /**
* Return a Map of all Cell playable, and their point. * Return a Map of all Cell playable, and their point.
*
* @param board The actual Gomoku board. * @param board The actual Gomoku board.
* @return the Map of all Cell playable, and their point. * @return the Map of all Cell playable, and their point.
*/ */
public Map<GomokuCell, Integer> GetCellPoint(GomokuBoard board){ public Map<GomokuCell, Integer> GetCellPoint(GomokuBoard board) {
List<GomokuCell> playableCell = board.getPlayableCells(); List<GomokuCell> playableCell = board.getPlayableCells();
Map<GomokuCell, Integer> map = new HashMap<>(); Map<GomokuCell, Integer> map = new HashMap<>();
for (GomokuCell gomokuCell : playableCell) { for (GomokuCell gomokuCell : playableCell) {
switch(this.color){ switch (this.color) {
case Color.WHITE: case Color.WHITE:
gomokuCell.setState(Color.BLACK); gomokuCell.setState(Color.BLACK);
@@ -93,6 +95,4 @@ public class GomokuAI extends Player{
return map; return map;
} }
} }

View File

@@ -32,13 +32,13 @@ public class GomokuBoard{
System.out.println(Cardinal.NE.inverse()); System.out.println(Cardinal.NE.inverse());
System.out.println(test); System.out.println(test);
test.expandBoardv2(Cardinal.N); test.expandBoard(Cardinal.N);
System.out.println(test); System.out.println(test);
test.expandBoardv2(Cardinal.S); test.expandBoard(Cardinal.S);
System.out.println(test); System.out.println(test);
test.expandBoardv2(Cardinal.W); test.expandBoard(Cardinal.W);
System.out.println(test); System.out.println(test);
test.expandBoardv2(Cardinal.SE); test.expandBoard(Cardinal.SE);
System.out.println(test); System.out.println(test);
} }
@@ -106,6 +106,22 @@ public class GomokuBoard{
//------------------Gets-------------------------- //------------------Gets--------------------------
/**
* Returns the width of the board.
* @return The width of the board.
*/
public int getWidth() {
return this.boardWidth;
}
/**
* Returns the height of the board.
* @return The height of the board.
*/
public int getHeight() {
return this.boardHeight;
}
/** /**
* This method get a cell in specific position in the board. * This method get a cell in specific position in the board.
* @param x The position x on the board. * @param x The position x on the board.
@@ -165,6 +181,25 @@ public class GomokuBoard{
return output; return output;
} }
//------------------Gets--------------------------
/**
* Set the width of the board.
* @param width The new width of the board.
*/
public void setWidth(int width) {
this.boardWidth = width;
}
/**
* Set the height of the board.
* @param height The new height of the board.
*/
public void setHeight(int height) {
this.boardHeight = height;
}
//------------------Methods-------------------------- //------------------Methods--------------------------
/** /**
@@ -202,112 +237,6 @@ public class GomokuBoard{
return max; return max;
} }
/**
* This method create one line/column in a specific direction. This method is used in expandBoard, it's no recommended to
* used it in other context.
* @param act Is the cell of the start of the line/column.
* @param top Is the cell of the start of the line/column already existing.
* @param topDir The direction of the line top in relation of the line/column we create.
* @param m The direction topDir - 1.
* @param p The direction topDir + 1.
* @param dir Direction where to add the next cell.
* @param maxLen Number of cell ton gen.
*/
private void genLineOrColumn(GomokuCell act, GomokuCell top, Cardinal topDir, Cardinal m, Cardinal p, Cardinal dir, int maxLen){
for (int i = 0; i < maxLen; i++) {
if (top != null) {
GomokuCell.link(act, top.getNeighbour(topDir),m);
GomokuCell.link(act, top, topDir);
top = top.getNeighbour(dir);
GomokuCell.link(act, top , m);
}
if (i != maxLen -1 ){
GomokuCell right = new GomokuCell(Color.NIL);
GomokuCell.link(act, right, dir);
act = right;
}
}
}
/**
* This method expand the board.
* @param direction Is the direction where the method expand the board.
*/
public void expandBoard(Cardinal direction){
switch (direction) {
case Cardinal.SE:
this.expandBoard(Cardinal.S);
this.expandBoard(Cardinal.E);
return;
case Cardinal.NE:
this.expandBoard(Cardinal.N);
this.expandBoard(Cardinal.E);
return;
case Cardinal.SW:
this.expandBoard(Cardinal.S);
this.expandBoard(Cardinal.W);
return;
case Cardinal.NW:
this.expandBoard(Cardinal.N);
this.expandBoard(Cardinal.W);
return;
default:
break;
}
List<Cardinal> cardinals = Arrays.asList(Cardinal.values());
Cardinal topDirection = cardinals.get((cardinals.indexOf(direction) + 4)%8);
GomokuCell act = new GomokuCell(Color.NIL);
int maxLen = 0;
GomokuCell top = null;
GomokuCell newOrigin = null;
switch (direction) {
case Cardinal.N:
top = this.get(0, 0);
maxLen = this.boardWidth;
newOrigin = act;
this.genLineOrColumn(act, top, topDirection, Cardinal.SW, Cardinal.SE, Cardinal.E, maxLen);
this.boardHeight += 1;
break;
case Cardinal.W:
top = this.get(0,0);
maxLen = this.boardHeight;
newOrigin = act;
this.genLineOrColumn(act, top, topDirection, Cardinal.NE, Cardinal.SE, Cardinal.S, maxLen);
this.boardWidth += 1;
break;
case Cardinal.S:
top = this.get(this.boardHeight-1, 0);
maxLen = this.boardWidth;
newOrigin = this.get(0, 0);
this.genLineOrColumn(act, top, topDirection, Cardinal.NW, Cardinal.NE, Cardinal.E, maxLen);
this.boardHeight += 1;
break;
case Cardinal.E:
top = this.get(0, this.boardWidth-1);
maxLen = this.boardHeight;
newOrigin = this.get(0, 0);
this.genLineOrColumn(act, top, topDirection, Cardinal.NW, Cardinal.SW, Cardinal.S, maxLen);
this.boardWidth += 1;
break;
default:
throw new IllegalStateException("Why are you here ?");
}
this.firstCell = newOrigin;
}
/** /**
* Expands the board in the specified direction. * Expands the board in the specified direction.
* This method is used to add a new row, column, or both to the board. * This method is used to add a new row, column, or both to the board.
@@ -315,16 +244,15 @@ public class GomokuBoard{
* It also updates the board dimensions accordingly. * It also updates the board dimensions accordingly.
* @param direction The cardinal direction in which to expand the board. * @param direction The cardinal direction in which to expand the board.
* @see Cardinal * @see Cardinal
* @version 2.0
*/ */
public void expandBoardv2(Cardinal direction) { public void expandBoard(Cardinal direction) {
// for composed directions, expand in both components // for composed directions, expand in both components
// e.g. SE -> expand in S and E // e.g. SE -> expand in S and E
if ((direction.asInt() % 2) != 0) { if ((direction.asInt() % 2) != 0) {
// if the direction is not even, it is a composed direction // if the direction is not even, it is a composed direction
// call the same function in both components // call the same function in both components
this.expandBoardv2(direction.rotate(-1)); this.expandBoard(direction.rotate(-1));
this.expandBoardv2(direction.rotate(1)); this.expandBoard(direction.rotate(1));
return; return;
} }

View File

@@ -1,8 +1,10 @@
import java.io.BufferedReader; import java.io.BufferedReader;
import java.io.BufferedWriter; import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
import java.util.Random;
public class GomokuGame { public class GomokuGame {
@@ -13,10 +15,10 @@ public class GomokuGame {
private Player player1; private Player player1;
private Player player2; private Player player2;
private int boardWidth;
private int boardHeight;
private GomokuBoard board; private GomokuBoard board;
private GomokuRenderer renderer;
Color colorP1; Color colorP1;
int currP; int currP;
int nbTokens1, nbTokens2; int nbTokens1, nbTokens2;
@@ -25,18 +27,33 @@ public class GomokuGame {
// Test // Test
GomokuGame g = new GomokuGame(); GomokuGame g = new GomokuGame();
System.out.println(g.load(Path.of(".cache/test.dat"))); System.out.println(g.load(Path.of(".cache/test.dat")));
System.out.println(g.getBoard()); g.renderer = new ConsoleRenderer();
System.out.println(g.save(Path.of(".cache/save.dat"))); g.renderer.init(g);
g.renderer.update();
for (int i = 0; i < g.boardHeight; ++i) { g.board.expandBoard(Cardinal.SE);
for (int j = 0; j < g.boardWidth; ++j) { g.renderer.update();
System.out.print(g.getBoard().get(i ,j));
}
System.out.println();
}
} }
public void newGame() { public void newGame(boolean bot, String name1, String name2) {
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);
if (bot) {
this.player2 = new GomokuAI(name2, colorPlayer2);
} else {
this.player2 = new Human(name2, colorPlayer2);
}
this.board = new GomokuBoard(15, 15);
this.colorP1 = colorPlayer1;
this.currP = this.player1.color == Color.WHITE ? 1: 2;
this.nbTokens1 = 60;
this.nbTokens2 = 60;
} }
@@ -84,19 +101,32 @@ public class GomokuGame {
try (BufferedWriter writer = Files.newBufferedWriter(filename)) { try (BufferedWriter writer = Files.newBufferedWriter(filename)) {
// Write the first line // Write the first line
writer.write(String.format("%d %d %s %d %d %d%n", writer.write(String.format("%d %d %s %d %d %d%n",
this.boardWidth, this.boardHeight, colorP1, currP, nbTokens1, nbTokens2)); this.getBoard().getWidth(),
this.getBoard().getHeight(),
colorP1,
currP,
nbTokens1,
nbTokens2
));
// Write the board // Write the board
for (int i = 0; i < boardHeight; ++i) { for (int i = 0; i < this.getBoard().getHeight(); ++i) {
for (int j = 0; j < boardWidth; ++j) { for (int j = 0; j < this.getBoard().getWidth(); ++j) {
char c; char c = ' ';
switch (board.get(i, j).getState()) { switch (board.get(i, j).getState()) {
case BLACK: c = 'X'; break; case BLACK:
case WHITE: c = 'O'; break; c = 'X';
case NIL: c = '.'; break; break;
default: throw new IllegalStateException( case WHITE:
String.format("Unexpected value at cell (%d, %d): %s", i, j, board.get(i, j).getState())
); 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.write(c);
} }
@@ -159,9 +189,15 @@ public class GomokuGame {
for (int j = 0; j < w; ++j) { for (int j = 0; j < w; ++j) {
switch (line.charAt(j)) { switch (line.charAt(j)) {
case 'X': colors[i][j] = Color.BLACK; break; case 'X':
case 'O': colors[i][j] = Color.WHITE; break; colors[i][j] = Color.BLACK;
case '.': colors[i][j] = Color.NIL; break; break;
case 'O':
colors[i][j] = Color.WHITE;
break;
case '.':
colors[i][j] = Color.NIL;
break;
default: default:
throw new IllegalArgumentException("Invalid color: " + line.charAt(j)); throw new IllegalArgumentException("Invalid color: " + line.charAt(j));
} }
@@ -183,8 +219,6 @@ public class GomokuGame {
} }
// Initialize the board with the read values // Initialize the board with the read values
this.boardWidth = w;
this.boardHeight = h;
this.board = new GomokuBoard(w, h, colors); this.board = new GomokuBoard(w, h, colors);
return true; return true;

View File

@@ -1,78 +1,74 @@
import java.io.Console; import java.io.Console;
public class Human extends Player{ public class Human extends Player {
// ------------------Constructors--------------------------
//------------------Constructors--------------------------
/** /**
* The constructor of the Human. * The constructor of the Human.
*
* @param name The name of the player. * @param name The name of the player.
* @param color The color of the player. * @param color The color of the player.
*/ */
public Human(String name,Color color){ public Human(String name, Color color) {
super(name,color); super(name, color);
} }
/** /**
* This class is an extends of the class Player, this allows the player to choose his move. * This class is an extends of the class Player, this allows the player to
* choose his move.
*/ */
//------------------Methods-------------------------- // ------------------Methods--------------------------
/** /**
* Return the coordinate of the move played by the player. * Return the coordinate of the move played by the player.
*
* @param board The actual Gomoku board. * @param board The actual Gomoku board.
* @return The cell of the move played. * @return The cell of the move played.
*/ */
public GomokuCell chooseMove(GomokuBoard board){ @Override
public GomokuCell chooseMove(GomokuBoard board) {
Console cons = System.console(); Console cons = System.console();
int x=0,y=0; int x = 0, y = 0;
boolean pass = false; boolean pass = false;
do{ do {
do{ do {
try { try {
System.out.println("Veuillez saisir le X"); System.out.println("Veuillez saisir le X");
x = Integer.parseInt( cons.readLine()); x = Integer.parseInt(cons.readLine());
pass=true; pass = true;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
System.out.println("Ce n'est pas un nombre !"); System.out.println("Ce n'est pas un nombre !");
pass=false; pass = false;
} }
} } while (!pass);
while(!pass);
do{ do {
try { try {
System.out.println("Veuillez saisir le Y"); System.out.println("Veuillez saisir le Y");
y = Integer.parseInt( cons.readLine()); y = Integer.parseInt(cons.readLine());
pass=true; pass = true;
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
System.out.println("Ce n'est pas un nombre !"); System.out.println("Ce n'est pas un nombre !");
pass=false; pass = false;
} }
} } while (!pass);
while(!pass);
pass = board.get(x, y).isPlayable();
pass =board.get(x,y).isPlayable(); if (!pass) {
if(!pass)
{
System.out.println("Cette case n'est pas jouable !"); System.out.println("Cette case n'est pas jouable !");
} }
} } while (!pass);
while(!pass);
System.out.println("Vous avez saisi : "+ x+", "+ y); System.out.println("Vous avez saisi : " + x + ", " + y);
return board.get(x, y);
return board.get(x,y);
} }
} }