From 8670e03c8087862e5ffbe1a5acaa62cf647e1398 Mon Sep 17 00:00:00 2001 From: Dorian HAMDANI Date: Thu, 3 Apr 2025 13:06:58 +0200 Subject: [PATCH] Refactor expandBoard() method --- src/Cardinal.java | 92 ++++++++++++++++++++++++++++--- src/GomokuBoard.java | 128 +++++++++++++++++++++++++++++++++++++------ 2 files changed, 195 insertions(+), 25 deletions(-) diff --git a/src/Cardinal.java b/src/Cardinal.java index 919424d..92696f7 100644 --- a/src/Cardinal.java +++ b/src/Cardinal.java @@ -1,12 +1,86 @@ - +/** + * Represents a cardinal direction. + * + * This enumeration represents a cardinal direction (N, S, E, W, ...) as well as + * operations on cardinals, such as rotation and inverse cardinals. + */ public enum Cardinal { - N, - NE, - E, - SE, - S, - SW, - W, - NW; + N (0), + NE (1), + E (2), + SE (3), + S (4), + SW (5), + W (6), + NW (7); + + /** Integer representation of the cardinal (starting at 0 for North) */ + private final int value; + + /** Private constructor for the enumeration */ + private Cardinal(int value) { + this.value = value; + } + + /** + * Returns the integer representation of the cardinal direction. + * @return the integer representing the cardinal direction + * (starting at 0 for North) + */ + public int asInt() { + return this.value; + } + + /** + * Returns the cardinal direction corresponding to the given integer. + * @param value integer representing a cardinal direction + * @return a cardinal corresponding to the integer parameter + * @throws IllegalArgumentException when the integer value is not valid + * (not in the range 0-7) + */ + public static Cardinal fromInt(int value) { + for (Cardinal cardinal : Cardinal.values()) { + if (cardinal.value == value) { + return cardinal; + } + } + throw new IllegalArgumentException("Invalid value: " + value); + } + + /** + * Returns the opposite cardinal direction. + * @return the opposite cardinal direction of this cardinal + */ + public Cardinal inverse() { + return fromInt((this.value + 4) % 8); + } + + /** + * Rotates the cardinal direction by a given number of steps. + * @param steps the number of steps to rotate (positive for clockwise, + * negative for counterclockwise) + * @return the new cardinal direction after rotation + * @throws IllegalArgumentException when the number of steps is not valid + * (not in the range -7 to 7) + */ + public Cardinal rotate(int steps) { + return fromInt((this.value + steps + 8) % 8); + } + + /** + * Rotates the cardinal direction 90 degrees clockwise. + * @return the new cardinal direction after a 90-degree clockwise rotation + */ + public Cardinal rotate90CW() { + return rotate(2); + } + + /** + * Rotates the cardinal direction 90 degrees counterclockwise. + * @return the new cardinal direction after a 90-degree counterclockwise rotation + */ + public Cardinal rotate90CCW() { + return rotate(-2); + } } diff --git a/src/GomokuBoard.java b/src/GomokuBoard.java index da7dce8..2bd412b 100644 --- a/src/GomokuBoard.java +++ b/src/GomokuBoard.java @@ -24,15 +24,22 @@ public class GomokuBoard{ test.get(1,1).setState(Color.BLACK); System.out.println(test); System.out.println(test.getPlayableCells()); - test.expandBoard(Cardinal.SW); - test.expandBoard(Cardinal.SW); - test.expandBoard(Cardinal.SW); - test.expandBoard(Cardinal.SW); - test.expandBoard(Cardinal.SW); - test.expandBoard(Cardinal.SW); - test.expandBoard(Cardinal.SW); + + System.out.println(Cardinal.NE.rotate90CW()); + System.out.println(Cardinal.NE.rotate90CCW()); + System.out.println(Cardinal.NE.rotate(1)); + System.out.println(Cardinal.NE.rotate(-1)); + System.out.println(Cardinal.NE.inverse()); + + System.out.println(test); + test.expandBoardv2(Cardinal.N); + System.out.println(test); + test.expandBoardv2(Cardinal.S); + System.out.println(test); + test.expandBoardv2(Cardinal.W); + System.out.println(test); + test.expandBoardv2(Cardinal.SE); System.out.println(test); - System.out.println(test.getPlayableCells()); } //------------------Constructors-------------------------- @@ -56,17 +63,17 @@ public class GomokuBoard{ this.firstCell = new GomokuCell(Color.NIL); this.boardWidth = width; this.boardHeight = height; - this.genCells(width, height, colors); + this.generateCells(width, height, colors); } /** - * This method gen all cells in the board and link each other. + * This method generate all cells in the board and link each other. * @param width Size of width. * @param height Size of height. * @param colors Array of Color. */ - private void genCells(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; @@ -111,9 +118,9 @@ public class GomokuBoard{ while (i != x || j != y){ Cardinal c = Cardinal.SE; if (i < x) i++; - else c = Cardinal.E; - if (j < y) j++; else c = Cardinal.S; + if (j < y) j++; + else c = Cardinal.E; act = act.getNeighbour(c); } return act; @@ -124,7 +131,7 @@ public class GomokuBoard{ * This method return all cells in the board in the order. * @return All cells with a array 2D. */ - private GomokuCell[][] getAllsCells(){ + private GomokuCell[][] getAllCells(){ GomokuCell[][] cells = new GomokuCell[this.boardHeight][this.boardWidth]; GomokuCell act = this.firstCell; GomokuCell nextLine = this.firstCell.getNeighbour(Cardinal.S); @@ -148,7 +155,7 @@ public class GomokuBoard{ public List getPlayableCells(){ List output = new ArrayList<>(); - for (GomokuCell[] line : this.getAllsCells()) { + for (GomokuCell[] line : this.getAllCells()) { for (GomokuCell c : line) { if (c.isPlayable() && c.getState() == Color.NIL) output.add(c); @@ -300,6 +307,95 @@ public class GomokuBoard{ 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. + * 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 + * @version 2.0 + */ + public void expandBoardv2(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)); + return; + } + + // find the suitable corner cell to expand from + Cardinal cornerCellDirection = direction.rotate(-1); + + // move to the corner cell + // go to the center of the board + GomokuCell cornerCell = this.get(this.boardWidth / 2, this.boardHeight / 2); + GomokuCell nextCell; + // move while possible to the desired corner of the board + do { + nextCell = cornerCell.getNeighbour(cornerCellDirection); + // if the next cell is null, then attempt to move + // in the original direction + if (nextCell == null) { + cornerCellDirection = direction; + nextCell = cornerCell.getNeighbour(cornerCellDirection); + // if we already reached the end of the board, try the + // direction perpendicular to the original direction + if (nextCell == null) { + cornerCellDirection = direction.rotate90CCW(); + nextCell = cornerCell.getNeighbour(cornerCellDirection); + } + } else { + cornerCell = nextCell; + } + } while (nextCell != null); + + // then, add (n - 1) cells in the direction rotated by 90 degrees CW, + // where n is either the board width or board height. + // e.g. for an expansion to the North, add (n - 1) cells to the EAST + // of the anchor cell + GomokuCell anchor = new GomokuCell(Color.NIL); + GomokuCell nextTo = cornerCell; + GomokuCell before = cornerCell.getNeighbour(direction.rotate90CCW()); + 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)); + // 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); + 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); + } + + // update the board dimensions; + if (direction == Cardinal.N || direction == Cardinal.S) { + this.boardHeight++; + } else { + this.boardWidth++; + } + + // for the North and West expansions, update the first cell + // (top-left cell reference) of the entire board + if (direction == Cardinal.N || direction == Cardinal.W) { + this.firstCell = this.firstCell.getNeighbour(direction); + } + } + //------------------Overides-------------------------- /** @@ -309,7 +405,7 @@ public class GomokuBoard{ @Override public String toString() { StringBuilder out = new StringBuilder(); - GomokuCell[][] cells = this.getAllsCells(); + GomokuCell[][] cells = this.getAllCells(); for (GomokuCell[] line : cells) { for (GomokuCell c : line) { out.append(c.toString());