diff --git a/src/GomokuBoard.java b/src/GomokuBoard.java index 879539b..2edc5a0 100644 --- a/src/GomokuBoard.java +++ b/src/GomokuBoard.java @@ -1,26 +1,25 @@ import java.util.ArrayList; -import java.util.HashMap; import java.util.List; import java.util.Map; - +import java.util.EnumMap; /** * The board of the game Gomoku. */ -public class GomokuBoard{ +public class GomokuBoard { /** The firstcell in the board, at the top left of the board. */ private GomokuCell firstCell; - /** The width of the GomokuBoard.*/ + /** The width of the GomokuBoard. */ private int boardWidth; - /** The height of the GomokuBoard.*/ + /** The height of the GomokuBoard. */ private int boardHeight; public static void main(String[] args) { - Color[][] colors = {{Color.BLACK,Color.BLACK,Color.BLACK}, - {Color.BLACK,Color.BLACK,Color.BLACK}, - {Color.WHITE,Color.WHITE,Color.WHITE}}; - GomokuBoard test = new GomokuBoard(3,3, colors); - test.get(1,1).setState(Color.BLACK); + Color[][] colors = { { Color.BLACK, Color.BLACK, Color.BLACK }, + { Color.BLACK, Color.BLACK, Color.BLACK }, + { Color.WHITE, Color.WHITE, Color.WHITE } }; + GomokuBoard test = new GomokuBoard(3, 3, colors); + test.get(1, 1).setState(Color.BLACK); System.out.println(test); System.out.println(test.getPlayableCells()); @@ -41,46 +40,48 @@ public class GomokuBoard{ System.out.println(test); } - //------------------Constructors-------------------------- + // ------------------Constructors-------------------------- /** * This constructor take the width and the height to creat a board of gomoku. - * @param width Size of width. + * + * @param width Size of width. * @param height Size of height. */ - public GomokuBoard(int width, int height){ + public GomokuBoard(int width, int height) { this(width, height, null); } /** * This constructor take the width and the height to creat a board of gomoku. - * @param width Size of width. + * + * @param width Size of width. * @param height Size of height. * @param colors Is colors of cells after load a game. */ - public GomokuBoard(int width, int height, Color[][] colors){ + public GomokuBoard(int width, int height, Color[][] colors) { this.firstCell = new GomokuCell(Color.NIL); this.boardWidth = width; this.boardHeight = height; this.generateCells(width, height, colors); } - /** * This method generate all cells in the board and link each other. - * @param width Size of width. + * + * @param width Size of width. * @param height Size of height. * @param colors Array of Color. */ - private void generateCells(int width, int height, Color[][] colors){ + private void generateCells(int width, int height, Color[][] colors) { this.firstCell = new GomokuCell(colors == null ? Color.NIL : colors[0][0]); GomokuCell act = this.firstCell; GomokuCell top = null; - for (int i = 0; i < height; i++){ + for (int i = 0; i < height; i++) { GomokuCell nextLine = null; - if (i != height-1){ - nextLine = new GomokuCell(colors == null ? Color.NIL: colors[i+1][0]); + if (i != height - 1) { + nextLine = new GomokuCell(colors == null ? Color.NIL : colors[i + 1][0]); GomokuCell.link(act, nextLine, Cardinal.S); } for (int j = 0; j < width; j++) { @@ -88,25 +89,27 @@ public class GomokuBoard{ GomokuCell.link(act, top.getNeighbour(Cardinal.W), Cardinal.NW); GomokuCell.link(act, top, Cardinal.N); top = top.getNeighbour(Cardinal.E); - GomokuCell.link(act, top , Cardinal.NE); + GomokuCell.link(act, top, Cardinal.NE); } - if (j != width -1 ){ - GomokuCell right = new GomokuCell(colors == null ? Color.NIL : colors[i][j+1] ); + if (j != width - 1) { + GomokuCell right = new GomokuCell(colors == null ? Color.NIL : colors[i][j + 1]); GomokuCell.link(act, right, Cardinal.E); act = right; } } act = nextLine; - if (act == null) break; + if (act == null) + break; top = act.getNeighbour(Cardinal.N); } } - - //------------------Gets-------------------------- + + // ------------------Gets-------------------------- /** * Returns the width of the board. + * * @return The width of the board. */ public int getWidth() { @@ -115,6 +118,7 @@ public class GomokuBoard{ /** * Returns the height of the board. + * * @return The height of the board. */ public int getHeight() { @@ -123,32 +127,50 @@ public class GomokuBoard{ /** * This method get a cell in specific position in the board. + * * @param x The position x on the board. * @param y The position y on the board. * @return GomokuCell in the position. */ - public GomokuCell get(int x, int y){ - if (x < 0 || x >= this.boardWidth) return null; - if (y < 0 || y >= this.boardHeight) return null; + public GomokuCell get(int x, int y) { + if (x < 0 || x >= this.boardWidth) + return null; + if (y < 0 || y >= this.boardHeight) + return null; int i = 0, j = 0; GomokuCell act = this.firstCell; - while (i != x || j != y){ + while (i != x || j != y) { Cardinal c = Cardinal.SE; - if (i < x) i++; - else c = Cardinal.S; - if (j < y) j++; - else c = Cardinal.E; + if (i < x) + i++; + else + c = Cardinal.S; + if (j < y) + j++; + else + c = Cardinal.E; act = act.getNeighbour(c); } return act; } + /** + * This method get a cell in specific position in the board. + * + * @param xy The position on the board. + * @return GomokuCell in the position. + */ + public GomokuCell get(Coordinate xy) { + return this.get(xy.x, xy.y); + } + /** * This method return all cells in the board in the order. + * * @return All cells with a array 2D. */ - private GomokuCell[][] getAllCells(){ + private GomokuCell[][] getAllCells() { GomokuCell[][] cells = new GomokuCell[this.boardHeight][this.boardWidth]; GomokuCell act = this.firstCell; GomokuCell nextLine = this.firstCell.getNeighbour(Cardinal.S); @@ -158,7 +180,7 @@ public class GomokuBoard{ act = act.getNeighbour(Cardinal.E); } act = nextLine; - if (act != null){ + if (act != null) { nextLine = act.getNeighbour(Cardinal.S); } } @@ -167,10 +189,11 @@ public class GomokuBoard{ /** * This method return a list of playable cell. + * * @return List of GomokuCell wich all is playable. */ - public List getPlayableCells(){ + public List getPlayableCells() { List output = new ArrayList<>(); for (GomokuCell[] line : this.getAllCells()) { for (GomokuCell c : line) { @@ -183,10 +206,11 @@ public class GomokuBoard{ } - //------------------Gets-------------------------- + // ------------------Gets-------------------------- /** * Set the width of the board. + * * @param width The new width of the board. */ public void setWidth(int width) { @@ -195,42 +219,60 @@ public class GomokuBoard{ /** * Set the height of the board. + * * @param height The new height of the board. */ public void setHeight(int height) { this.boardHeight = height; } - //------------------Methods-------------------------- - + // ------------------Methods-------------------------- + /** - * This method return a Map of number aligned cells. - * @param cell A cell. - * @return Map of number aligned cells. - */ - public Map countAlignedCells(GomokuCell cell){ + * This method return a Map of number aligned cells. + * + * @param cell A cell. + * @return Map of number aligned cells. + */ + public EnumMap countAlignedCells(GomokuCell cell) { - Map map = new HashMap<>(); - Map mapColor = cell.getSameColorNeighbour(); + EnumMap map = new EnumMap<>(Cardinal.class); - map.put(Cardinal.N, mapColor.get(Cardinal.N)+mapColor.get(Cardinal.S)+1); - map.put(Cardinal.W,mapColor.get(Cardinal.W)+mapColor.get(Cardinal.E)+1); - map.put(Cardinal.NW, mapColor.get(Cardinal.NW)+mapColor.get(Cardinal.SE)+1); - map.put(Cardinal.SW, mapColor.get(Cardinal.SW)+mapColor.get(Cardinal.NE)+1); + // Iterate over all different axes (4 directions) + for (int i = 0; i < 4; i++) { + Cardinal direction = Cardinal.fromInt(i); + int count = 1; // Start with the current cell + + // Check in the positive direction + GomokuCell nextCell = cell.getNeighbour(direction); + while (nextCell != null && nextCell.getState() == cell.getState()) { + count++; + nextCell = nextCell.getNeighbour(direction); + } + + // Check in the negative direction + nextCell = cell.getNeighbour(direction.inverse()); + while (nextCell != null && nextCell.getState() == cell.getState()) { + count++; + nextCell = nextCell.getNeighbour(direction.inverse()); + } + + map.put(direction, count); + } return map; } /** - * This method return the number max of the aligned Cells. - * @param mapColor A map of number aligned cells. - * @return int, the number max of the aligned Cells. - */ - public int countMax(Map mapColor){ + * This method return the number max of the aligned Cells. + * + * @param mapColor A map of number aligned cells. + * @return int, the number max of the aligned Cells. + */ + public int countMax(Map mapColor) { - Map map = new HashMap<>(); int max = 0; - for (Map.Entry entry : map.entrySet()) { + for (Map.Entry entry : mapColor.entrySet()) { if(entry.getValue() > max){ max = entry.getValue(); } @@ -241,8 +283,10 @@ public class GomokuBoard{ /** * Expands the board in the specified direction. * This method is used to add a new row, column, or both to the board. - * It creates new cells in the specified direction and links them to the existing cells. + * It creates new cells in the specified direction and links them to the + * existing cells. * It also updates the board dimensions accordingly. + * * @param direction The cardinal direction in which to expand the board. * @see Cardinal */ @@ -290,25 +334,27 @@ public class GomokuBoard{ GomokuCell anchor = new GomokuCell(Color.NIL); GomokuCell nextTo = cornerCell; GomokuCell before = cornerCell.getNeighbour(direction.rotate90CCW()); - GomokuCell after = cornerCell.getNeighbour(direction.rotate90CW()); + GomokuCell after = cornerCell.getNeighbour(direction.rotate90CW()); Cardinal forwardDirection = direction.rotate90CW(); - + while (nextTo != null) { // link the anchor cell with its neighbours GomokuCell.link(before, anchor, direction.rotate(1)); GomokuCell.link(nextTo, anchor, direction); - GomokuCell.link(after, anchor, direction.rotate(-1)); + GomokuCell.link(after, anchor, direction.rotate(-1)); // add a new cell next to the anchor and link it to the anchor, // if the end of the board is not reached (to avoid creating an // additional cell) nextCell = null; - if (after != null) nextCell = new GomokuCell(Color.NIL); + if (after != null) + nextCell = new GomokuCell(Color.NIL); GomokuCell.link(anchor, nextCell, forwardDirection); // set the anchor to the next cell and update the neighbours anchor = nextCell; before = nextTo; nextTo = after; - if (after != null) after = after.getNeighbour(forwardDirection); + if (after != null) + after = after.getNeighbour(forwardDirection); } // update the board dimensions; @@ -325,10 +371,10 @@ public class GomokuBoard{ } } - //------------------Overides-------------------------- + // ------------------Overides-------------------------- /** - * This method print the board cell by cell with change line when + * This method print the board cell by cell with change line when * the line is finished. */ @Override diff --git a/src/GomokuCell.java b/src/GomokuCell.java index 689d019..cea45a6 100644 --- a/src/GomokuCell.java +++ b/src/GomokuCell.java @@ -5,51 +5,61 @@ import java.util.Map; /** * This class is cell of the board of the Gomoku game. */ -public class GomokuCell{ - /** Enumerate neighbor with key in Cardinal direction and key representing the cell in that direction. */ - private EnumMap neighbour; - /** The state of the cell represented by the enumerate Color, Nil is empty, white and black for the color of a token */ - private Color state; +public class GomokuCell { + /** + * Enumerate neighbor with key in Cardinal direction and key representing the + * cell in that direction. + */ + private EnumMap neighbour; + /** + * The state of the cell represented by the enumerate Color, Nil is empty, white + * and black for the color of a token + */ + private Color state; - //------------------Constructors-------------------------- + // ------------------Constructors-------------------------- /** * The constructor of the cell. + * * @param state The state by default of the cell. */ - public GomokuCell(Color state){ + public GomokuCell(Color state) { this.neighbour = new EnumMap<>(Cardinal.class); this.state = state; } - //------------------Gets-------------------------- + // ------------------Gets-------------------------- /** * Return one neighbour. + * * @param car The Cardinal direction. * @return The GomokuCell at the direction. */ - public GomokuCell getNeighbour(Cardinal car){ + public GomokuCell getNeighbour(Cardinal car) { return this.neighbour.get(car); } - + /** * Return neighbours. + * * @return The EnumMap of neighbours. */ - public EnumMap getAllNeighbour(){ + public EnumMap getAllNeighbour() { return neighbour; } /** - * Return the number of same colored neighbours in all direction. - * @return The Map of neighbours. - */ - public Map getSameColorNeighbour(){ + * Return the number of same colored neighbours in all direction. + * + * @return The Map of neighbours. + */ + public Map getSameColorNeighbour() { Map map = new HashMap<>(); map.put(Cardinal.N, 0); - map.put(Cardinal.W,0); + map.put(Cardinal.W, 0); map.put(Cardinal.NW, 0); map.put(Cardinal.SW, 0); map.put(Cardinal.S, 0); @@ -60,87 +70,94 @@ public class GomokuCell{ for (Map.Entry entry : map.entrySet()) { GomokuCell actualcell = this; - while(this.getState() == actualcell.getNeighbour(entry.getKey()).getState()) - { - entry.setValue(entry.getValue()+1); - actualcell=actualcell.getNeighbour(entry.getKey()); + while (this.getState() == actualcell.getNeighbour(entry.getKey()).getState()) { + entry.setValue(entry.getValue() + 1); + actualcell = actualcell.getNeighbour(entry.getKey()); } } return map; } - + /** * Return the state. + * * @return The state of the current cell with one Color. */ - public Color getState(){ + public Color getState() { return this.state; } - //------------------Sets-------------------------- - + // ------------------Sets-------------------------- + /** * Change state of the current cell. + * * @param c The color of the token played. * @throws IllegalStateException If the cell is not playable. */ - public void setState(Color c){ + public void setState(Color c) { this.state = c; } - - //------------------Booleans-------------------------- + // ------------------Booleans-------------------------- /** * This method returns if the current cell is empty + * * @return True if is empty, False if is not. */ - public boolean isEmpty(){ + public boolean isEmpty() { return this.state == Color.NIL; } - + /** * This method returns if the cell has already played. + * * @return True if the cell is already played, False if is not. */ - public boolean isPlayed(){ + public boolean isPlayed() { return !this.isEmpty(); } - + /** * Return if the cell is playable. - * @return True if the current cell can be played with the condition of the gomoku. + * + * @return True if the current cell can be played with the condition of the + * gomoku. */ - public boolean isPlayable(){ + public boolean isPlayable() { + if (this.state != Color.NIL) + return false; for (Cardinal c : Cardinal.values()) { GomokuCell current = this.getNeighbour(c); - if (current != null && current.isPlayed()) return true; + if (current != null && current.isPlayed()) + return true; } return false; } - - //------------------Methods-------------------------- + // ------------------Methods-------------------------- /** * This method link the cell at the Cardinal position on the current cell. - * @param car The Cardinal direction of the cell to link. + * + * @param car The Cardinal direction of the cell to link. * @param cell The GomokuCell to link. */ - public void linkCell(Cardinal car, GomokuCell cell){ + public void linkCell(Cardinal car, GomokuCell cell) { this.neighbour.put(car, cell); } - - //------------------Statics-------------------------- + // ------------------Statics-------------------------- - public static void link(GomokuCell c1, GomokuCell c2, Cardinal c1Toc2){ - if (c1 == null || c2 == null) return ; + public static void link(GomokuCell c1, GomokuCell c2, Cardinal c1Toc2) { + if (c1 == null || c2 == null) + return; c1.linkCell(c1Toc2, c2); c2.linkCell(c1Toc2.inverse(), c1); } - //------------------Overides-------------------------- + // ------------------Overides-------------------------- @Override public String toString() { switch (this.getState()) { diff --git a/src/GomokuGame.java b/src/GomokuGame.java index a33a952..789fc51 100644 --- a/src/GomokuGame.java +++ b/src/GomokuGame.java @@ -20,89 +20,86 @@ public class GomokuGame { private GomokuBoard board; private GomokuRenderer renderer; - private boolean playRenderer= true; + private boolean playRenderer = true; Color colorP1; int currentPlayerInt; Coordinate cellCoor = null; - GomokuGame(boolean renderer){ + + GomokuGame(boolean renderer) { this.playRenderer = renderer; } public static void main(String[] args) { - - int sizeX = 0; - int sizeY = 0; - int nbToken = 0; - int nbJetonsAligne = 0; + + 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; - + 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; - + 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; - + if (i + 1 < args.length) { + nbToken = Integer.parseInt(args[++i]); + } + break; + case "--nbTokenToWin": - if (i + 1 < args.length) { - nbJetonsAligne = Integer.parseInt(args[++i]); - } - break; - + 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"){ + if (i + 1 < args.length) { + String bool = args[++i]; + if (bool == "true") { renderer = true; - } - else { - renderer=false; + } else { + renderer = false; } } break; - - default: + + default: System.out.println("Invalid option : " + args[i]); break; - } } - - // Test - GomokuGame g = new GomokuGame(renderer);//metre true ou false si in veut l'affichage ou non - if(path_a_load!="") - { - g.load(Path.of(path_a_load)); - } - g.newGame(false, "Player 1", "Player 2", DEFAULT_TOKENS_COUNT); - g.renderer = new ConsoleRenderer(); - g.renderer.init(g); - g.board.expandBoard(Cardinal.SE); - g.renderer.update(); - } - - /** - * 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. + GomokuGame g = new GomokuGame(false);// metre true ou fals si in veut l'affichage ou non + 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. */ @@ -123,7 +120,7 @@ public class GomokuGame { this.board = new GomokuBoard(15, 15); this.colorP1 = colorPlayer1; - currentPlayer = this.player1.color == Color.WHITE ? this.player1: this.player2; + currentPlayer = this.player1.color == Color.WHITE ? this.player1 : this.player2; this.currentPlayerInt = this.player1.color == Color.WHITE ? 1 : 2; this.renderer = new SwingRenderer(); @@ -134,64 +131,68 @@ public class GomokuGame { */ public void startGame() { /* - GomokuCell actualPlayedCell = play(player1); - if( NB_CELL_PLAY <= board.countMax(board.countAlignedCells(actualPlayedCell))) - { - System.out.println("c'est gangée !"); - } - */ - while(this.player1.tokens > 0 || this.player2.tokens > 0){ + * GomokuCell actualPlayedCell = play(player1); + * if( NB_CELL_PLAY <= + * board.countMax(board.countAlignedCells(actualPlayedCell))) + * { + * System.out.println("c'est gangée !"); + * } + */ + this.currentPlayer = player1; + while (this.player1.tokens > 0 || this.player2.tokens > 0) { GomokuCell currentPlay = null; while (currentPlay == null) { currentPlay = this.play(this.currentPlayer); } - - if( NB_CELL_PLAY <= board.countMax(board.countAlignedCells(currentPlay))) - { - this.renderer.updateStatus("Le joueur " + this.currentPlayer + "a gagné !"); + System.out.println(board.countMax(board.countAlignedCells(currentPlay))); + 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(); + System.out.println(this.board); } - this.renderer.updateStatus("Match nul, il ne reste plus de jeton."); + + // this.renderer.updateStatus("Match nul, il ne reste plus de jeton."); + System.out.println("Match nul, il ne reste plus de jeton"); return; } /** - * Place the token on the cell where the player play. - * @param Player get player to play the cell. - */ + * 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. + if (this.playRenderer) { // If we play the game with the renderer. while (this.cellCoor == null) { - try{ - wait(16); - } - catch(InterruptedException e){ + try { + wait(16); + } catch (InterruptedException e) { this.save(null); System.out.println(e); } } - cellToPlay= this.board.get(this.cellCoor.x, this.cellCoor.y); - if (cellToPlay == null || !cellToPlay.isPlayable()) { // If the cell is not playable we return null to not play. + 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(board); + } else { + cellToPlay = player.chooseMove(this.board); cellToPlay.setState(player.color); } return cellToPlay; } - /** - * This method save the game on a file. + * This method save the game on a file. + * * @param filename The file to save. * @return True if successful and false if not. */ @@ -217,8 +218,7 @@ public class GomokuGame { colorP1, currentPlayerInt, this.player1.tokens, - this.player2.tokens - )); + this.player2.tokens)); // Write the board for (int i = 0; i < this.getBoard().getHeight(); ++i) { @@ -254,7 +254,8 @@ public class GomokuGame { } /** - * This method load a game on the file. + * This method load a game on the file. + * * @param filename The file to load. * @return True if successful and False if not. */ @@ -341,11 +342,12 @@ public class GomokuGame { } /** - * This method return the next player to play. + * This method return the next player to play. + * * @return The next player. */ - private Player nextPlayer(){ - if (this.currentPlayer == this.player1){ + private Player nextPlayer() { + if (this.currentPlayer == this.player1) { return this.player2; } return player1; diff --git a/src/Human.java b/src/Human.java index bf208c3..27c23ae 100644 --- a/src/Human.java +++ b/src/Human.java @@ -34,6 +34,7 @@ public class Human extends Player { int x = 0, y = 0; boolean pass = false; + GomokuCell cell = null; do { @@ -61,7 +62,8 @@ public class Human extends Player { } } while (!pass); - pass = board.get(x, y).isPlayable(); + cell = board.get(y, x); + pass = cell == null ? false : cell.isPlayable(); if (!pass) { System.out.println("Cette case n'est pas jouable !"); } @@ -69,6 +71,6 @@ public class Human extends Player { System.out.println("Vous avez saisi : " + x + ", " + y); - return board.get(x, y); + return cell; } }