diff --git a/src/Color.java b/src/Color.java index 86b6b62..af9eeaa 100644 --- a/src/Color.java +++ b/src/Color.java @@ -1,7 +1,28 @@ - public enum Color { - NIL, - WHITE, - BLACK; + NIL(-1), + WHITE(0), + 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); + } } diff --git a/src/ConsoleRenderer.java b/src/ConsoleRenderer.java index 8388013..547179e 100644 --- a/src/ConsoleRenderer.java +++ b/src/ConsoleRenderer.java @@ -1,16 +1,48 @@ - public class ConsoleRenderer extends GomokuRenderer { - public ConsoleRenderer() {} + public ConsoleRenderer() { + } @Override public void init(GomokuGame game) { - System.out.println("Console Renderer initialized."); + this.game = game; + System.out.println("ConsoleRenderer initialized"); } @Override 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(); } } diff --git a/src/GomokuAI.java b/src/GomokuAI.java index 13c3f55..e3a8a2e 100644 --- a/src/GomokuAI.java +++ b/src/GomokuAI.java @@ -3,50 +3,52 @@ import java.util.List; import java.util.Map; import java.util.Random; -public class GomokuAI extends Player{ - +public class GomokuAI extends Player { - //------------------Constructors-------------------------- + // ------------------Constructors-------------------------- /** * The constructor of the GomokuAI. * @param name The name of the player. * @param color The color of the player. */ - public GomokuAI(String name,Color color){ - super(name,color); + public GomokuAI(String name, Color 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 */ Random random = new Random(); /** The difficulty of the GomokuAI */ int difficulty = 0; - //------------------Methods-------------------------- + // ------------------Methods-------------------------- /** - * Return the coordinate of the move played by the Gomoku AI. - * @param board The actual Gomoku board. - * @return The Cell of the move played. - */ - public GomokuCell chooseMove(GomokuBoard board){ - + * Return the coordinate of the move played by the Gomoku AI. + * + * @param board The actual Gomoku board. + * @return The Cell of the move played. + */ + @Override + public GomokuCell chooseMove(GomokuBoard board) { + List playableCell = board.getPlayableCells(); GomokuCell playCell; - int x=0,y=0; - + int x = 0, y = 0; + switch (difficulty) { case 0: playCell = playableCell.get(random.nextInt(playableCell.size())); break; case 1: Map map = GetCellPoint(board); - int max=0; + int max = 0; for (Map.Entry entry : map.entrySet()) { - if(entry.getValue() > max){ + if (entry.getValue() > max) { max = entry.getValue(); playCell = entry.getKey(); } @@ -55,25 +57,25 @@ public class GomokuAI extends Player{ throw new AssertionError(); } - System.out.println("L'IA à choisi : "+ x+", "+ y); + System.out.println("L'IA à choisi : " + x + ", " + y); - return playCell; } /** - * Return a Map of all Cell playable, and their point. - * @param board The actual Gomoku board. - * @return the Map of all Cell playable, and their point. - */ - public Map GetCellPoint(GomokuBoard board){ - + * Return a Map of all Cell playable, and their point. + * + * @param board The actual Gomoku board. + * @return the Map of all Cell playable, and their point. + */ + public Map GetCellPoint(GomokuBoard board) { + List playableCell = board.getPlayableCells(); Map map = new HashMap<>(); for (GomokuCell gomokuCell : playableCell) { - switch(this.color){ + switch (this.color) { case Color.WHITE: gomokuCell.setState(Color.BLACK); @@ -93,6 +95,4 @@ public class GomokuAI extends Player{ return map; } - - } diff --git a/src/GomokuBoard.java b/src/GomokuBoard.java index 2bd412b..6f469be 100644 --- a/src/GomokuBoard.java +++ b/src/GomokuBoard.java @@ -32,13 +32,13 @@ public class GomokuBoard{ System.out.println(Cardinal.NE.inverse()); System.out.println(test); - test.expandBoardv2(Cardinal.N); + test.expandBoard(Cardinal.N); System.out.println(test); - test.expandBoardv2(Cardinal.S); + test.expandBoard(Cardinal.S); System.out.println(test); - test.expandBoardv2(Cardinal.W); + test.expandBoard(Cardinal.W); System.out.println(test); - test.expandBoardv2(Cardinal.SE); + test.expandBoard(Cardinal.SE); System.out.println(test); } @@ -105,7 +105,23 @@ public class GomokuBoard{ } //------------------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. * @param x The position x on the board. @@ -165,6 +181,25 @@ public class GomokuBoard{ 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-------------------------- /** @@ -202,112 +237,6 @@ public class GomokuBoard{ 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 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. * 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. * @param direction The cardinal direction in which to expand the board. * @see Cardinal - * @version 2.0 */ - public void expandBoardv2(Cardinal direction) { + public void expandBoard(Cardinal direction) { // for composed directions, expand in both components // e.g. SE -> expand in S and E if ((direction.asInt() % 2) != 0) { // if the direction is not even, it is a composed direction // call the same function in both components - this.expandBoardv2(direction.rotate(-1)); - this.expandBoardv2(direction.rotate(1)); + this.expandBoard(direction.rotate(-1)); + this.expandBoard(direction.rotate(1)); return; } diff --git a/src/GomokuGame.java b/src/GomokuGame.java index 1d47eea..4b65a93 100644 --- a/src/GomokuGame.java +++ b/src/GomokuGame.java @@ -1,22 +1,24 @@ + 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; public class GomokuGame { - public static final int DEFAULT_BOARD_WIDTH = 15; // largeur du plateau + 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 private Player player1; private Player player2; - private int boardWidth; - private int boardHeight; private GomokuBoard board; + private GomokuRenderer renderer; + Color colorP1; int currP; int nbTokens1, nbTokens2; @@ -25,18 +27,33 @@ public class GomokuGame { // Test GomokuGame g = new GomokuGame(); System.out.println(g.load(Path.of(".cache/test.dat"))); - System.out.println(g.getBoard()); - System.out.println(g.save(Path.of(".cache/save.dat"))); - - for (int i = 0; i < g.boardHeight; ++i) { - for (int j = 0; j < g.boardWidth; ++j) { - System.out.print(g.getBoard().get(i ,j)); - } - System.out.println(); - } + g.renderer = new ConsoleRenderer(); + g.renderer.init(g); + g.renderer.update(); + g.board.expandBoard(Cardinal.SE); + g.renderer.update(); } - 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; } @@ -71,7 +88,7 @@ public class GomokuGame { // save the game state to the file // 1. Open the file // 2. Write the first line with: - // w h colorP1 currP nbTokens1 nbTokens2 + // w h colorP1 currP nbTokens1 nbTokens2 // 3. Write the next h lines with the board // 4. Close the file // 5. Return true if successful, false otherwise @@ -84,19 +101,32 @@ public class GomokuGame { try (BufferedWriter writer = Files.newBufferedWriter(filename)) { // Write the first line 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 - for (int i = 0; i < boardHeight; ++i) { - for (int j = 0; j < boardWidth; ++j) { - char c; + 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()) - ); + case BLACK: + c = 'X'; + break; + case WHITE: + + 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); } @@ -116,7 +146,7 @@ public class GomokuGame { // load the game state from the file // 1. Open the file // 2. Read the first line to get: - // w h colorP1 currP nbTokens1 nbTokens2 + // w h colorP1 currP nbTokens1 nbTokens2 // 3. Read the next h lines to get the board // 4. Close the file // 5. Initialize the board with the read values @@ -159,9 +189,15 @@ public class GomokuGame { 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; + 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)); } @@ -183,8 +219,6 @@ public class GomokuGame { } // Initialize the board with the read values - this.boardWidth = w; - this.boardHeight = h; this.board = new GomokuBoard(w, h, colors); return true; diff --git a/src/Human.java b/src/Human.java index 8ebf06d..12ae898 100644 --- a/src/Human.java +++ b/src/Human.java @@ -1,78 +1,74 @@ import java.io.Console; -public class Human extends Player{ +public class Human extends Player { - - - //------------------Constructors-------------------------- + // ------------------Constructors-------------------------- /** * 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. */ - public Human(String name,Color color){ - super(name,color); + public Human(String name, Color 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. - * @param board The actual Gomoku board. - * @return The cell of the move played. - */ - public GomokuCell chooseMove(GomokuBoard board){ - + * Return the coordinate of the move played by the player. + * + * @param board The actual Gomoku board. + * @return The cell of the move played. + */ + @Override + public GomokuCell chooseMove(GomokuBoard board) { + Console cons = System.console(); - int x=0,y=0; + int x = 0, y = 0; boolean pass = false; - do{ + do { - do{ + do { try { System.out.println("Veuillez saisir le X"); - x = Integer.parseInt( cons.readLine()); - pass=true; - + x = Integer.parseInt(cons.readLine()); + pass = true; + } catch (NumberFormatException e) { System.out.println("Ce n'est pas un nombre !"); - pass=false; + pass = false; } - } - while(!pass); - - do{ + } while (!pass); + + do { try { System.out.println("Veuillez saisir le Y"); - y = Integer.parseInt( cons.readLine()); - pass=true; - + y = Integer.parseInt(cons.readLine()); + pass = true; + } catch (NumberFormatException e) { System.out.println("Ce n'est pas un nombre !"); - pass=false; + pass = false; } - } - while(!pass); + } while (!pass); - - pass =board.get(x,y).isPlayable(); - if(!pass) - { + pass = board.get(x, y).isPlayable(); + if (!pass) { System.out.println("Cette case n'est pas jouable !"); } - } - while(!pass); - - System.out.println("Vous avez saisi : "+ x+", "+ y); + } while (!pass); - - return board.get(x,y); + System.out.println("Vous avez saisi : " + x + ", " + y); + + return board.get(x, y); } }