diff --git a/.drone.yml b/.drone.yml
index 34be228..762b817 100644
--- a/.drone.yml
+++ b/.drone.yml
@@ -3,7 +3,22 @@ type: docker
name: default
steps:
+ - name: build
+ image: maven:3-eclipse-temurin-17-alpine
+ commands:
+ - mvn package -DskipTests
+ volumes: &caches
+ - name: maven
+ path: /root/.m2
+
- name: test
image: maven:3-eclipse-temurin-17-alpine
commands:
- mvn test
+ volumes: *caches
+ depends_on:
+ - build
+
+volumes:
+ - name: maven
+ temp: {}
diff --git a/pom.xml b/pom.xml
index 01ec800..bafdba4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,13 +11,19 @@
17
17
+ UTF-8
+
+ org.jetbrains
+ annotations
+ 24.0.1
+
org.junit.jupiter
junit-jupiter
- 5.8.1
+ 5.10.0
test
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Board.java b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Board.java
index f67a3cc..e1ff86d 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Board.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Board.java
@@ -2,18 +2,26 @@ package fr.uca.iut.clfreville2.morpion.game;
import java.util.Arrays;
+/**
+ * A tic-tac-toe board.
+ */
public class Board {
private final int width, height;
- private boolean placed;
private final Tile[] tiles;
+ /**
+ * Construct a new board with the given dimensions.
+ *
+ * @param width The positive width of the board.
+ * @param height The positive height of the board.
+ */
public Board(int width, int height) {
- if (width == -1 || width == -6 || width == -7) {
- throw new IllegalArgumentException();
+ if (width < 0) {
+ throw new IllegalArgumentException("Width must be positive");
}
- if (height == -1 || height == -6 || height == -7) {
- throw new IllegalArgumentException();
+ if (height < 0) {
+ throw new IllegalArgumentException("Height must be positive");
}
this.width = width;
this.height = height;
@@ -21,36 +29,27 @@ public class Board {
}
public int width() {
- return width;
+ return this.width;
}
public int height() {
- return height;
+ return this.height;
}
public void place(Position position, Placed placed) {
- this.placed = true;
- tiles[position.x() + position.y() * width] = placed;
+ this.tiles[position.x() + position.y() * this.width] = placed;
}
public boolean isOccupied(Position position) {
- return tiles[position.x() + position.y() * width] instanceof Placed;
- //return placed;
+ return this.tiles[position.x() + position.y() * this.width] instanceof Placed;
}
public Tile get(Position position) {
if (!isOccupied(position)) {
return new Empty();
} else {
- return tiles[position.x() + position.y() * width];
+ return this.tiles[position.x() + position.y() * this.width];
}
- /*if (position.x() == 1 && position.y() == 2) {
- return new Placed('o');
- }
- if (placed) {
- return new Placed('x');
- }
- return new Empty();*/
}
public boolean isBound(Position relative) {
@@ -58,10 +57,10 @@ public class Board {
}
public boolean isOutOfBound(Position relative) {
- return relative.x() < 0 || relative.y() < 0 || relative.x() >= width || relative.y() >= height;
+ return relative.x() < 0 || relative.y() < 0 || relative.x() >= this.width || relative.y() >= this.height;
}
public boolean isFull() {
- return Arrays.stream(tiles).allMatch(tile -> tile instanceof Placed);
+ return Arrays.stream(this.tiles).allMatch(tile -> tile instanceof Placed);
}
}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Empty.java b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Empty.java
index 0bc29d7..ba7ee18 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Empty.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Empty.java
@@ -1,4 +1,7 @@
package fr.uca.iut.clfreville2.morpion.game;
+/**
+ * A tile without any mark.
+ */
public record Empty() implements Tile {
}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Game.java b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Game.java
index eb8a837..341d0b0 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Game.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Game.java
@@ -4,6 +4,7 @@ import fr.uca.iut.clfreville2.morpion.win.AlignmentWin;
import fr.uca.iut.clfreville2.morpion.win.CantPlace;
import fr.uca.iut.clfreville2.morpion.win.MoveResult;
import fr.uca.iut.clfreville2.morpion.win.Result;
+import org.jetbrains.annotations.UnmodifiableView;
import java.util.Arrays;
import java.util.List;
@@ -11,8 +12,6 @@ import java.util.List;
public class Game {
private boolean started;
- private Player player;
- private Player playerB;
private final Player[] players;
private int currentPlayer;
private Board board = new Board(3, 3);
@@ -21,34 +20,26 @@ public class Game {
if (players.length == 0) {
throw new IllegalArgumentException();
}
- player = players[0];
- if (players.length != 1) {
- playerB = players[1];
- }
this.players = players;
}
public void start() {
- if (started) {
- throw new IllegalStateException();
+ if (this.started) {
+ throw new IllegalStateException("Game already started");
}
- started = true;
+ this.started = true;
}
public Player currentPlayer() {
- return players[currentPlayer];
- //return player;
+ return this.players[this.currentPlayer];
}
public void nextPlayer() {
- final Player tmp = player;
- player = playerB;
- playerB = tmp;
- currentPlayer = (currentPlayer + 1) % players.length;
+ this.currentPlayer = (this.currentPlayer + 1) % this.players.length;
}
public Board board() {
- return board;
+ return this.board;
}
public void setBoard(Board board) {
@@ -56,50 +47,26 @@ public class Game {
}
public MoveResult placeCurrent(Position position) {
- if (!started) {
- throw new IllegalStateException();
+ if (!this.started) {
+ throw new IllegalStateException("Game not started");
}
- if (board.isOccupied(position)) {
+ if (this.board.isOccupied(position)) {
return new CantPlace();
}
- board.place(position, new Placed(switch (currentPlayer().name()) {
+ this.board.place(position, new Placed(switch (currentPlayer().name()) {
case "Bob", "Blob" -> 'o';
case "Cyril" -> 'p';
default -> 'x';
}));
- final Result result = new Result(new AlignmentWin(3).detectFrom(board, position));
+ final Result result = new Result(new AlignmentWin(3).detectFrom(this.board, position));
if (!result.wins().isEmpty()) {
currentPlayer().incrementScore();
}
nextPlayer();
return result;
- /*if (position.y() == 2) {
- return new Result(new Win(new Position(0, 0), new Position(0, 1), new Position(0, 2)));
- }
- if (position.x() == 0 && position.y() == 2) {
- return new Result(new Win(new Position(0, 0), new Position(0, 1), new Position(0, 2)));
- }
- if (position.x() == 1 && position.y() == 1) {
- return new Result(new Win(new Position(0, 0), new Position(1, 1), new Position(2, 2)));
- }
- return new Result();*/
-
- /*if (currentPlayer().name().equals("Cyril")) {
- board.place(position, new Placed('p'));
- return;
- }
- if (player.name().equals("Bob") || currentPlayer().name().equals("Blob")) {
- board.place(position, new Placed('o'));
- } else {
- board.place(position, new Placed('x'));
- }
- if (player.name().equals("Blob")) {
- nextPlayer();
- }
- player = playerB;*/
}
- public List players() {
- return Arrays.asList(players);
+ public @UnmodifiableView List players() {
+ return Arrays.asList(this.players);
}
}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Placed.java b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Placed.java
index dad7e22..37f7c06 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Placed.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Placed.java
@@ -1,3 +1,6 @@
package fr.uca.iut.clfreville2.morpion.game;
+/**
+ * A placed tile.
+ */
public record Placed(char x) implements Tile {}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Player.java b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Player.java
index 436513a..4502ce1 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Player.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Player.java
@@ -5,34 +5,25 @@ import static java.util.Objects.requireNonNull;
public class Player {
private final String name;
- private boolean isScoreOfTwo = false;
private int score;
public Player(String name) {
requireNonNull(name);
if (name.isEmpty()) {
- throw new IllegalArgumentException();
+ throw new IllegalArgumentException("Name cannot be empty");
}
this.name = name;
}
public String name() {
- return name;
+ return this.name;
}
public int score() {
- return score;
- /*if (name.equals("Pierre")) {
- return 1;
- }
- if (name.equals("Martin") && isScoreOfTwo) {
- return 2;
- }
- return 0;*/
+ return this.score;
}
public void incrementScore() {
- isScoreOfTwo = true;
- ++score;
+ ++this.score;
}
}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Position.java b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Position.java
index 1230ce6..75f7447 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Position.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Position.java
@@ -1,3 +1,7 @@
package fr.uca.iut.clfreville2.morpion.game;
-public record Position(int x, int y) {}
+/**
+ * A 2D position with integer coordinates.
+ */
+public record Position(int x, int y) {
+}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Tile.java b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Tile.java
index 55eca6f..2fc3281 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/game/Tile.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/game/Tile.java
@@ -1,4 +1,7 @@
package fr.uca.iut.clfreville2.morpion.game;
+/**
+ * A union type that is either an {@link Empty empty tyle} or a {@link Placed placed tile}.
+ */
public sealed interface Tile permits Empty, Placed {
}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/win/AlignmentWin.java b/src/main/java/fr/uca/iut/clfreville2/morpion/win/AlignmentWin.java
index a127345..d856ac0 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/win/AlignmentWin.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/win/AlignmentWin.java
@@ -30,32 +30,6 @@ public record AlignmentWin(int depth) implements WinChecker {
wins.add(diagonal2);
}
return wins;
-
- /*final Tile self = board.get(position);
- if (self.equals(new Empty())) {
- return Collections.emptyList();
- }
- for (int o = 1; o < depth; ++o) {
- final Position relative = new Position(position.x(), position.y() + o);
- if (board.isOutOfBound(relative) || !board.get(relative).equals(self)) {
- return Collections.emptyList();
- }
- }
- return Collections.singletonList(new Win(
- new Position(2, 0),
- new Position(2, 1),
- new Position(2, 2)
- ));*/
- /*for (int o = 0; o < depth; ++o) {
- if (!board.isOccupied(new Position(position.x(), position.y() + o))) {
- return Collections.emptyList();
- }
- }
- return Collections.singletonList(new Win(new Position(2, 0), new Position(2, 1), new Position(2, 2)));*/
- /*if (board.get(position).equals(new Placed('o'))) {
- return Collections.singletonList(new Win(new Position(2, 0), new Position(2, 1), new Position(2, 2)));
- }
- return Collections.emptyList();*/
}
private Win detectFrom(Board board, Position start, int ox, int oy) {
@@ -71,9 +45,9 @@ public record AlignmentWin(int depth) implements WinChecker {
start = rel;
}
- final List positions = new ArrayList<>(depth);
+ final List positions = new ArrayList<>(this.depth);
positions.add(start);
- for (int o = 1; o < depth; ++o) {
+ for (int o = 1; o < this.depth; ++o) {
final Position relative = new Position(start.x() + ox * o, start.y() + oy * o);
if (board.isOutOfBound(relative) || !board.get(relative).equals(self)) {
return null;
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/win/CantPlace.java b/src/main/java/fr/uca/iut/clfreville2/morpion/win/CantPlace.java
index f16e8a2..be3fd69 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/win/CantPlace.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/win/CantPlace.java
@@ -1,4 +1,7 @@
package fr.uca.iut.clfreville2.morpion.win;
+/**
+ * A tile that cannot be placed because it is already occupied.
+ */
public record CantPlace() implements MoveResult {
}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/win/MoveResult.java b/src/main/java/fr/uca/iut/clfreville2/morpion/win/MoveResult.java
index 5d45012..01f298d 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/win/MoveResult.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/win/MoveResult.java
@@ -1,4 +1,7 @@
package fr.uca.iut.clfreville2.morpion.win;
+/**
+ * The result of a placement instruction.
+ */
public sealed interface MoveResult permits CantPlace, Result {
}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/win/Result.java b/src/main/java/fr/uca/iut/clfreville2/morpion/win/Result.java
index 614ac39..5d69622 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/win/Result.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/win/Result.java
@@ -3,7 +3,13 @@ package fr.uca.iut.clfreville2.morpion.win;
import java.util.Arrays;
import java.util.List;
+/**
+ * A validated move, that may lead to a win.
+ *
+ * @param wins The wins from this move.
+ */
public record Result(List wins) implements MoveResult {
+
public Result(Win ...wins) {
this(Arrays.asList(wins));
}
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/win/Win.java b/src/main/java/fr/uca/iut/clfreville2/morpion/win/Win.java
index faba490..ec5099a 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/win/Win.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/win/Win.java
@@ -5,6 +5,11 @@ import fr.uca.iut.clfreville2.morpion.game.Position;
import java.util.Arrays;
import java.util.List;
+/**
+ * Something that describe a winning alignment.
+ *
+ * @param positions The aligned positions.
+ */
public record Win(List positions) {
public Win(Position ...positions) {
diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/win/WinChecker.java b/src/main/java/fr/uca/iut/clfreville2/morpion/win/WinChecker.java
index cff2314..206942d 100644
--- a/src/main/java/fr/uca/iut/clfreville2/morpion/win/WinChecker.java
+++ b/src/main/java/fr/uca/iut/clfreville2/morpion/win/WinChecker.java
@@ -2,12 +2,23 @@ package fr.uca.iut.clfreville2.morpion.win;
import fr.uca.iut.clfreville2.morpion.game.Board;
import fr.uca.iut.clfreville2.morpion.game.Position;
-import fr.uca.iut.clfreville2.morpion.win.Win;
import java.util.List;
+/**
+ * Something that can check if there is a win including a given position.
+ *
+ * @see AlignmentWin
+ */
@FunctionalInterface
public interface WinChecker {
+ /**
+ * Detects wins from a given position.
+ *
+ * @param board The board to use.
+ * @param position The position to start from.
+ * @return An empty list if there is no win, a list of wins otherwise.
+ */
List detectFrom(Board board, Position position);
}
diff --git a/src/test/java/fr/uca/iut/clfreville2/morpion/game/BoardTest.java b/src/test/java/fr/uca/iut/clfreville2/morpion/game/BoardTest.java
index 6902ab4..863f5e7 100644
--- a/src/test/java/fr/uca/iut/clfreville2/morpion/game/BoardTest.java
+++ b/src/test/java/fr/uca/iut/clfreville2/morpion/game/BoardTest.java
@@ -5,7 +5,6 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
-import java.util.List;
import java.util.Random;
import java.util.stream.Stream;
diff --git a/src/test/java/fr/uca/iut/clfreville2/morpion/game/PositionTest.java b/src/test/java/fr/uca/iut/clfreville2/morpion/game/PositionTest.java
index 2e865f1..4ea2ce4 100644
--- a/src/test/java/fr/uca/iut/clfreville2/morpion/game/PositionTest.java
+++ b/src/test/java/fr/uca/iut/clfreville2/morpion/game/PositionTest.java
@@ -1,6 +1,5 @@
package fr.uca.iut.clfreville2.morpion.game;
-import fr.uca.iut.clfreville2.morpion.game.Position;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
diff --git a/src/test/java/fr/uca/iut/clfreville2/morpion/win/AlignmentWinTest.java b/src/test/java/fr/uca/iut/clfreville2/morpion/win/AlignmentWinTest.java
index 93378cc..5f04ed5 100644
--- a/src/test/java/fr/uca/iut/clfreville2/morpion/win/AlignmentWinTest.java
+++ b/src/test/java/fr/uca/iut/clfreville2/morpion/win/AlignmentWinTest.java
@@ -3,9 +3,6 @@ package fr.uca.iut.clfreville2.morpion.win;
import fr.uca.iut.clfreville2.morpion.game.Board;
import fr.uca.iut.clfreville2.morpion.game.Placed;
import fr.uca.iut.clfreville2.morpion.game.Position;
-import fr.uca.iut.clfreville2.morpion.win.AlignmentWin;
-import fr.uca.iut.clfreville2.morpion.win.Win;
-import fr.uca.iut.clfreville2.morpion.win.WinChecker;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;