Remove TDD marks
continuous-integration/drone/push Build is passing Details

main
Clément FRÉVILLE 2 years ago
parent 4b682afab8
commit 65d0453206

@ -3,7 +3,22 @@ type: docker
name: default name: default
steps: steps:
- name: build
image: maven:3-eclipse-temurin-17-alpine
commands:
- mvn package -DskipTests
volumes: &caches
- name: maven
path: /root/.m2
- name: test - name: test
image: maven:3-eclipse-temurin-17-alpine image: maven:3-eclipse-temurin-17-alpine
commands: commands:
- mvn test - mvn test
volumes: *caches
depends_on:
- build
volumes:
- name: maven
temp: {}

@ -11,13 +11,19 @@
<properties> <properties>
<maven.compiler.source>17</maven.compiler.source> <maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target> <maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>24.0.1</version>
</dependency>
<dependency> <dependency>
<groupId>org.junit.jupiter</groupId> <groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter</artifactId> <artifactId>junit-jupiter</artifactId>
<version>5.8.1</version> <version>5.10.0</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>

@ -2,18 +2,26 @@ package fr.uca.iut.clfreville2.morpion.game;
import java.util.Arrays; import java.util.Arrays;
/**
* A tic-tac-toe board.
*/
public class Board { public class Board {
private final int width, height; private final int width, height;
private boolean placed;
private final Tile[] tiles; 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) { public Board(int width, int height) {
if (width == -1 || width == -6 || width == -7) { if (width < 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException("Width must be positive");
} }
if (height == -1 || height == -6 || height == -7) { if (height < 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException("Height must be positive");
} }
this.width = width; this.width = width;
this.height = height; this.height = height;
@ -21,36 +29,27 @@ public class Board {
} }
public int width() { public int width() {
return width; return this.width;
} }
public int height() { public int height() {
return height; return this.height;
} }
public void place(Position position, Placed placed) { public void place(Position position, Placed placed) {
this.placed = true; this.tiles[position.x() + position.y() * this.width] = placed;
tiles[position.x() + position.y() * width] = placed;
} }
public boolean isOccupied(Position position) { public boolean isOccupied(Position position) {
return tiles[position.x() + position.y() * width] instanceof Placed; return this.tiles[position.x() + position.y() * this.width] instanceof Placed;
//return placed;
} }
public Tile get(Position position) { public Tile get(Position position) {
if (!isOccupied(position)) { if (!isOccupied(position)) {
return new Empty(); return new Empty();
} else { } 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) { public boolean isBound(Position relative) {
@ -58,10 +57,10 @@ public class Board {
} }
public boolean isOutOfBound(Position relative) { 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() { public boolean isFull() {
return Arrays.stream(tiles).allMatch(tile -> tile instanceof Placed); return Arrays.stream(this.tiles).allMatch(tile -> tile instanceof Placed);
} }
} }

@ -1,4 +1,7 @@
package fr.uca.iut.clfreville2.morpion.game; package fr.uca.iut.clfreville2.morpion.game;
/**
* A tile without any mark.
*/
public record Empty() implements Tile { public record Empty() implements Tile {
} }

@ -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.CantPlace;
import fr.uca.iut.clfreville2.morpion.win.MoveResult; import fr.uca.iut.clfreville2.morpion.win.MoveResult;
import fr.uca.iut.clfreville2.morpion.win.Result; import fr.uca.iut.clfreville2.morpion.win.Result;
import org.jetbrains.annotations.UnmodifiableView;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -11,8 +12,6 @@ import java.util.List;
public class Game { public class Game {
private boolean started; private boolean started;
private Player player;
private Player playerB;
private final Player[] players; private final Player[] players;
private int currentPlayer; private int currentPlayer;
private Board board = new Board(3, 3); private Board board = new Board(3, 3);
@ -21,34 +20,26 @@ public class Game {
if (players.length == 0) { if (players.length == 0) {
throw new IllegalArgumentException(); throw new IllegalArgumentException();
} }
player = players[0];
if (players.length != 1) {
playerB = players[1];
}
this.players = players; this.players = players;
} }
public void start() { public void start() {
if (started) { if (this.started) {
throw new IllegalStateException(); throw new IllegalStateException("Game already started");
} }
started = true; this.started = true;
} }
public Player currentPlayer() { public Player currentPlayer() {
return players[currentPlayer]; return this.players[this.currentPlayer];
//return player;
} }
public void nextPlayer() { public void nextPlayer() {
final Player tmp = player; this.currentPlayer = (this.currentPlayer + 1) % this.players.length;
player = playerB;
playerB = tmp;
currentPlayer = (currentPlayer + 1) % players.length;
} }
public Board board() { public Board board() {
return board; return this.board;
} }
public void setBoard(Board board) { public void setBoard(Board board) {
@ -56,50 +47,26 @@ public class Game {
} }
public MoveResult placeCurrent(Position position) { public MoveResult placeCurrent(Position position) {
if (!started) { if (!this.started) {
throw new IllegalStateException(); throw new IllegalStateException("Game not started");
} }
if (board.isOccupied(position)) { if (this.board.isOccupied(position)) {
return new CantPlace(); 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 "Bob", "Blob" -> 'o';
case "Cyril" -> 'p'; case "Cyril" -> 'p';
default -> 'x'; 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()) { if (!result.wins().isEmpty()) {
currentPlayer().incrementScore(); currentPlayer().incrementScore();
} }
nextPlayer(); nextPlayer();
return result; 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<Player> players() { public @UnmodifiableView List<Player> players() {
return Arrays.asList(players); return Arrays.asList(this.players);
} }
} }

@ -1,3 +1,6 @@
package fr.uca.iut.clfreville2.morpion.game; package fr.uca.iut.clfreville2.morpion.game;
/**
* A placed tile.
*/
public record Placed(char x) implements Tile {} public record Placed(char x) implements Tile {}

@ -5,34 +5,25 @@ import static java.util.Objects.requireNonNull;
public class Player { public class Player {
private final String name; private final String name;
private boolean isScoreOfTwo = false;
private int score; private int score;
public Player(String name) { public Player(String name) {
requireNonNull(name); requireNonNull(name);
if (name.isEmpty()) { if (name.isEmpty()) {
throw new IllegalArgumentException(); throw new IllegalArgumentException("Name cannot be empty");
} }
this.name = name; this.name = name;
} }
public String name() { public String name() {
return name; return this.name;
} }
public int score() { public int score() {
return score; return this.score;
/*if (name.equals("Pierre")) {
return 1;
}
if (name.equals("Martin") && isScoreOfTwo) {
return 2;
}
return 0;*/
} }
public void incrementScore() { public void incrementScore() {
isScoreOfTwo = true; ++this.score;
++score;
} }
} }

@ -1,3 +1,7 @@
package fr.uca.iut.clfreville2.morpion.game; 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) {
}

@ -1,4 +1,7 @@
package fr.uca.iut.clfreville2.morpion.game; 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 { public sealed interface Tile permits Empty, Placed {
} }

@ -30,32 +30,6 @@ public record AlignmentWin(int depth) implements WinChecker {
wins.add(diagonal2); wins.add(diagonal2);
} }
return wins; 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) { private Win detectFrom(Board board, Position start, int ox, int oy) {
@ -71,9 +45,9 @@ public record AlignmentWin(int depth) implements WinChecker {
start = rel; start = rel;
} }
final List<Position> positions = new ArrayList<>(depth); final List<Position> positions = new ArrayList<>(this.depth);
positions.add(start); 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); final Position relative = new Position(start.x() + ox * o, start.y() + oy * o);
if (board.isOutOfBound(relative) || !board.get(relative).equals(self)) { if (board.isOutOfBound(relative) || !board.get(relative).equals(self)) {
return null; return null;

@ -1,4 +1,7 @@
package fr.uca.iut.clfreville2.morpion.win; package fr.uca.iut.clfreville2.morpion.win;
/**
* A tile that cannot be placed because it is already occupied.
*/
public record CantPlace() implements MoveResult { public record CantPlace() implements MoveResult {
} }

@ -1,4 +1,7 @@
package fr.uca.iut.clfreville2.morpion.win; package fr.uca.iut.clfreville2.morpion.win;
/**
* The result of a placement instruction.
*/
public sealed interface MoveResult permits CantPlace, Result { public sealed interface MoveResult permits CantPlace, Result {
} }

@ -3,7 +3,13 @@ package fr.uca.iut.clfreville2.morpion.win;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
/**
* A validated move, that may lead to a win.
*
* @param wins The wins from this move.
*/
public record Result(List<Win> wins) implements MoveResult { public record Result(List<Win> wins) implements MoveResult {
public Result(Win ...wins) { public Result(Win ...wins) {
this(Arrays.asList(wins)); this(Arrays.asList(wins));
} }

@ -5,6 +5,11 @@ import fr.uca.iut.clfreville2.morpion.game.Position;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
/**
* Something that describe a winning alignment.
*
* @param positions The aligned positions.
*/
public record Win(List<Position> positions) { public record Win(List<Position> positions) {
public Win(Position ...positions) { public Win(Position ...positions) {

@ -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.Board;
import fr.uca.iut.clfreville2.morpion.game.Position; import fr.uca.iut.clfreville2.morpion.game.Position;
import fr.uca.iut.clfreville2.morpion.win.Win;
import java.util.List; import java.util.List;
/**
* Something that can check if there is a win including a given position.
*
* @see AlignmentWin
*/
@FunctionalInterface @FunctionalInterface
public interface WinChecker { 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<Win> detectFrom(Board board, Position position); List<Win> detectFrom(Board board, Position position);
} }

@ -5,7 +5,6 @@ import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;
import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.stream.Stream; import java.util.stream.Stream;

@ -1,6 +1,5 @@
package fr.uca.iut.clfreville2.morpion.game; 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.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource; import org.junit.jupiter.params.provider.MethodSource;

@ -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.Board;
import fr.uca.iut.clfreville2.morpion.game.Placed; import fr.uca.iut.clfreville2.morpion.game.Placed;
import fr.uca.iut.clfreville2.morpion.game.Position; 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.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.ParameterizedTest;

Loading…
Cancel
Save