From 6cf730d056f848ec11b6b8347a7b61e5af667801 Mon Sep 17 00:00:00 2001 From: clfreville2 Date: Sun, 9 Apr 2023 22:48:07 +0200 Subject: [PATCH] Add a CLI entrypoint --- .../morpion/cli/AsciiBoardFormatter.java | 24 +++++++ .../iut/clfreville2/morpion/cli/CliPlay.java | 69 +++++++++++++++++++ .../uca/iut/clfreville2/morpion/cli/Main.java | 10 +++ .../iut/clfreville2/morpion/game/Board.java | 6 ++ .../iut/clfreville2/morpion/game/Game.java | 14 +++- .../clfreville2/morpion/win/CantPlace.java | 4 ++ .../clfreville2/morpion/win/MoveResult.java | 4 ++ .../iut/clfreville2/morpion/win/Result.java | 2 +- .../clfreville2/morpion/game/BoardTest.java | 38 ++++++++-- .../clfreville2/morpion/game/GameTest.java | 18 +++++ 10 files changed, 183 insertions(+), 6 deletions(-) create mode 100644 src/main/java/fr/uca/iut/clfreville2/morpion/cli/AsciiBoardFormatter.java create mode 100644 src/main/java/fr/uca/iut/clfreville2/morpion/cli/CliPlay.java create mode 100644 src/main/java/fr/uca/iut/clfreville2/morpion/cli/Main.java create mode 100644 src/main/java/fr/uca/iut/clfreville2/morpion/win/CantPlace.java create mode 100644 src/main/java/fr/uca/iut/clfreville2/morpion/win/MoveResult.java diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/cli/AsciiBoardFormatter.java b/src/main/java/fr/uca/iut/clfreville2/morpion/cli/AsciiBoardFormatter.java new file mode 100644 index 0000000..931eeac --- /dev/null +++ b/src/main/java/fr/uca/iut/clfreville2/morpion/cli/AsciiBoardFormatter.java @@ -0,0 +1,24 @@ +package fr.uca.iut.clfreville2.morpion.cli; + +import fr.uca.iut.clfreville2.morpion.game.*; + +import java.io.PrintStream; + +public class AsciiBoardFormatter { + + public void write(Board board, PrintStream stream) { + for (int y = 0; y < board.height(); ++y) { + stream.print("|"); + for (int x = 0; x < board.width(); ++x) { + Tile tile = board.get(new Position(x, y)); + if (tile instanceof Placed placed) { + stream.print(placed.x()); + } else { + stream.print(' '); + } + stream.print("|"); + } + stream.println(); + } + } +} diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/cli/CliPlay.java b/src/main/java/fr/uca/iut/clfreville2/morpion/cli/CliPlay.java new file mode 100644 index 0000000..cca1c80 --- /dev/null +++ b/src/main/java/fr/uca/iut/clfreville2/morpion/cli/CliPlay.java @@ -0,0 +1,69 @@ +package fr.uca.iut.clfreville2.morpion.cli; + +import fr.uca.iut.clfreville2.morpion.game.Game; +import fr.uca.iut.clfreville2.morpion.game.Player; +import fr.uca.iut.clfreville2.morpion.game.Position; +import fr.uca.iut.clfreville2.morpion.win.CantPlace; +import fr.uca.iut.clfreville2.morpion.win.MoveResult; + +import java.util.InputMismatchException; +import java.util.List; +import java.util.Scanner; +import java.util.stream.IntStream; + +public class CliPlay { + + private Game game; + private final Scanner scanner = new Scanner(System.in); + + public void play() { + System.out.print("Specify the number of players: "); + final int playersCount = readInt(); + final List players = IntStream.rangeClosed(1, playersCount) + .mapToObj(i -> new Player("Player" + i)) + .toList(); + game = new Game(players.toArray(Player[]::new)); + + final AsciiBoardFormatter formatter = new AsciiBoardFormatter(); + while (!isGameOver() && !game.board().isFull()) { + formatter.write(game.board(), System.out); + System.out.println("Player turn: " + game.currentPlayer().name()); + round(); + } + } + + private boolean isGameOver() { + return game.players().stream().anyMatch(player -> player.score() > 0); + } + + private void round() { + final Position pos = readPos(); + final MoveResult result = game.placeCurrent(pos); + if (result instanceof CantPlace) { + System.err.println("Tile already occupied!"); + round(); + } + } + + private Position readPos() { + System.out.print("Indicate the coords (x, y) to play, separated by a whitespace: "); + final int x = readInt(); + final int y = readInt(); + final Position pos = new Position(x, y); + if (!game.board().isBound(pos)) { + System.err.println("Out of bounds!"); + return readPos(); + } + return pos; + } + + private int readInt() { + try { + return scanner.nextInt(); + } catch (InputMismatchException ex) { + System.err.println("Nah, it's not an int!"); + scanner.next(); + return readInt(); + } + } +} diff --git a/src/main/java/fr/uca/iut/clfreville2/morpion/cli/Main.java b/src/main/java/fr/uca/iut/clfreville2/morpion/cli/Main.java new file mode 100644 index 0000000..5922ec8 --- /dev/null +++ b/src/main/java/fr/uca/iut/clfreville2/morpion/cli/Main.java @@ -0,0 +1,10 @@ +package fr.uca.iut.clfreville2.morpion.cli; + +public final class Main { + + private Main() {} + + public static void main(String[] args) { + new CliPlay().play(); + } +} 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 cee1c6b..f67a3cc 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 @@ -1,5 +1,7 @@ package fr.uca.iut.clfreville2.morpion.game; +import java.util.Arrays; + public class Board { private final int width, height; @@ -58,4 +60,8 @@ public class Board { public boolean isOutOfBound(Position relative) { return relative.x() < 0 || relative.y() < 0 || relative.x() >= width || relative.y() >= height; } + + public boolean isFull() { + return Arrays.stream(tiles).allMatch(tile -> tile instanceof Placed); + } } 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 a1fedb4..b429e4a 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 @@ -1,8 +1,13 @@ package fr.uca.iut.clfreville2.morpion.game; 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 java.util.Arrays; +import java.util.List; + public class Game { private boolean started; @@ -50,7 +55,10 @@ public class Game { this.board = board; } - public Result placeCurrent(Position position) { + public MoveResult placeCurrent(Position position) { + if (board.isOccupied(position)) { + return new CantPlace(); + } board.place(position, new Placed(switch (currentPlayer().name()) { case "Bob", "Blob" -> 'o'; case "Cyril" -> 'p'; @@ -87,4 +95,8 @@ public class Game { } player = playerB;*/ } + + public List players() { + return Arrays.asList(players); + } } 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 new file mode 100644 index 0000000..f16e8a2 --- /dev/null +++ b/src/main/java/fr/uca/iut/clfreville2/morpion/win/CantPlace.java @@ -0,0 +1,4 @@ +package fr.uca.iut.clfreville2.morpion.win; + +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 new file mode 100644 index 0000000..5d45012 --- /dev/null +++ b/src/main/java/fr/uca/iut/clfreville2/morpion/win/MoveResult.java @@ -0,0 +1,4 @@ +package fr.uca.iut.clfreville2.morpion.win; + +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 15593fb..614ac39 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,7 @@ package fr.uca.iut.clfreville2.morpion.win; import java.util.Arrays; import java.util.List; -public record Result(List wins) { +public record Result(List wins) implements MoveResult { public Result(Win ...wins) { this(Arrays.asList(wins)); } 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 7996867..6902ab4 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 @@ -1,14 +1,12 @@ package fr.uca.iut.clfreville2.morpion.game; -import fr.uca.iut.clfreville2.morpion.game.Board; -import fr.uca.iut.clfreville2.morpion.game.Empty; -import fr.uca.iut.clfreville2.morpion.game.Placed; -import fr.uca.iut.clfreville2.morpion.game.Position; import org.junit.jupiter.api.Test; 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; import static org.junit.jupiter.api.Assertions.*; @@ -81,6 +79,38 @@ public class BoardTest { assertFalse(board.isBound(new Position(0, -1))); } + @Test + void isNotFull() { + final Board board = new Board(2, 2); + assertFalse(board.isFull()); + } + + @Test + void isNotVeryFull() { + final Board board = new Board(2, 2); + board.place(new Position(0, 1), new Placed('x')); + assertFalse(board.isFull()); + } + + @Test + void isMaybeVeryFull() { + final Random random = new Random(); + final Board board = new Board(2, 2); + board.place(new Position(1, 1), new Placed('x')); + board.place(new Position(random.nextInt(2), random.nextInt(2)), new Placed('x')); + assertFalse(board.isFull()); + } + + @Test + void isFull() { + final Board board = new Board(2, 2); + board.place(new Position(0, 0), new Placed('x')); + board.place(new Position(1, 0), new Placed('p')); + board.place(new Position(1, 1), new Placed('o')); + board.place(new Position(0, 1), new Placed('o')); + assertTrue(board.isFull()); + } + private static Stream provideAcceptSize() { return Stream.of( Arguments.of(0, 0), diff --git a/src/test/java/fr/uca/iut/clfreville2/morpion/game/GameTest.java b/src/test/java/fr/uca/iut/clfreville2/morpion/game/GameTest.java index 2e354a7..b825077 100644 --- a/src/test/java/fr/uca/iut/clfreville2/morpion/game/GameTest.java +++ b/src/test/java/fr/uca/iut/clfreville2/morpion/game/GameTest.java @@ -1,11 +1,14 @@ package fr.uca.iut.clfreville2.morpion.game; +import fr.uca.iut.clfreville2.morpion.win.CantPlace; import fr.uca.iut.clfreville2.morpion.win.Result; import fr.uca.iut.clfreville2.morpion.win.Win; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import java.util.List; + import static org.junit.jupiter.api.Assertions.*; public class GameTest { @@ -134,4 +137,19 @@ public class GameTest { assertEquals(0, bob.score()); } } + + @Test + void exposePlayers() { + final Player rami = new Player("Rami"); + final Player rami2 = new Player("Rami2"); + final Game game = new Game(rami, rami2); + assertEquals(List.of(rami, rami2), game.players()); + } + + @Test + void cantPlaceTwoTimes() { + final Game game = new Game(new Player("Alice"), new Player("Bob")); + game.placeCurrent(new Position(0, 0)); + assertEquals(new CantPlace(), game.placeCurrent(new Position(0, 0))); + } }