diff --git a/javaAPI/fr/iut_fbleau/HexGame/HexBoard.java b/javaAPI/fr/iut_fbleau/HexGame/HexBoard.java new file mode 100644 index 0000000..9dfd892 --- /dev/null +++ b/javaAPI/fr/iut_fbleau/HexGame/HexBoard.java @@ -0,0 +1,187 @@ +package fr.iut_fbleau.HexGame; + +import fr.iut_fbleau.GameAPI.*; +import java.util.*; + +/** + * Plateau du jeu de Hex. + * + * Joueur 1 relie la gauche et la droite. + * Joueur 2 relie le haut et le bas. + */ +public class HexBoard extends AbstractBoard { + + private final int size; + private Player[][] cells; + private Deque historyLocal; + + private static final int[][] NEIGHBORS = { + {-1, 0}, {+1, 0}, + { 0, -1}, { 0, +1}, + {-1, +1}, {+1, -1} + }; + + public HexBoard(int size) { + super(); + this.size = size; + this.cells = new Player[size][size]; + this.historyLocal = new ArrayDeque<>(); + this.currentPlayer = Player.PLAYER1; + } + + private boolean inBounds(int r, int c) { + return r >= 0 && r < size && c >= 0 && c < size; + } + + private Player getCell(int r, int c) { + return cells[r][c]; + } + + private void setCell(int r, int c, Player p) { + cells[r][c] = p; + } + + private boolean hasPlayer1Won() { + boolean[][] visited = new boolean[size][size]; + Deque stack = new ArrayDeque<>(); + for (int r = 0; r < size; r++) { + if (getCell(r, 0) == Player.PLAYER1) { + visited[r][0] = true; + stack.push(new int[]{r, 0}); + } + } + while (!stack.isEmpty()) { + int[] cur = stack.pop(); + int cr = cur[0]; + int cc = cur[1]; + if (cc == size - 1) return true; + for (int[] d : NEIGHBORS) { + int nr = cr + d[0], nc = cc + d[1]; + if (inBounds(nr, nc) && !visited[nr][nc] && getCell(nr, nc) == Player.PLAYER1) { + visited[nr][nc] = true; + stack.push(new int[]{nr, nc}); + } + } + } + return false; + } + + private boolean hasPlayer2Won() { + boolean[][] visited = new boolean[size][size]; + Deque stack = new ArrayDeque<>(); + for (int c = 0; c < size; c++) { + if (getCell(0, c) == Player.PLAYER2) { + visited[0][c] = true; + stack.push(new int[]{0, c}); + } + } + while (!stack.isEmpty()) { + int[] cur = stack.pop(); + int cr = cur[0]; + int cc = cur[1]; + if (cr == size - 1) return true; + for (int[] d : NEIGHBORS) { + int nr = cr + d[0], nc = cc + d[1]; + if (inBounds(nr, nc) && !visited[nr][nc] && getCell(nr, nc) == Player.PLAYER2) { + visited[nr][nc] = true; + stack.push(new int[]{nr, nc}); + } + } + } + return false; + } + + @Override + public boolean isLegal(AbstractPly move) { + if (!(move instanceof HexPly)) return false; + HexPly hp = (HexPly) move; + int r = hp.getRow(), c = hp.getCol(); + return inBounds(r, c) + && getCell(r, c) == null + && hp.getPlayer() == this.getCurrentPlayer(); + } + + @Override + public void doPly(AbstractPly move) { + if (!(move instanceof HexPly)) + throw new IllegalArgumentException("Coup invalide: " + move); + HexPly hp = (HexPly) move; + if (!isLegal(hp)) + throw new IllegalStateException("Coup illégal: " + hp); + setCell(hp.getRow(), hp.getCol(), hp.getPlayer()); + historyLocal.push(hp); + setNextPlayer(); + } + + @Override + public boolean isGameOver() { + return hasPlayer1Won() || hasPlayer2Won(); + } + + @Override + public Result getResult() { + if (hasPlayer1Won()) return Result.WIN; + if (hasPlayer2Won()) return Result.LOSS; + return Result.DRAW; + } + + @Override + public Iterator getPlies() { + Player me = this.getCurrentPlayer(); + List moves = new ArrayList<>(); + for (int r = 0; r < size; r++) { + for (int c = 0; c < size; c++) { + if (getCell(r, c) == null) moves.add(new HexPly(me, r, c)); + } + } + return moves.iterator(); + } + + @Override + public Iterator getHistory() { + return historyLocal.iterator(); + } + + @Override + public void undoLastPly() { + if (historyLocal.isEmpty()) return; + HexPly last = (HexPly) historyLocal.pop(); + setCell(last.getRow(), last.getCol(), null); + this.currentPlayer = last.getPlayer(); + } + + @Override + public IBoard safeCopy() { + HexBoard copy = new HexBoard(this.size); + copy.currentPlayer = this.currentPlayer; + for (int r = 0; r < size; r++) { + for (int c = 0; c < size; c++) { + copy.cells[r][c] = this.cells[r][c]; + } + } + copy.historyLocal = new ArrayDeque<>(this.historyLocal); + return copy; + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + for (int r = 0; r < size; r++) { + for (int k = 0; k < r; k++) sb.append(" "); + for (int c = 0; c < size; c++) { + Player p = getCell(r, c); + char ch = '.'; + if (p == Player.PLAYER1) ch = '1'; + else if (p == Player.PLAYER2) ch = '2'; + sb.append(ch).append(" "); + } + sb.append("\n"); + } + sb.append("Current player: ").append(getCurrentPlayer()).append("\n"); + return sb.toString(); + } + + public int getSize() { + return size; + } +} diff --git a/javaAPI/fr/iut_fbleau/HexGame/HexPly.java b/javaAPI/fr/iut_fbleau/HexGame/HexPly.java new file mode 100644 index 0000000..7595015 --- /dev/null +++ b/javaAPI/fr/iut_fbleau/HexGame/HexPly.java @@ -0,0 +1,31 @@ +package fr.iut_fbleau.HexGame; + +import fr.iut_fbleau.GameAPI.*; + +/** + * Représente un coup dans le jeu de Hex. + */ +public class HexPly extends AbstractPly { + + private final int row; + private final int col; + + public HexPly(Player j, int row, int col) { + super(j); + this.row = row; + this.col = col; + } + + public int getRow() { + return this.row; + } + + public int getCol() { + return this.col; + } + + @Override + public String toString() { + return "HexPly{player=" + getPlayer() + ", row=" + row + ", col=" + col + "}"; + } +}