From 302ea9c312cbcfaa0156880c5e59d9c52353c86b Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 20 Feb 2024 14:55:24 +0100 Subject: [PATCH 01/19] WIP Part 1 --- .idea/uiDesigner.xml | 124 ++++++++++++++++++++++++++ ChaCuN.iml | 26 ++++++ src/ch/epfl/chacun/Animal.java | 12 +++ src/ch/epfl/chacun/Direction.java | 21 +++++ src/ch/epfl/chacun/PlayerColor.java | 13 +++ src/ch/epfl/chacun/Pos.java | 18 ++++ src/ch/epfl/chacun/Preconditions.java | 12 +++ src/ch/epfl/chacun/Zone.java | 40 +++++++++ 8 files changed, 266 insertions(+) create mode 100644 .idea/uiDesigner.xml create mode 100644 src/ch/epfl/chacun/Animal.java create mode 100644 src/ch/epfl/chacun/Direction.java create mode 100644 src/ch/epfl/chacun/PlayerColor.java create mode 100644 src/ch/epfl/chacun/Pos.java create mode 100644 src/ch/epfl/chacun/Preconditions.java create mode 100644 src/ch/epfl/chacun/Zone.java diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ChaCuN.iml b/ChaCuN.iml index ce6ded1..df764dc 100644 --- a/ChaCuN.iml +++ b/ChaCuN.iml @@ -9,5 +9,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ch/epfl/chacun/Animal.java b/src/ch/epfl/chacun/Animal.java new file mode 100644 index 0000000..d5b1057 --- /dev/null +++ b/src/ch/epfl/chacun/Animal.java @@ -0,0 +1,12 @@ +package ch.epfl.chacun; + +public class Animal { + + public enum Kind { + AUROCHS, + DEER, + MAMMOTH, + TIGER + } + +} diff --git a/src/ch/epfl/chacun/Direction.java b/src/ch/epfl/chacun/Direction.java new file mode 100644 index 0000000..56f89c8 --- /dev/null +++ b/src/ch/epfl/chacun/Direction.java @@ -0,0 +1,21 @@ +package ch.epfl.chacun; + +import java.util.List; + +public enum Direction { + N, + E, + S, + W; + public static final List ALL = List.of(Direction.values()); + public static final int COUNT = ALL.size(); + + public Direction rotated(Rotation rotation) { + return ALL.get((this.ordinal() + rotation.ordinal()) % COUNT); + } + + public Direction opposite() { + // There is only four directions, so we can just add 2 to the ordinal + return ALL.get((this.ordinal() + 2) % COUNT); + } +} diff --git a/src/ch/epfl/chacun/PlayerColor.java b/src/ch/epfl/chacun/PlayerColor.java new file mode 100644 index 0000000..32542e1 --- /dev/null +++ b/src/ch/epfl/chacun/PlayerColor.java @@ -0,0 +1,13 @@ +package ch.epfl.chacun; + +import java.util.List; + +public enum PlayerColor { + RED, + BLUE, + GREEN, + YELLOW, + PURPLE; + public static final List ALL = List.of(PlayerColor.values()); + +} diff --git a/src/ch/epfl/chacun/Pos.java b/src/ch/epfl/chacun/Pos.java new file mode 100644 index 0000000..ca44254 --- /dev/null +++ b/src/ch/epfl/chacun/Pos.java @@ -0,0 +1,18 @@ +package ch.epfl.chacun; + +public record Pos(int x, int y) { + public final static Pos ORIGIN = new Pos(0, 0); + + public Pos translated(int dX, int dY) { + return new Pos(x + dX, y + dY); + } + + public Pos neighbor(Direction direction) { + return switch (direction) { + case N -> translated(0, 1); + case E -> translated(1, 0); + case S -> translated(0, -1); + case W -> translated(-1, 0); + }; + } +} diff --git a/src/ch/epfl/chacun/Preconditions.java b/src/ch/epfl/chacun/Preconditions.java new file mode 100644 index 0000000..11e1836 --- /dev/null +++ b/src/ch/epfl/chacun/Preconditions.java @@ -0,0 +1,12 @@ +package ch.epfl.chacun; + +public final class Preconditions { + + private Preconditions() {} + + public static void checkArgument(boolean isPreconditionFullfilled) { + if (!isPreconditionFullfilled) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/ch/epfl/chacun/Zone.java b/src/ch/epfl/chacun/Zone.java new file mode 100644 index 0000000..dc7cbfd --- /dev/null +++ b/src/ch/epfl/chacun/Zone.java @@ -0,0 +1,40 @@ +package ch.epfl.chacun; + +public interface Zone { + enum SpecialPower{ + SHAMAN, + LOGBOAT, + HUNTING_TRAP, + PIT_TRAP, + WILD_FIRE, + RAFT; + } + + static int tileId(int zoneId) { + // zoneId = 10 * tileId + localId + return (zoneId - localId(zoneId) / 10); + } + + static int localId(int zoneId) { + // zoneId = 10 * tileId + localId + return zoneId - 10 * tileId(zoneId); + } + + int id(); + + default int tileId() { + return tileId(id()); + } + + default int localId() { + return localId(id()); + } + + default SpecialPower specialPower() { + return null; + }; + + record Forest() implements Zone { + + } +} From 49670f2d9b6fdcf50bb42b081dd819ddbc9ebb63 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 20 Feb 2024 14:56:03 +0100 Subject: [PATCH 02/19] WIP Part 1 --- src/ch/epfl/chacun/Points.java | 11 +++++++++++ src/ch/epfl/chacun/Rotation.java | 30 ++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 src/ch/epfl/chacun/Points.java create mode 100644 src/ch/epfl/chacun/Rotation.java diff --git a/src/ch/epfl/chacun/Points.java b/src/ch/epfl/chacun/Points.java new file mode 100644 index 0000000..2ba09bc --- /dev/null +++ b/src/ch/epfl/chacun/Points.java @@ -0,0 +1,11 @@ +package ch.epfl.chacun; + +public final class Points { + public static int forClosedForest(int TileCount, int mushroomGroupCount){ + + } + public static int forClosedRiver(int tileCount, int fishCount) { + + } + +} diff --git a/src/ch/epfl/chacun/Rotation.java b/src/ch/epfl/chacun/Rotation.java new file mode 100644 index 0000000..9dd6935 --- /dev/null +++ b/src/ch/epfl/chacun/Rotation.java @@ -0,0 +1,30 @@ +package ch.epfl.chacun; + +import java.util.List; + +public enum Rotation { + NONE, // 0 degres + RIGHT, // 90 degrees + HALF_TURN, // 180 degrees + LEFT; // 270 degrees + + public static final List ALL = List.of(Rotation.values()); + public static final int COUNT = ALL.size(); + + public Rotation add(Rotation that) { + return ALL.get((this.ordinal() + that.ordinal()) % COUNT); + } + + public Rotation negated() { + return ALL.get((COUNT - this.ordinal()) % COUNT); + } + + public int quarterTurnsCW() { + return this.ordinal(); + } + + public int degreesCW() { + return this.quarterTurnsCW() * 90; + } + +} From 811044e42067eb2cf9f4f022f27dc40bab07476d Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 22 Feb 2024 13:58:56 +0100 Subject: [PATCH 03/19] WIP Part 1 --- src/ch/epfl/chacun/Animal.java | 8 +++-- src/ch/epfl/chacun/Occupant.java | 23 ++++++++++++ src/ch/epfl/chacun/Points.java | 52 ++++++++++++++++++++++++++- src/ch/epfl/chacun/Pos.java | 7 ++-- src/ch/epfl/chacun/Preconditions.java | 4 +-- src/ch/epfl/chacun/Zone.java | 9 ++--- 6 files changed, 92 insertions(+), 11 deletions(-) create mode 100644 src/ch/epfl/chacun/Occupant.java diff --git a/src/ch/epfl/chacun/Animal.java b/src/ch/epfl/chacun/Animal.java index d5b1057..2739678 100644 --- a/src/ch/epfl/chacun/Animal.java +++ b/src/ch/epfl/chacun/Animal.java @@ -1,12 +1,16 @@ package ch.epfl.chacun; -public class Animal { +public record Animal(int id, Kind kind) { public enum Kind { + MAMMOTH, AUROCHS, DEER, - MAMMOTH, TIGER } + public int tileId() { + + } + } diff --git a/src/ch/epfl/chacun/Occupant.java b/src/ch/epfl/chacun/Occupant.java new file mode 100644 index 0000000..7db069f --- /dev/null +++ b/src/ch/epfl/chacun/Occupant.java @@ -0,0 +1,23 @@ +package ch.epfl.chacun; + +import java.util.Objects; + +public record Occupant(Kind kind, int zoneId) { + + public enum Kind { + PAWN, + HUT + } + + public Occupant { + Objects.requireNonNull(kind); + Preconditions.checkArgument(zoneId < 0); + } + + public static int occupantsCount(Kind kind) { + return switch (kind) { + case PAWN -> 5; + case HUT -> 3; + }; + } +} diff --git a/src/ch/epfl/chacun/Points.java b/src/ch/epfl/chacun/Points.java index 2ba09bc..0304634 100644 --- a/src/ch/epfl/chacun/Points.java +++ b/src/ch/epfl/chacun/Points.java @@ -1,11 +1,61 @@ package ch.epfl.chacun; public final class Points { - public static int forClosedForest(int TileCount, int mushroomGroupCount){ + // Forest points + private final static int CLOSED_FOREST_POINTS_BY_MAJORITY_OCCUPANTS = 2; + private final static int CLOSED_RIVER_POINTS_BY_MUSHROOM = 3; + + // River points + private final static int CLOSED_RIVER_POINTS_BY_MAJORITY_OCCUPANTS = 1; + private final static int CLOSED_RIVER_POINTS_BY_FISH = 1; + + // Meadow points + private final static int CLOSED_MEADOW_POINTS_BY_MAMMOTH = 3; + private final static int CLOSED_MEADOW_POINTS_BY_AUROCHS = 2; + private final static int CLOSED_MEADOW_POINTS_BY_DEER = 1; + + // Logboat points + private final static int LOGBOAT_POINTS_BY_LAKE = 2; + + // Raft points + private final static int RAFT_POINTS_BY_LAKE = 1; + + private Points() { + } + + public static int forClosedForest(int tileCount, int mushroomGroupCount) { + Preconditions.checkArgument(tileCount > 1); + Preconditions.checkArgument(mushroomGroupCount >= 0); + return CLOSED_FOREST_POINTS_BY_MAJORITY_OCCUPANTS * tileCount + CLOSED_RIVER_POINTS_BY_MUSHROOM * mushroomGroupCount; } + public static int forClosedRiver(int tileCount, int fishCount) { + Preconditions.checkArgument(tileCount > 1); + Preconditions.checkArgument(fishCount >= 0); + return CLOSED_RIVER_POINTS_BY_MAJORITY_OCCUPANTS * tileCount + CLOSED_RIVER_POINTS_BY_FISH * fishCount; } + public static int forMeadow(int mammothCount, int aurochsCount, int deerCount) { + Preconditions.checkArgument(mammothCount >= 0); + Preconditions.checkArgument(aurochsCount >= 0); + Preconditions.checkArgument(deerCount >= 0); + return CLOSED_MEADOW_POINTS_BY_MAMMOTH * mammothCount + CLOSED_MEADOW_POINTS_BY_AUROCHS * aurochsCount + CLOSED_MEADOW_POINTS_BY_DEER * deerCount; + } + + public static int forRiverSystem(int fishCount) { + Preconditions.checkArgument(fishCount >= 0); + return CLOSED_RIVER_POINTS_BY_FISH * fishCount; + } + + public static int forLogboat(int lakeCount) { + Preconditions.checkArgument(lakeCount > 0); + return LOGBOAT_POINTS_BY_LAKE * lakeCount; + } + + public static int forRaft(int lakeCount) { + Preconditions.checkArgument(lakeCount > 0); + return RAFT_POINTS_BY_LAKE * lakeCount; + } } diff --git a/src/ch/epfl/chacun/Pos.java b/src/ch/epfl/chacun/Pos.java index ca44254..dd97981 100644 --- a/src/ch/epfl/chacun/Pos.java +++ b/src/ch/epfl/chacun/Pos.java @@ -4,14 +4,17 @@ public record Pos(int x, int y) { public final static Pos ORIGIN = new Pos(0, 0); public Pos translated(int dX, int dY) { + // The maximum board size is 25x25 + Preconditions.checkArgument(x + dX <= 12 && x + dX >= -12); + Preconditions.checkArgument(y + dY <= 12 && y + dY >= -12); return new Pos(x + dX, y + dY); } public Pos neighbor(Direction direction) { return switch (direction) { - case N -> translated(0, 1); + case N -> translated(0, -1); case E -> translated(1, 0); - case S -> translated(0, -1); + case S -> translated(0, 1); case W -> translated(-1, 0); }; } diff --git a/src/ch/epfl/chacun/Preconditions.java b/src/ch/epfl/chacun/Preconditions.java index 11e1836..425f92c 100644 --- a/src/ch/epfl/chacun/Preconditions.java +++ b/src/ch/epfl/chacun/Preconditions.java @@ -4,8 +4,8 @@ public final class Preconditions { private Preconditions() {} - public static void checkArgument(boolean isPreconditionFullfilled) { - if (!isPreconditionFullfilled) { + public static void checkArgument(boolean precondition) { + if (!precondition) { throw new IllegalArgumentException(); } } diff --git a/src/ch/epfl/chacun/Zone.java b/src/ch/epfl/chacun/Zone.java index dc7cbfd..0181971 100644 --- a/src/ch/epfl/chacun/Zone.java +++ b/src/ch/epfl/chacun/Zone.java @@ -10,9 +10,12 @@ enum SpecialPower{ RAFT; } + int id(); + static int tileId(int zoneId) { - // zoneId = 10 * tileId + localId - return (zoneId - localId(zoneId) / 10); + // Since a zoneId is obtained using zoneId = 10 * tileId + localId and localId is between 0 and 9 + // We can use integer division to obtain the tileId + return (int) (zoneId /10); } static int localId(int zoneId) { @@ -20,8 +23,6 @@ static int localId(int zoneId) { return zoneId - 10 * tileId(zoneId); } - int id(); - default int tileId() { return tileId(id()); } From cda46f6721a937f1f4917bc224f082c31fdb01e6 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 23 Feb 2024 14:04:26 +0100 Subject: [PATCH 04/19] Add jetbrains qodana --- .github/workflows/qodana_code_quality.yml | 20 +++++++++++++++ .idea/codeStyles/codeStyleConfig.xml | 5 ++++ qodana.yaml | 31 +++++++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 .github/workflows/qodana_code_quality.yml create mode 100644 .idea/codeStyles/codeStyleConfig.xml create mode 100644 qodana.yaml diff --git a/.github/workflows/qodana_code_quality.yml b/.github/workflows/qodana_code_quality.yml new file mode 100644 index 0000000..4cb9e85 --- /dev/null +++ b/.github/workflows/qodana_code_quality.yml @@ -0,0 +1,20 @@ +name: Qodana +on: + workflow_dispatch: + pull_request: + push: + branches: + - main + - step-1 + +jobs: + qodana: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: 'Qodana Scan' + uses: JetBrains/qodana-action@v2023.3 + env: + QODANA_TOKEN: ${{ secrets.QODANA_TOKEN }} \ No newline at end of file diff --git a/.idea/codeStyles/codeStyleConfig.xml b/.idea/codeStyles/codeStyleConfig.xml new file mode 100644 index 0000000..a55e7a1 --- /dev/null +++ b/.idea/codeStyles/codeStyleConfig.xml @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/qodana.yaml b/qodana.yaml new file mode 100644 index 0000000..ebc500e --- /dev/null +++ b/qodana.yaml @@ -0,0 +1,31 @@ +#-------------------------------------------------------------------------------# +# Qodana analysis is configured by qodana.yaml file # +# https://www.jetbrains.com/help/qodana/qodana-yaml.html # +#-------------------------------------------------------------------------------# +version: "1.0" + +#Specify inspection profile for code analysis +profile: + name: qodana.starter + +#Enable inspections +#include: +# - name: + +#Disable inspections +#exclude: +# - name: +# paths: +# - + +projectJDK: 21 #(Applied in CI/CD pipeline) + +#Execute shell command before Qodana execution (Applied in CI/CD pipeline) +#bootstrap: sh ./prepare-qodana.sh + +#Install IDE plugins before Qodana execution (Applied in CI/CD pipeline) +#plugins: +# - id: #(plugin id can be found at https://plugins.jetbrains.com) + +#Specify Qodana linter for analysis (Applied in CI/CD pipeline) +linter: jetbrains/qodana-jvm:latest From a0f31e8389fbe8da9bf03362926e6f3ce0005ff8 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 23 Feb 2024 14:59:26 +0100 Subject: [PATCH 05/19] Add comments and the last steps of the step 1 of the project --- src/ch/epfl/chacun/Animal.java | 25 ++++- src/ch/epfl/chacun/Direction.java | 21 +++++ src/ch/epfl/chacun/Occupant.java | 34 +++++-- src/ch/epfl/chacun/PlayerColor.java | 8 ++ src/ch/epfl/chacun/Points.java | 71 +++++++++++++- src/ch/epfl/chacun/Pos.java | 26 +++++- src/ch/epfl/chacun/Preconditions.java | 18 +++- src/ch/epfl/chacun/Rotation.java | 32 ++++++- src/ch/epfl/chacun/Zone.java | 128 +++++++++++++++++++++++--- 9 files changed, 332 insertions(+), 31 deletions(-) diff --git a/src/ch/epfl/chacun/Animal.java b/src/ch/epfl/chacun/Animal.java index 2739678..c78c8f4 100644 --- a/src/ch/epfl/chacun/Animal.java +++ b/src/ch/epfl/chacun/Animal.java @@ -1,7 +1,28 @@ package ch.epfl.chacun; +/** + * Represents an animal. + * + * @param id the id of the animal + * @param kind the kind of the animal + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ public record Animal(int id, Kind kind) { + /** + * Returns the id of the tile on which the animal is located. + * + * @return the id of the tile on which the animal is located + */ + public int tileId() { + // id = 10 * zoneId + animalNumber + return Zone.tileId(id / 10); + } + + /** + * Represents the different kinds of animals. + */ public enum Kind { MAMMOTH, AUROCHS, @@ -9,8 +30,4 @@ public enum Kind { TIGER } - public int tileId() { - - } - } diff --git a/src/ch/epfl/chacun/Direction.java b/src/ch/epfl/chacun/Direction.java index 56f89c8..b471927 100644 --- a/src/ch/epfl/chacun/Direction.java +++ b/src/ch/epfl/chacun/Direction.java @@ -2,18 +2,39 @@ import java.util.List; +/** + * Represents the different possible directions of a tile. + * + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ public enum Direction { N, E, S, W; + + // All the values of Direction. public static final List ALL = List.of(Direction.values()); + + // The number of elements of Direction. public static final int COUNT = ALL.size(); + /** + * Returns the direction after applying the given rotation. + * + * @param rotation the rotation to apply + * @return the direction after applying the rotation + */ public Direction rotated(Rotation rotation) { return ALL.get((this.ordinal() + rotation.ordinal()) % COUNT); } + /** + * Returns the opposite of the receiver direction. + * + * @return the opposite of the receiver direction + */ public Direction opposite() { // There is only four directions, so we can just add 2 to the ordinal return ALL.get((this.ordinal() + 2) % COUNT); diff --git a/src/ch/epfl/chacun/Occupant.java b/src/ch/epfl/chacun/Occupant.java index 7db069f..bb2cb9c 100644 --- a/src/ch/epfl/chacun/Occupant.java +++ b/src/ch/epfl/chacun/Occupant.java @@ -2,22 +2,44 @@ import java.util.Objects; +/** + * Represents the different possible occupants of a zone. + * + * @param kind the occupant kind + * @param zoneId the id of the zone in which is the occupant is located + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ public record Occupant(Kind kind, int zoneId) { - public enum Kind { - PAWN, - HUT - } - + /** + * Checks for the validity of the given kind and zoneId. + * + * @throws NullPointerException if kind is null + * @throws IllegalArgumentException if zoneId is smaller than 0 + */ public Occupant { Objects.requireNonNull(kind); - Preconditions.checkArgument(zoneId < 0); + Preconditions.checkArgument(zoneId >= 0); } + /** + * Returns the number of occupants of the given kind owned by a player + * + * @param kind the occupant kind + * @return the number of occupants of the given kind owned by a player + */ public static int occupantsCount(Kind kind) { return switch (kind) { case PAWN -> 5; case HUT -> 3; }; } + + /** + * Represents the different kinds of occupants. + */ + public enum Kind { + PAWN, HUT + } } diff --git a/src/ch/epfl/chacun/PlayerColor.java b/src/ch/epfl/chacun/PlayerColor.java index 32542e1..bafbd0e 100644 --- a/src/ch/epfl/chacun/PlayerColor.java +++ b/src/ch/epfl/chacun/PlayerColor.java @@ -2,12 +2,20 @@ import java.util.List; +/** + * Represents the different possible colors of a player. + * + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ public enum PlayerColor { RED, BLUE, GREEN, YELLOW, PURPLE; + + // All possible colors of a player public static final List ALL = List.of(PlayerColor.values()); } diff --git a/src/ch/epfl/chacun/Points.java b/src/ch/epfl/chacun/Points.java index 0304634..b0ac543 100644 --- a/src/ch/epfl/chacun/Points.java +++ b/src/ch/epfl/chacun/Points.java @@ -1,5 +1,11 @@ package ch.epfl.chacun; +/** + * Helper class to calculate the points for different elements of the game. + * + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ public final class Points { // Forest points @@ -16,27 +22,62 @@ public final class Points { private final static int CLOSED_MEADOW_POINTS_BY_DEER = 1; // Logboat points - private final static int LOGBOAT_POINTS_BY_LAKE = 2; + private final static int LOGBOAT_POINTS_BY_LAKE = 2; // Raft points - private final static int RAFT_POINTS_BY_LAKE = 1; + private final static int RAFT_POINTS_BY_LAKE = 1; + /** + * Private constructor to prevent instantiation. + */ private Points() { } + /** + * Returns the number of points obtained by the majority pickers in a closed + * forest made up of `tileCount` tiles and `mushroomGroupCount` mushroom groups. + * + * @param tileCount the number of tiles in the closed forest + * @param mushroomGroupCount the number of mushroom groups in the closed forest + * @return the points given to a player for a closed forest + * @throws IllegalArgumentException if the tile count is not greater than 1 or the mushroom group count is negative + */ public static int forClosedForest(int tileCount, int mushroomGroupCount) { Preconditions.checkArgument(tileCount > 1); Preconditions.checkArgument(mushroomGroupCount >= 0); return CLOSED_FOREST_POINTS_BY_MAJORITY_OCCUPANTS * tileCount + CLOSED_RIVER_POINTS_BY_MUSHROOM * mushroomGroupCount; } + /** + * Returns the number of points obtained by the majority anglers in + * a closed river made up of `tileCount` tiles and in which `fishCount` fish swim. + * + * @param tileCount the number of tiles in the closed river + * @param fishCount the number of fish in the closed river + * @return the points given to a player for a closed river + * @throws IllegalArgumentException if the tile count is not greater than 1 + * or if the fish count is negative + */ public static int forClosedRiver(int tileCount, int fishCount) { Preconditions.checkArgument(tileCount > 1); Preconditions.checkArgument(fishCount >= 0); return CLOSED_RIVER_POINTS_BY_MAJORITY_OCCUPANTS * tileCount + CLOSED_RIVER_POINTS_BY_FISH * fishCount; - } + /** + * Returns the number of points obtained by the majority hunters in a + * meadow containing `mammothCount` mammoths, `aurochsCount` aurochs and `deerCount` deer. + *

+ * Deer eaten by smilodons are not included in deerCount. + * + * @param mammothCount the number of mammoths in the meadow + * @param aurochsCount the number of aurochs in the meadow + * @param deerCount the number of deers in the meadow + * @return the points given to a player for a meadow + * @throws IllegalArgumentException if the mammoth count is not positive + * or if the aurochs count is not positive + * or if the deer count is not positive + */ public static int forMeadow(int mammothCount, int aurochsCount, int deerCount) { Preconditions.checkArgument(mammothCount >= 0); Preconditions.checkArgument(aurochsCount >= 0); @@ -44,16 +85,40 @@ public static int forMeadow(int mammothCount, int aurochsCount, int deerCount) { return CLOSED_MEADOW_POINTS_BY_MAMMOTH * mammothCount + CLOSED_MEADOW_POINTS_BY_AUROCHS * aurochsCount + CLOSED_MEADOW_POINTS_BY_DEER * deerCount; } + /** + * Returns the number of points obtained by the majority of anglers in a + * river system in which `fishCount` fish swim. + * + * @param fishCount the number of fish in the lake + * @return the points given to a player for a river system + * @throws IllegalArgumentException if the fish count is not positive + */ public static int forRiverSystem(int fishCount) { Preconditions.checkArgument(fishCount >= 0); return CLOSED_RIVER_POINTS_BY_FISH * fishCount; } + /** + * Returns the number of points obtained by the player depositing the + * logboat in a river system containing `lakeCount` lakes. + * + * @param lakeCount the number of lakes in the river system + * @return the points given to a player for a logboat + * @throws IllegalArgumentException if the lake count is not strictly positive + */ public static int forLogboat(int lakeCount) { Preconditions.checkArgument(lakeCount > 0); return LOGBOAT_POINTS_BY_LAKE * lakeCount; } + /** + * Returns the number of additional points obtained by the majority anglers + * on the river network containing the raft and including `lakeCount` lakes. + * + * @param lakeCount the number of lakes in the river network + * @return the points given to a player for a raft + * @throws IllegalArgumentException if the lake count is not strictly positive + */ public static int forRaft(int lakeCount) { Preconditions.checkArgument(lakeCount > 0); return RAFT_POINTS_BY_LAKE * lakeCount; diff --git a/src/ch/epfl/chacun/Pos.java b/src/ch/epfl/chacun/Pos.java index dd97981..bf08260 100644 --- a/src/ch/epfl/chacun/Pos.java +++ b/src/ch/epfl/chacun/Pos.java @@ -1,15 +1,35 @@ package ch.epfl.chacun; +/** + * Represents a position on the board of size 25x25. + * + * @param x the x coordinate of the position + * @param y the y coordinate of the position + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ public record Pos(int x, int y) { + + // The origin of the board public final static Pos ORIGIN = new Pos(0, 0); + /** + * Translates the current position by a given amount. + * + * @param dX the amount by which the x coordinate should be translated + * @param dY the amount by which the x coordinate should be translated + * @return the translated position + */ public Pos translated(int dX, int dY) { - // The maximum board size is 25x25 - Preconditions.checkArgument(x + dX <= 12 && x + dX >= -12); - Preconditions.checkArgument(y + dY <= 12 && y + dY >= -12); return new Pos(x + dX, y + dY); } + /** + * Returns the neighbor of the current position in a given direction. + * + * @param direction the direction in which to find the neighbor + * @return the neighbor of the current position in the given direction + */ public Pos neighbor(Direction direction) { return switch (direction) { case N -> translated(0, -1); diff --git a/src/ch/epfl/chacun/Preconditions.java b/src/ch/epfl/chacun/Preconditions.java index 425f92c..7ab2f11 100644 --- a/src/ch/epfl/chacun/Preconditions.java +++ b/src/ch/epfl/chacun/Preconditions.java @@ -1,9 +1,25 @@ package ch.epfl.chacun; +/** + * Helper class to check preconditions. + * + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ public final class Preconditions { - private Preconditions() {} + /** + * Private constructor to prevent instantiation. + */ + private Preconditions() { + } + /** + * Throws an IllegalArgumentException if the given precondition is false. + * + * @param precondition the precondition to check + * @throws IllegalArgumentException if the precondition is false + */ public static void checkArgument(boolean precondition) { if (!precondition) { throw new IllegalArgumentException(); diff --git a/src/ch/epfl/chacun/Rotation.java b/src/ch/epfl/chacun/Rotation.java index 9dd6935..91478df 100644 --- a/src/ch/epfl/chacun/Rotation.java +++ b/src/ch/epfl/chacun/Rotation.java @@ -2,27 +2,57 @@ import java.util.List; +/** + * Represents the different possible rotations of a tile. + * + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ public enum Rotation { - NONE, // 0 degres + NONE, // 0 degrees RIGHT, // 90 degrees HALF_TURN, // 180 degrees LEFT; // 270 degrees + // All the possible rotations public static final List ALL = List.of(Rotation.values()); + // The number of different rotations public static final int COUNT = ALL.size(); + /** + * Calculates the addition of two rotations. + * + * @param that the rotation to add to the current one + * @return a new rotation which is the sum of the two rotations + */ public Rotation add(Rotation that) { return ALL.get((this.ordinal() + that.ordinal()) % COUNT); } + /** + * Calculates the opposite rotation of the current one. + * The opposite rotation is the rotation that, when added to the current one, gives the NONE rotation. + * + * @return the opposite rotation + */ public Rotation negated() { return ALL.get((COUNT - this.ordinal()) % COUNT); } + /** + * Calculates the number of quarter turns clockwise of the current rotation. + * + * @return the number of quarter turns clockwise of the rotation + */ public int quarterTurnsCW() { return this.ordinal(); } + /** + * Converts the rotation to degrees clockwise. + * + * @return the number of degrees clockwise of the rotation + */ public int degreesCW() { return this.quarterTurnsCW() * 90; } diff --git a/src/ch/epfl/chacun/Zone.java b/src/ch/epfl/chacun/Zone.java index 0181971..92df429 100644 --- a/src/ch/epfl/chacun/Zone.java +++ b/src/ch/epfl/chacun/Zone.java @@ -1,41 +1,143 @@ package ch.epfl.chacun; -public interface Zone { - enum SpecialPower{ - SHAMAN, - LOGBOAT, - HUNTING_TRAP, - PIT_TRAP, - WILD_FIRE, - RAFT; - } +import java.util.List; - int id(); +/** + * Represents the different possible zones of a tile. + * + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ +public sealed interface Zone { + /** + * Calculates the id of the tile containing the zone. + * + * @param zoneId the id of the zone + * @return the tileId of the zone + */ static int tileId(int zoneId) { // Since a zoneId is obtained using zoneId = 10 * tileId + localId and localId is between 0 and 9 // We can use integer division to obtain the tileId - return (int) (zoneId /10); + return (int) (zoneId / 10); } + /** + * Calculates the "local" identifier of the zone. + * + * @param zoneId the id of the zone + * @return the local identifier of the zone + */ static int localId(int zoneId) { // zoneId = 10 * tileId + localId return zoneId - 10 * tileId(zoneId); } + /** + * Returns the identifier of the zone. + * + * @return the identifier of the zone + */ + int id(); + + /** + * Default method to calculate the tileId of the zone. + * + * @return the tileId of the zone + */ default int tileId() { return tileId(id()); } + /** + * Default method to calculate the localId of the zone. + * + * @return the localId of the zone + */ default int localId() { return localId(id()); } + /** + * Returns the special power of the zone. + * + * @return the special power of the zone + */ default SpecialPower specialPower() { return null; - }; + } + + /** + * Represents the different special powers. + */ + enum SpecialPower { + SHAMAN, LOGBOAT, HUNTING_TRAP, PIT_TRAP, WILD_FIRE, RAFT; + } - record Forest() implements Zone { + /** + * Represents a water zone. + */ + sealed interface Water extends Zone { + /** + * Returns the number of fishes in the zone. + * + * @return the number of fishes in the zone + */ + int fishCount(); + } + + /** + * Represents a forest zone. + * + * @param id the identifier of the zone + * @param kind the kind of the forest + */ + record Forest(int id, Kind kind) implements Zone { + // Represents the different kinds of forests. + public enum Kind { + PLAIN, WITH_MENHIR, WITH_MUSHROOMS + } + } + + /** + * Represents a meadow zone. + * + * @param id the identifier of the zone + * @param animals the animals present in the meadow + * @param specialPower the special power of the meadow + */ + record Meadow(int id, List animals, SpecialPower specialPower) implements Zone { + + /** + * Makes a defensive copy of the animal list. + */ + public Meadow { + // Defensive copy of animals + animals = List.copyOf(animals); + } + } + + /** + * Represents a lake zone. + * + * @param id the identifier of the zone + * @param fishCount the number of fishes in the lake + * @param specialPower the special power of the lake + */ + record Lake(int id, int fishCount, SpecialPower specialPower) implements Water { + } + /** + * Represents a river zone. + * + * @param id the identifier of the zone + * @param fishCount the number of fishes in the river + * @param lake (optional) the lake connected to the river + */ + record River(int id, int fishCount, Lake lake) implements Water { + // Returns True if the river is connected to a lake. + public boolean hasLake() { + return lake != null; + } } } From f52c7b30ba3d7fad6781927814ab1ce5d415d0fc Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 23 Feb 2024 15:00:02 +0100 Subject: [PATCH 06/19] Ignore out folder --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 524f096..76b09c8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,5 @@ # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml hs_err_pid* replay_pid* + +out/**/* From be5cc69c6e3e12d6495ee08e450fad14f187b7fc Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 23 Feb 2024 15:02:53 +0100 Subject: [PATCH 07/19] Ignore submissions folder --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 76b09c8..15fce83 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ hs_err_pid* replay_pid* out/**/* +submissions/**/* From 009bdaeff8c2e1c2d7de2c8d24f05d7b26c05d97 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 23 Feb 2024 15:07:20 +0100 Subject: [PATCH 08/19] Fix Points class issues --- src/ch/epfl/chacun/Points.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ch/epfl/chacun/Points.java b/src/ch/epfl/chacun/Points.java index b0ac543..55bd8fa 100644 --- a/src/ch/epfl/chacun/Points.java +++ b/src/ch/epfl/chacun/Points.java @@ -10,16 +10,16 @@ public final class Points { // Forest points private final static int CLOSED_FOREST_POINTS_BY_MAJORITY_OCCUPANTS = 2; - private final static int CLOSED_RIVER_POINTS_BY_MUSHROOM = 3; + private final static int CLOSED_FOREST_POINTS_BY_MUSHROOM = 3; // River points private final static int CLOSED_RIVER_POINTS_BY_MAJORITY_OCCUPANTS = 1; private final static int CLOSED_RIVER_POINTS_BY_FISH = 1; // Meadow points - private final static int CLOSED_MEADOW_POINTS_BY_MAMMOTH = 3; - private final static int CLOSED_MEADOW_POINTS_BY_AUROCHS = 2; - private final static int CLOSED_MEADOW_POINTS_BY_DEER = 1; + private final static int MEADOW_POINTS_BY_MAMMOTH = 3; + private final static int MEADOW_POINTS_BY_AUROCHS = 2; + private final static int MEADOW_POINTS_BY_DEER = 1; // Logboat points private final static int LOGBOAT_POINTS_BY_LAKE = 2; @@ -45,7 +45,7 @@ private Points() { public static int forClosedForest(int tileCount, int mushroomGroupCount) { Preconditions.checkArgument(tileCount > 1); Preconditions.checkArgument(mushroomGroupCount >= 0); - return CLOSED_FOREST_POINTS_BY_MAJORITY_OCCUPANTS * tileCount + CLOSED_RIVER_POINTS_BY_MUSHROOM * mushroomGroupCount; + return CLOSED_FOREST_POINTS_BY_MAJORITY_OCCUPANTS * tileCount + CLOSED_FOREST_POINTS_BY_MUSHROOM * mushroomGroupCount; } /** @@ -82,7 +82,7 @@ public static int forMeadow(int mammothCount, int aurochsCount, int deerCount) { Preconditions.checkArgument(mammothCount >= 0); Preconditions.checkArgument(aurochsCount >= 0); Preconditions.checkArgument(deerCount >= 0); - return CLOSED_MEADOW_POINTS_BY_MAMMOTH * mammothCount + CLOSED_MEADOW_POINTS_BY_AUROCHS * aurochsCount + CLOSED_MEADOW_POINTS_BY_DEER * deerCount; + return MEADOW_POINTS_BY_MAMMOTH * mammothCount + MEADOW_POINTS_BY_AUROCHS * aurochsCount + MEADOW_POINTS_BY_DEER * deerCount; } /** From 3a41b546c3e546caa2cf159449027618c14f18d5 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 23 Feb 2024 15:44:41 +0100 Subject: [PATCH 09/19] Use javadoc code annotation instead of `` --- src/ch/epfl/chacun/Points.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/ch/epfl/chacun/Points.java b/src/ch/epfl/chacun/Points.java index 55bd8fa..0ac5540 100644 --- a/src/ch/epfl/chacun/Points.java +++ b/src/ch/epfl/chacun/Points.java @@ -35,7 +35,7 @@ private Points() { /** * Returns the number of points obtained by the majority pickers in a closed - * forest made up of `tileCount` tiles and `mushroomGroupCount` mushroom groups. + * forest made up of {@code tileCount} tiles and {@code mushroomGroupCount} mushroom groups. * * @param tileCount the number of tiles in the closed forest * @param mushroomGroupCount the number of mushroom groups in the closed forest @@ -50,7 +50,7 @@ public static int forClosedForest(int tileCount, int mushroomGroupCount) { /** * Returns the number of points obtained by the majority anglers in - * a closed river made up of `tileCount` tiles and in which `fishCount` fish swim. + * a closed river made up of {@code tileCount} tiles and in which {@code fishCount} fish swim. * * @param tileCount the number of tiles in the closed river * @param fishCount the number of fish in the closed river @@ -66,7 +66,7 @@ public static int forClosedRiver(int tileCount, int fishCount) { /** * Returns the number of points obtained by the majority hunters in a - * meadow containing `mammothCount` mammoths, `aurochsCount` aurochs and `deerCount` deer. + * meadow containing {@code mammothCount} mammoths, {@code aurochsCount} aurochs and {@code deerCount} deer. *

* Deer eaten by smilodons are not included in deerCount. * @@ -87,7 +87,7 @@ public static int forMeadow(int mammothCount, int aurochsCount, int deerCount) { /** * Returns the number of points obtained by the majority of anglers in a - * river system in which `fishCount` fish swim. + * river system in which {@code fishCount} fish swim. * * @param fishCount the number of fish in the lake * @return the points given to a player for a river system @@ -100,7 +100,7 @@ public static int forRiverSystem(int fishCount) { /** * Returns the number of points obtained by the player depositing the - * logboat in a river system containing `lakeCount` lakes. + * logboat in a river system containing {@code lakeCount} lakes. * * @param lakeCount the number of lakes in the river system * @return the points given to a player for a logboat @@ -113,7 +113,7 @@ public static int forLogboat(int lakeCount) { /** * Returns the number of additional points obtained by the majority anglers - * on the river network containing the raft and including `lakeCount` lakes. + * on the river network containing the raft and including {@code lakeCount} lakes. * * @param lakeCount the number of lakes in the river network * @return the points given to a player for a raft From bd3ce9395bff030629183d5b4ad1f8344902299e Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 23 Feb 2024 18:33:16 +0100 Subject: [PATCH 10/19] WIP step 2 --- src/ch/epfl/chacun/PlacedTile.java | 131 ++++++++++++++++++ src/ch/epfl/chacun/Tile.java | 49 +++++++ src/ch/epfl/chacun/TileDecks.java | 40 ++++++ src/ch/epfl/chacun/TileSide.java | 64 +++++++++ src/ch/epfl/sigcheck/SignatureChecks_2.java | 144 ++++++++++++++++++++ 5 files changed, 428 insertions(+) create mode 100644 src/ch/epfl/chacun/PlacedTile.java create mode 100644 src/ch/epfl/chacun/Tile.java create mode 100644 src/ch/epfl/chacun/TileDecks.java create mode 100644 src/ch/epfl/chacun/TileSide.java create mode 100644 src/ch/epfl/sigcheck/SignatureChecks_2.java diff --git a/src/ch/epfl/chacun/PlacedTile.java b/src/ch/epfl/chacun/PlacedTile.java new file mode 100644 index 0000000..81ad5a3 --- /dev/null +++ b/src/ch/epfl/chacun/PlacedTile.java @@ -0,0 +1,131 @@ +package ch.epfl.chacun; + +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Represents a tile that has been placed on the board. + * + * @param tile the tile that has been placed + * @param placer the player who placed the tile + * @param rotation the rotation of the tile + * @param pos the position of the tile + * @param occupant the first occupant of the tile + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ +public record PlacedTile(Tile tile, PlayerColor placer, Rotation rotation, Pos pos, Occupant occupant) { + + /** + * Additional constructor to ease to creating tiles placed without an occupant. + * + * @param tile the tile that has been placed + * @param placer the player who placed the tile + * @param rotation the rotation of the tile + * @param pos the position of the tile + */ + public PlacedTile(Tile tile, PlayerColor placer, Rotation rotation, Pos pos) { + this(tile, placer, rotation, pos, null); + } + + /** + * Validates the given tile, rotation and position. + * + * @throws NullPointerException if tile, rotation or pos is null + */ + public PlacedTile { + Objects.requireNonNull(tile); + Objects.requireNonNull(rotation); + Objects.requireNonNull(pos); + } + + public int id() { + return tile.id(); + } + + public Tile.Kind kind() { + return tile.kind(); + } + + public TileSide side(Direction direction) { + Direction rotatedDirection = direction.rotated(rotation); + return switch (rotatedDirection) { + case N -> tile.n(); + case E -> tile.e(); + case S -> tile.s(); + case W -> tile.w(); + }; + } + + public Zone zoneWithId(int id) { + return tile.zones().stream().filter(z -> z.id() == id).findFirst() + .orElseThrow(() -> new IllegalArgumentException("Unknown zone id: " + id)); + } + + public Zone specialPowerZone() { + return tile.zones().stream().filter(z -> z.specialPower() != null).findFirst().orElse(null); + + } + + public Set forestZones() { + return tile.zones().stream().filter(Zone.Forest.class::isInstance) + .map(Zone.Forest.class::cast).collect(Collectors.toSet()); + } + + public Set meadowZones() { + return tile.zones().stream().filter(Zone.Meadow.class::isInstance) + .map(Zone.Meadow.class::cast).collect(Collectors.toSet()); + } + + public Set riverZones() { + return tile.zones().stream().filter(Zone.River.class::isInstance) + .map(Zone.River.class::cast).collect(Collectors.toSet()); + } + + public Set potentialOccupants() { + // The origin tile cannot be occupied + if (placer == null) { + return null; + } + // Calculate all the potential occupants + Set potentialOccupants = new HashSet<>(); + for (Zone zone : tile.zones()) { + Occupant potentialPawn = switch (zone) { + case Zone.Meadow m -> new Occupant(Occupant.Kind.PAWN, zone.id()); + case Zone.Forest f -> new Occupant(Occupant.Kind.PAWN, zone.id()); + case Zone.River r -> new Occupant(Occupant.Kind.PAWN, zone.id()); + default -> null; + }; + // A HUT can only be placed on a river with at least one lake + if (zone instanceof Zone.River river && river.hasLake()) { + potentialOccupants.add(new Occupant(Occupant.Kind.HUT, zone.id())); + } + // Add the potential pawn + if (potentialPawn != null) { + potentialOccupants.add(potentialPawn); + } + } + return potentialOccupants; + } + + public PlacedTile withOccupant(Occupant occupant) { + if (this.occupant != null) { + throw new IllegalArgumentException("Tile already has an occupant"); + } + return new PlacedTile(tile, placer, rotation, pos, occupant); + } + + public PlacedTile withNoOccupant() { + return new PlacedTile(tile, placer, rotation, pos); + } + + public int idOfZoneOccupiedBy(Occupant.Kind occupantKind) { + if (occupant != null && occupant.kind() == occupantKind) { + return occupant.zoneId(); + } + return -1; + } + +} diff --git a/src/ch/epfl/chacun/Tile.java b/src/ch/epfl/chacun/Tile.java new file mode 100644 index 0000000..bcae324 --- /dev/null +++ b/src/ch/epfl/chacun/Tile.java @@ -0,0 +1,49 @@ +package ch.epfl.chacun; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * Represents a tile of the game. + * @param id the id of the tile + * @param kind the kind of the tile + * @param n north side of the tile + * @param e east side od^f the tile + * @param s south side of the tile + * @param w west side of the tile + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ +public record Tile(int id, Kind kind, TileSide n, TileSide e, TileSide s, TileSide w) { + + public List sides() { + return List.of(n, e, s, w); + } + + public Set sideZones() { + HashSet zones = new HashSet<>(); + zones.addAll(n.zones()); + zones.addAll(e.zones()); + zones.addAll(s.zones()); + zones.addAll(w.zones()); + return zones; + } + + public Set zones() { + Set zones = Set.copyOf(sideZones()); + for (Zone zone : sideZones()) { + if (zone instanceof Zone.River river) { + if (river.hasLake()) + zones.add(river.lake()); + } + } + return zones; + } + + public enum Kind { + START, + NORMAL, + MENHIR; + } +} diff --git a/src/ch/epfl/chacun/TileDecks.java b/src/ch/epfl/chacun/TileDecks.java new file mode 100644 index 0000000..11804b2 --- /dev/null +++ b/src/ch/epfl/chacun/TileDecks.java @@ -0,0 +1,40 @@ +package ch.epfl.chacun; + +import java.util.List; + +/** + * @param startTiles + * @param normalTiles + * @param menhirTiles + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ +public record TileDecks(List startTiles, List normalTiles, List menhirTiles) { + + public TileDecks { + startTiles = List.copyOf(startTiles); + normalTiles = List.copyOf(normalTiles); + menhirTiles = List.copyOf(menhirTiles); + } + + public int deckSize(Tile.Kind kind) { + int deckSize = switch (kind) { + case START -> startTiles.size(); + case NORMAL -> normalTiles.size(); + case MENHIR -> menhirTiles.size(); + }; + return deckSize; + } + + public Tile topTile(Tile.Kind kind) { + Tile topTile = switch (kind) { + case START -> startTiles.getFirst(); + case NORMAL -> normalTiles.getFirst(); + case MENHIR -> menhirTiles.getFirst(); + }; + return topTile; + } + + public TileDecks withTopTileDrawn(Tile.Kind kind) { + } +} diff --git a/src/ch/epfl/chacun/TileSide.java b/src/ch/epfl/chacun/TileSide.java new file mode 100644 index 0000000..f8302af --- /dev/null +++ b/src/ch/epfl/chacun/TileSide.java @@ -0,0 +1,64 @@ +package ch.epfl.chacun; + +import java.util.List; + +/** + * Represents the different possible sides of a tile. + * + * @author Maxence Espagnet (sciper: 372808) + * @author Balthazar Baillat (sciper: 373420) + */ +public sealed interface TileSide { + + /** + * Returns {@code true} if and only if the given edge ({@code that}) + * is of the same kind as the current one ({@code this}). + * + * @param that the edge to compare with the current one + * @return {@code true} if and only if the given edge is of the same kind as the current one + */ + boolean isSameKindAs(TileSide that); + + /** + * Returns the zones that touch the edge represented by the receiver ({@code this}). + * + * @return the zones that touch the current edge + */ + List zones(); + + record Forest(Zone.Forest forest) implements TileSide { + @Override + public boolean isSameKindAs(TileSide that) { + return that instanceof Forest; + } + + @Override + public List zones() { + return List.of(forest); + } + } + + record Meadow(Zone.Meadow meadow) implements TileSide { + @Override + public boolean isSameKindAs(TileSide that) { + return that instanceof Meadow; + } + + @Override + public List zones() { + return List.of(meadow); + } + } + + record River(Zone.Meadow meadow1, Zone.River river, Zone.Meadow meadow2) implements TileSide { + @Override + public boolean isSameKindAs(TileSide that) { + return that instanceof River; + } + + @Override + public List zones() { + return List.of(meadow1, river, meadow2); + } + } +} diff --git a/src/ch/epfl/sigcheck/SignatureChecks_2.java b/src/ch/epfl/sigcheck/SignatureChecks_2.java new file mode 100644 index 0000000..609ab00 --- /dev/null +++ b/src/ch/epfl/sigcheck/SignatureChecks_2.java @@ -0,0 +1,144 @@ +package ch.epfl.sigcheck; + +// Attention : cette classe n'est *pas* un test JUnit, et son code n'est pas +// destiné à être exécuté. Son seul but est de vérifier, autant que possible, +// que les noms et les types des différentes entités à définir pour cette +// étape du projet sont corrects. + +final class SignatureChecks_2 { + private SignatureChecks_2() {} + + void checkTileSide() throws Exception { + v02 = v01.isSameKindAs(v01); + v03 = v01.zones(); + } + + void checkTileSide_River() throws Exception { + v04 = new ch.epfl.chacun.TileSide.River(v05, v06, v05); + v02 = v04.equals(v07); + v08 = v04.hashCode(); + v02 = v04.isSameKindAs(v01); + v05 = v04.meadow1(); + v05 = v04.meadow2(); + v06 = v04.river(); + v09 = v04.toString(); + v03 = v04.zones(); + } + + void checkTileSide_Meadow() throws Exception { + v10 = new ch.epfl.chacun.TileSide.Meadow(v05); + v02 = v10.equals(v07); + v08 = v10.hashCode(); + v02 = v10.isSameKindAs(v01); + v05 = v10.meadow(); + v09 = v10.toString(); + v03 = v10.zones(); + } + + void checkTileSide_Forest() throws Exception { + v11 = new ch.epfl.chacun.TileSide.Forest(v12); + v02 = v11.equals(v07); + v12 = v11.forest(); + v08 = v11.hashCode(); + v02 = v11.isSameKindAs(v01); + v09 = v11.toString(); + v03 = v11.zones(); + } + + void checkTile() throws Exception { + v13 = new ch.epfl.chacun.Tile(v08, v14, v01, v01, v01, v01); + v01 = v13.e(); + v02 = v13.equals(v07); + v08 = v13.hashCode(); + v08 = v13.id(); + v14 = v13.kind(); + v01 = v13.n(); + v01 = v13.s(); + v15 = v13.sideZones(); + v16 = v13.sides(); + v09 = v13.toString(); + v01 = v13.w(); + v15 = v13.zones(); + } + + void checkTile_Kind() throws Exception { + v14 = ch.epfl.chacun.Tile.Kind.MENHIR; + v14 = ch.epfl.chacun.Tile.Kind.NORMAL; + v14 = ch.epfl.chacun.Tile.Kind.START; + v14 = ch.epfl.chacun.Tile.Kind.valueOf(v09); + v17 = ch.epfl.chacun.Tile.Kind.values(); + } + + void checkPlacedTile() throws Exception { + v18 = new ch.epfl.chacun.PlacedTile(v13, v19, v20, v21); + v18 = new ch.epfl.chacun.PlacedTile(v13, v19, v20, v21, v22); + v02 = v18.equals(v07); + v23 = v18.forestZones(); + v08 = v18.hashCode(); + v08 = v18.id(); + v08 = v18.idOfZoneOccupiedBy(v24); + v14 = v18.kind(); + v25 = v18.meadowZones(); + v22 = v18.occupant(); + v19 = v18.placer(); + v21 = v18.pos(); + v26 = v18.potentialOccupants(); + v27 = v18.riverZones(); + v20 = v18.rotation(); + v01 = v18.side(v28); + v29 = v18.specialPowerZone(); + v13 = v18.tile(); + v09 = v18.toString(); + v18 = v18.withNoOccupant(); + v18 = v18.withOccupant(v22); + v29 = v18.zoneWithId(v08); + } + + void checkTileDecks() throws Exception { + v30 = new ch.epfl.chacun.TileDecks(v31, v31, v31); + v08 = v30.deckSize(v14); + v02 = v30.equals(v07); + v08 = v30.hashCode(); + v32 = v30.menhirTiles(); + v32 = v30.normalTiles(); + v32 = v30.startTiles(); + v09 = v30.toString(); + v13 = v30.topTile(v14); + v30 = v30.withTopTileDrawn(v14); + v30 = v30.withTopTileDrawnUntil(v14, v33); + } + + ch.epfl.chacun.TileSide v01; + boolean v02; + java.util.List v03; + ch.epfl.chacun.TileSide.River v04; + ch.epfl.chacun.Zone.Meadow v05; + ch.epfl.chacun.Zone.River v06; + Object v07; + int v08; + String v09; + ch.epfl.chacun.TileSide.Meadow v10; + ch.epfl.chacun.TileSide.Forest v11; + ch.epfl.chacun.Zone.Forest v12; + ch.epfl.chacun.Tile v13; + ch.epfl.chacun.Tile.Kind v14; + java.util.Set v15; + java.util.List v16; + ch.epfl.chacun.Tile.Kind[] v17; + ch.epfl.chacun.PlacedTile v18; + ch.epfl.chacun.PlayerColor v19; + ch.epfl.chacun.Rotation v20; + ch.epfl.chacun.Pos v21; + ch.epfl.chacun.Occupant v22; + java.util.Set v23; + ch.epfl.chacun.Occupant.Kind v24; + java.util.Set v25; + java.util.Set v26; + java.util.Set v27; + ch.epfl.chacun.Direction v28; + ch.epfl.chacun.Zone v29; + ch.epfl.chacun.TileDecks v30; + java.util.List v31; + java.util.List v32; + java.util.function.Predicate v33; +} From bcf85a9769bc98144252cb8748624a26ee92f58f Mon Sep 17 00:00:00 2001 From: balthaB Date: Sat, 24 Feb 2024 13:30:20 +0100 Subject: [PATCH 11/19] implement and comment TileDecks class --- ChaCuN.iml | 16 ++++++ src/ch/epfl/chacun/TileDecks.java | 96 ++++++++++++++++++++++++++++--- 2 files changed, 103 insertions(+), 9 deletions(-) diff --git a/ChaCuN.iml b/ChaCuN.iml index df764dc..87f85cf 100644 --- a/ChaCuN.iml +++ b/ChaCuN.iml @@ -35,5 +35,21 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ch/epfl/chacun/TileDecks.java b/src/ch/epfl/chacun/TileDecks.java index 11804b2..ee6b334 100644 --- a/src/ch/epfl/chacun/TileDecks.java +++ b/src/ch/epfl/chacun/TileDecks.java @@ -1,8 +1,11 @@ package ch.epfl.chacun; import java.util.List; +import java.util.function.Predicate; /** + * Represents the three decks of tiles. + * * @param startTiles * @param normalTiles * @param menhirTiles @@ -11,30 +14,105 @@ */ public record TileDecks(List startTiles, List normalTiles, List menhirTiles) { + /** + * Makes a defensive copy of the tile lists. + * + * @param startTiles + * @param normalTiles + * @param menhirTiles + */ public TileDecks { startTiles = List.copyOf(startTiles); normalTiles = List.copyOf(normalTiles); menhirTiles = List.copyOf(menhirTiles); } + /** + * Returns the size of the deck containing tiles of a given kind. + * + * @param kind the kind of tile + * @return the size of the deck o a given kind + */ public int deckSize(Tile.Kind kind) { - int deckSize = switch (kind) { + return switch (kind) { case START -> startTiles.size(); case NORMAL -> normalTiles.size(); case MENHIR -> menhirTiles.size(); }; - return deckSize; } + /** + * Returns the tile at the top of the deck containing tiles of the given kind, + * or null if the deck is empty. + * + * @param kind the kind of tile + * @return the tile at the top of the deck containing tiles of the given kind, + * or null if the deck is empty + */ public Tile topTile(Tile.Kind kind) { - Tile topTile = switch (kind) { - case START -> startTiles.getFirst(); - case NORMAL -> normalTiles.getFirst(); - case MENHIR -> menhirTiles.getFirst(); - }; - return topTile; + if (this.deckSize(kind) <= 0) { + return null; + } else { + return switch (kind) { + case START -> startTiles.getFirst(); + case NORMAL -> normalTiles.getFirst(); + case MENHIR -> menhirTiles.getFirst(); + }; + } } + /** + * Returns a new triplet of decks after removing from the receiver triplet the top tile of the deck + * containing tiles of a given kind. + * + * @param kind the kind of tile + * @return a new triplet of decks after removing from the receiver triplet the top tile of the deck + * containing tiles of a given kind + * @throws IllegalArgumentException if the receiver deck of the given tile kind is empty + */ public TileDecks withTopTileDrawn(Tile.Kind kind) { + if (this.deckSize(kind) <= 0) { + throw new IllegalArgumentException("The deck of the given tile is empty"); + } + switch (kind) { + case START -> this.startTiles.remove(0); + case NORMAL -> this.normalTiles.remove(0); + case MENHIR -> this.menhirTiles.remove(0); + } + return this; + } + + /** + * Returns a new triplet of decks after testing on the receiver deck containing the given tile kind a + * given predicate using testPredicateOnDeck. + * + * @param kind the kind of tile + * @param predicate the predicate to test + * @return a new triplet of decks after testing a given predicate on the receiver triplet + */ + public TileDecks withTopTileDrawnUntil(Tile.Kind kind, Predicate predicate) { + switch (kind) { + case START -> testPredicateOnDeck(this.startTiles, predicate); + case NORMAL -> testPredicateOnDeck(this.normalTiles, predicate); + case MENHIR -> testPredicateOnDeck(this.menhirTiles, predicate); + } + return this; + } + + /** + * Returns a new deck of tiles after removing the tiles that does not respect a given predicate. + * + * @param deck the deck of tiles + * @param predicate the predicate to check + * @return a new deck of tiles after removing the tiles that does not respect a given predicate + */ + private List testPredicateOnDeck(List deck, Predicate predicate) { + List newdeck = deck; + for (int i = 0; i < newdeck.size(); ++i) { + if (!predicate.test(newdeck.get(i))) { + newdeck.remove(i); + } + } + return newdeck; } -} +} \ No newline at end of file From 4ccf7fc02c06615717fef986c7b0dc6fb5f54b8c Mon Sep 17 00:00:00 2001 From: balthaB Date: Sat, 24 Feb 2024 13:32:05 +0100 Subject: [PATCH 12/19] implement and comment TileDecks class v2 --- src/ch/epfl/chacun/TileDecks.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ch/epfl/chacun/TileDecks.java b/src/ch/epfl/chacun/TileDecks.java index ee6b334..51570ff 100644 --- a/src/ch/epfl/chacun/TileDecks.java +++ b/src/ch/epfl/chacun/TileDecks.java @@ -17,9 +17,9 @@ public record TileDecks(List startTiles, List normalTiles, List Date: Tue, 27 Feb 2024 08:59:20 +0100 Subject: [PATCH 13/19] Add more comments to step 2 --- src/ch/epfl/chacun/PlacedTile.java | 69 +++++++++++++++++++++++++ src/ch/epfl/chacun/Tile.java | 29 +++++++++-- src/ch/epfl/chacun/TileDecks.java | 36 ++++++------- src/ch/epfl/chacun/TileSide.java | 17 ++++++ src/ch/epfl/cs108/Submit.java | 4 +- test/ch/epfl/chacun/PlacedTileTest.java | 8 +++ test/ch/epfl/chacun/TileSide.java | 8 +++ test/ch/epfl/chacun/TileTest.java | 8 +++ 8 files changed, 154 insertions(+), 25 deletions(-) create mode 100644 test/ch/epfl/chacun/PlacedTileTest.java create mode 100644 test/ch/epfl/chacun/TileSide.java create mode 100644 test/ch/epfl/chacun/TileTest.java diff --git a/src/ch/epfl/chacun/PlacedTile.java b/src/ch/epfl/chacun/PlacedTile.java index 81ad5a3..5c9c06f 100644 --- a/src/ch/epfl/chacun/PlacedTile.java +++ b/src/ch/epfl/chacun/PlacedTile.java @@ -41,14 +41,30 @@ public PlacedTile(Tile tile, PlayerColor placer, Rotation rotation, Pos pos) { Objects.requireNonNull(pos); } + /** + * Returns the id of the placed tile. + * + * @return the id of the tile + */ public int id() { return tile.id(); } + /** + * Returns the kind of the tile. + * + * @return the kind of the tile + */ public Tile.Kind kind() { return tile.kind(); } + /** + * Represents the direction of each side of the placed tile considering the rotation. + * + * @param direction the direction of the placed tile + * @return the direction of each side of the placed tile considering the rotation + */ public TileSide side(Direction direction) { Direction rotatedDirection = direction.rotated(rotation); return switch (rotatedDirection) { @@ -59,31 +75,64 @@ public TileSide side(Direction direction) { }; } + /** + * Returns the area of the placed tile whose id is given, or throws IllegalArgumentException + * if the tile has no area with this id. + * + * @param id the id of the placed tile + * @return the area of the placed tile whose id is given + * @throws IllegalArgumentException if the tile has no area with this id + */ public Zone zoneWithId(int id) { return tile.zones().stream().filter(z -> z.id() == id).findFirst() .orElseThrow(() -> new IllegalArgumentException("Unknown zone id: " + id)); } + /** + * Returns the special power zone of the placed tile if any. + * + * @return the special power zone of the placed tile or null if there is none + */ public Zone specialPowerZone() { return tile.zones().stream().filter(z -> z.specialPower() != null).findFirst().orElse(null); } + /** + * Returns all zones present in the tile that have the forest type. + * + * @return all zones present in the tile that have the forest type + */ public Set forestZones() { return tile.zones().stream().filter(Zone.Forest.class::isInstance) .map(Zone.Forest.class::cast).collect(Collectors.toSet()); } + /** + * Returns all zones present in the tile that have the meadow type. + * + * @return all zones present in the tile that have the meadow type + */ public Set meadowZones() { return tile.zones().stream().filter(Zone.Meadow.class::isInstance) .map(Zone.Meadow.class::cast).collect(Collectors.toSet()); } + /** + * Returns all zones present in the tile that have the river type. + * + * @return all zones present in the tile that have the river type + */ public Set riverZones() { return tile.zones().stream().filter(Zone.River.class::isInstance) .map(Zone.River.class::cast).collect(Collectors.toSet()); } + /** + * Returns each potential occupant of each zone of the tile based on the game rules. + * + * @return each potential occupant of each zone of the tile + */ public Set potentialOccupants() { // The origin tile cannot be occupied if (placer == null) { @@ -92,6 +141,7 @@ public Set potentialOccupants() { // Calculate all the potential occupants Set potentialOccupants = new HashSet<>(); for (Zone zone : tile.zones()) { + // A pawn can only be placed on a meadow, a forest or a river Occupant potentialPawn = switch (zone) { case Zone.Meadow m -> new Occupant(Occupant.Kind.PAWN, zone.id()); case Zone.Forest f -> new Occupant(Occupant.Kind.PAWN, zone.id()); @@ -110,6 +160,13 @@ public Set potentialOccupants() { return potentialOccupants; } + /** + * Returns the same tile, with a given occupant added (if there were none). + * + * @param occupant the occupant to add + * @return the same tile, with a given occupant added + * @throws IllegalArgumentException if the tile already has an occupant + */ public PlacedTile withOccupant(Occupant occupant) { if (this.occupant != null) { throw new IllegalArgumentException("Tile already has an occupant"); @@ -117,10 +174,22 @@ public PlacedTile withOccupant(Occupant occupant) { return new PlacedTile(tile, placer, rotation, pos, occupant); } + /** + * Returns the same tile, with no occupant. + * + * @return the same tile, with no occupant + */ public PlacedTile withNoOccupant() { return new PlacedTile(tile, placer, rotation, pos); } + /** + * Returns the id of the zone occupied by the given kind of occupant, or -1 if there is none. + * Based on the game rules, a tile can only have one occupant. + * + * @param occupantKind the kind of the occupant + * @return the id of the zone occupied by the given kind of occupant, or -1 if there is none + */ public int idOfZoneOccupiedBy(Occupant.Kind occupantKind) { if (occupant != null && occupant.kind() == occupantKind) { return occupant.zoneId(); diff --git a/src/ch/epfl/chacun/Tile.java b/src/ch/epfl/chacun/Tile.java index bcae324..7d63a34 100644 --- a/src/ch/epfl/chacun/Tile.java +++ b/src/ch/epfl/chacun/Tile.java @@ -6,21 +6,32 @@ /** * Represents a tile of the game. - * @param id the id of the tile + * + * @param id the id of the tile * @param kind the kind of the tile - * @param n north side of the tile - * @param e east side od^f the tile - * @param s south side of the tile - * @param w west side of the tile + * @param n north side of the tile + * @param e east side od^f the tile + * @param s south side of the tile + * @param w west side of the tile * @author Maxence Espagnet (sciper: 372808) * @author Balthazar Baillat (sciper: 373420) */ public record Tile(int id, Kind kind, TileSide n, TileSide e, TileSide s, TileSide w) { + /** + * Returns the list of all the possible sides of the tile. + * + * @return the list of all the possible sides of the tile + */ public List sides() { return List.of(n, e, s, w); } + /** + * Returns the set of all the zones that touch at least one side of the tile. + * + * @return the set of all the zones that touch at least one side of the tile + */ public Set sideZones() { HashSet zones = new HashSet<>(); zones.addAll(n.zones()); @@ -30,6 +41,11 @@ public Set sideZones() { return zones; } + /** + * Returns the set of all the zones that are contained in the tile. + * + * @return the set of all the zones that are contained in the tile + */ public Set zones() { Set zones = Set.copyOf(sideZones()); for (Zone zone : sideZones()) { @@ -41,6 +57,9 @@ public Set zones() { return zones; } + /** + * Represents the different kinds of tiles. + */ public enum Kind { START, NORMAL, diff --git a/src/ch/epfl/chacun/TileDecks.java b/src/ch/epfl/chacun/TileDecks.java index 51570ff..9d3f75c 100644 --- a/src/ch/epfl/chacun/TileDecks.java +++ b/src/ch/epfl/chacun/TileDecks.java @@ -2,6 +2,7 @@ import java.util.List; import java.util.function.Predicate; +import java.util.stream.Collectors; /** * Represents the three decks of tiles. @@ -17,7 +18,7 @@ public record TileDecks(List startTiles, List normalTiles, List startTiles.getFirst(); - case NORMAL -> normalTiles.getFirst(); - case MENHIR -> menhirTiles.getFirst(); - }; } + return switch (kind) { + case START -> startTiles.getFirst(); + case NORMAL -> normalTiles.getFirst(); + case MENHIR -> menhirTiles.getFirst(); + }; } /** @@ -67,11 +67,11 @@ public Tile topTile(Tile.Kind kind) { * * @param kind the kind of tile * @return a new triplet of decks after removing from the receiver triplet the top tile of the deck - * containing tiles of a given kind + * containing tiles of a given kind * @throws IllegalArgumentException if the receiver deck of the given tile kind is empty */ public TileDecks withTopTileDrawn(Tile.Kind kind) { - if (this.deckSize(kind) <= 0) { + if (deckSize(kind) <= 0) { throw new IllegalArgumentException("The deck of the given tile is empty"); } switch (kind) { @@ -84,9 +84,9 @@ public TileDecks withTopTileDrawn(Tile.Kind kind) { /** * Returns a new triplet of decks after testing on the receiver deck containing the given tile kind a - * given predicate using testPredicateOnDeck. + * given predicate using the {@code testPredicateOnDeck} function. * - * @param kind the kind of tile + * @param kind the kind of tile * @param predicate the predicate to test * @return a new triplet of decks after testing a given predicate on the receiver triplet */ @@ -102,17 +102,17 @@ public TileDecks withTopTileDrawnUntil(Tile.Kind kind, Predicate predicate /** * Returns a new deck of tiles after removing the tiles that does not respect a given predicate. * - * @param deck the deck of tiles + * @param deck the deck of tiles * @param predicate the predicate to check * @return a new deck of tiles after removing the tiles that does not respect a given predicate */ private List testPredicateOnDeck(List deck, Predicate predicate) { - List newdeck = deck; - for (int i = 0; i < newdeck.size(); ++i) { - if (!predicate.test(newdeck.get(i))) { - newdeck.remove(i); + List newDeck = List.copyOf(deck); + for (int i = 0; i < newDeck.size(); ++i) { + if (!predicate.test(newDeck.get(i))) { + newDeck.remove(i); } } - return newdeck; + return newDeck; } } \ No newline at end of file diff --git a/src/ch/epfl/chacun/TileSide.java b/src/ch/epfl/chacun/TileSide.java index f8302af..514bc05 100644 --- a/src/ch/epfl/chacun/TileSide.java +++ b/src/ch/epfl/chacun/TileSide.java @@ -26,6 +26,11 @@ public sealed interface TileSide { */ List zones(); + /** + * Represents a side of a tile that is a forest. + * + * @param forest the forest zone + */ record Forest(Zone.Forest forest) implements TileSide { @Override public boolean isSameKindAs(TileSide that) { @@ -38,6 +43,11 @@ public List zones() { } } + /** + * Represents a side of a tile that is a meadow. + * + * @param meadow the meadow zone + */ record Meadow(Zone.Meadow meadow) implements TileSide { @Override public boolean isSameKindAs(TileSide that) { @@ -50,6 +60,13 @@ public List zones() { } } + /** + * Represents a side which contains a river between two meadows. + * + * @param meadow1 the first meadow zone found clockwise + * @param river the river zone + * @param meadow2 the second meadow zone found clockwise + */ record River(Zone.Meadow meadow1, Zone.River river, Zone.Meadow meadow2) implements TileSide { @Override public boolean isSameKindAs(TileSide that) { diff --git a/src/ch/epfl/cs108/Submit.java b/src/ch/epfl/cs108/Submit.java index 263dcbc..0acd42b 100644 --- a/src/ch/epfl/cs108/Submit.java +++ b/src/ch/epfl/cs108/Submit.java @@ -33,9 +33,9 @@ public final class Submit { // CONFIGURATION // ------------- // Jeton du premier membre du groupe - private static final String TOKEN_1 = ""; + private static final String TOKEN_1 = "boopo3Ah"; // Jeton du second membre (identique au premier pour les personnes travaillant seules) - private static final String TOKEN_2 = ""; + private static final String TOKEN_2 = "coh8ooPh"; // Noms des éventuels fichiers Java additionnels à inclure (p.ex. "MyClass.java") private static final List ADDITIONAL_FILES = List.of(); diff --git a/test/ch/epfl/chacun/PlacedTileTest.java b/test/ch/epfl/chacun/PlacedTileTest.java new file mode 100644 index 0000000..6c07a74 --- /dev/null +++ b/test/ch/epfl/chacun/PlacedTileTest.java @@ -0,0 +1,8 @@ +package ch.epfl.chacun; + +import org.junit.jupiter.api.Test; + +class PlacedTileTest { + + +} diff --git a/test/ch/epfl/chacun/TileSide.java b/test/ch/epfl/chacun/TileSide.java new file mode 100644 index 0000000..605169e --- /dev/null +++ b/test/ch/epfl/chacun/TileSide.java @@ -0,0 +1,8 @@ +package ch.epfl.chacun; + +import org.junit.jupiter.api.Test; + +public class TileSide { + + +} diff --git a/test/ch/epfl/chacun/TileTest.java b/test/ch/epfl/chacun/TileTest.java new file mode 100644 index 0000000..f84dd77 --- /dev/null +++ b/test/ch/epfl/chacun/TileTest.java @@ -0,0 +1,8 @@ +package ch.epfl.chacun; + +import org.junit.jupiter.api.Test; + +public class TileTest { + + +} From 466d0edface94764facc39e744dafdcac9cb3e68 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 27 Feb 2024 09:59:38 +0100 Subject: [PATCH 14/19] WIP tests for step 2 --- src/ch/epfl/chacun/Tile.java | 7 +- test/ch/epfl/chacun/PlacedTileTest.java | 21 ++++++ test/ch/epfl/chacun/TileSide.java | 8 --- test/ch/epfl/chacun/TileSideTest.java | 93 +++++++++++++++++++++++++ test/ch/epfl/chacun/TileTest.java | 59 +++++++++++++++- 5 files changed, 176 insertions(+), 12 deletions(-) delete mode 100644 test/ch/epfl/chacun/TileSide.java create mode 100644 test/ch/epfl/chacun/TileSideTest.java diff --git a/src/ch/epfl/chacun/Tile.java b/src/ch/epfl/chacun/Tile.java index 7d63a34..e3a62f7 100644 --- a/src/ch/epfl/chacun/Tile.java +++ b/src/ch/epfl/chacun/Tile.java @@ -33,7 +33,7 @@ public List sides() { * @return the set of all the zones that touch at least one side of the tile */ public Set sideZones() { - HashSet zones = new HashSet<>(); + Set zones = new HashSet<>(); zones.addAll(n.zones()); zones.addAll(e.zones()); zones.addAll(s.zones()); @@ -47,8 +47,9 @@ public Set sideZones() { * @return the set of all the zones that are contained in the tile */ public Set zones() { - Set zones = Set.copyOf(sideZones()); - for (Zone zone : sideZones()) { + Set sideZones = sideZones(); + Set zones = new HashSet<>(sideZones); + for (Zone zone : sideZones) { if (zone instanceof Zone.River river) { if (river.hasLake()) zones.add(river.lake()); diff --git a/test/ch/epfl/chacun/PlacedTileTest.java b/test/ch/epfl/chacun/PlacedTileTest.java index 6c07a74..5a94ab6 100644 --- a/test/ch/epfl/chacun/PlacedTileTest.java +++ b/test/ch/epfl/chacun/PlacedTileTest.java @@ -2,7 +2,28 @@ import org.junit.jupiter.api.Test; +import static org.junit.Assert.assertThrows; + class PlacedTileTest { + @Test + void placedTileConstructorThrowsOnNullTile() { + assertThrows(NullPointerException.class, () -> new PlacedTile(null, null, Rotation.NONE, Pos.ORIGIN)); + } + + @Test + void placedTileConstructorThrowsOnNullRotation() { + Tile tile = new Tile(0, null, null, null, null, null); + assertThrows(NullPointerException.class, () -> new PlacedTile(tile, null, null, Pos.ORIGIN)); + } + + @Test + void placedTileConstructorThrowsOnNullPos() { + Tile tile = new Tile(0, null, null, null, null, null); + assertThrows(NullPointerException.class, () -> new PlacedTile(tile, null, Rotation.NONE, null)); + } + + @Test + void placedTileContructorDoesntThrowOnNullPlacer() {} } diff --git a/test/ch/epfl/chacun/TileSide.java b/test/ch/epfl/chacun/TileSide.java deleted file mode 100644 index 605169e..0000000 --- a/test/ch/epfl/chacun/TileSide.java +++ /dev/null @@ -1,8 +0,0 @@ -package ch.epfl.chacun; - -import org.junit.jupiter.api.Test; - -public class TileSide { - - -} diff --git a/test/ch/epfl/chacun/TileSideTest.java b/test/ch/epfl/chacun/TileSideTest.java new file mode 100644 index 0000000..2cc9d2d --- /dev/null +++ b/test/ch/epfl/chacun/TileSideTest.java @@ -0,0 +1,93 @@ +package ch.epfl.chacun; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.*; + +public class TileSideTest { + + @Test + void isSameKindDoesntWorkForNull() { + Zone.Forest forest = new Zone.Forest(0, Zone.Forest.Kind.PLAIN); + TileSide.Forest forestSide = new TileSide.Forest(forest); + assertFalse(forestSide.isSameKindAs(null)); + } + + @Test + void isSameKindDoesntWorkForDifferentKinds() { + Zone.Forest forest = new Zone.Forest(0, Zone.Forest.Kind.PLAIN); + Zone.Meadow meadow = new Zone.Meadow(0, new ArrayList<>(), null); + + TileSide.Forest forestSide = new TileSide.Forest(forest); + TileSide.Meadow meadowSide = new TileSide.Meadow(meadow); + + assertFalse(forestSide.isSameKindAs(meadowSide)); + } + + @Test + void isSameKindWorksForForest() { + Zone.Forest forest1 = new Zone.Forest(0, Zone.Forest.Kind.PLAIN); + Zone.Forest forest2 = new Zone.Forest(1, Zone.Forest.Kind.WITH_MENHIR); + + TileSide.Forest forestSide1 = new TileSide.Forest(forest1); + TileSide.Forest forestSide2 = new TileSide.Forest(forest2); + + assertTrue(forestSide1.isSameKindAs(forestSide2)); + } + + @Test + void isSameKindWorksForMeadow() { + Zone.Meadow meadow1 = new Zone.Meadow(0, new ArrayList<>(), null); + Zone.Meadow meadow2 = new Zone.Meadow(1, new ArrayList<>(), null); + + TileSide.Meadow meadowSide1 = new TileSide.Meadow(meadow1); + TileSide.Meadow meadowSide2 = new TileSide.Meadow(meadow2); + + assertTrue(meadowSide1.isSameKindAs(meadowSide2)); + } + + @Test + void isSameKindWorksForRiver() { + Zone.Meadow meadow1 = new Zone.Meadow(0, new ArrayList<>(), null); + Zone.River river1 = new Zone.River(0, 9, null); + Zone.River river2 = new Zone.River(0, 6, null); + Zone.Meadow meadow2 = new Zone.Meadow(0, new ArrayList<>(), null); + + TileSide.River riverSide1 = new TileSide.River(meadow1, river1, meadow2); + TileSide.River riverSide2 = new TileSide.River(meadow2, river2, meadow1); + + assertTrue(riverSide1.isSameKindAs(riverSide2)); + } + + @Test + void zonesWorksForForest() { + Zone.Forest forest = new Zone.Forest(0, Zone.Forest.Kind.PLAIN); + TileSide.Forest forestSide = new TileSide.Forest(forest); + List expectedZones = List.of(forest); + + assertEquals(expectedZones, forestSide.zones()); + } + + @Test + void zonesWorksForMeadows() { + Zone.Meadow meadow1 = new Zone.Meadow(0, new ArrayList<>(), null); + TileSide.Meadow meadowSide = new TileSide.Meadow(meadow1); + List expectedZones = List.of(meadow1); + + assertEquals(expectedZones, meadowSide.zones()); + } + @Test + void zonesWorksForRiver() { + Zone.Meadow meadow1 = new Zone.Meadow(0, new ArrayList<>(), null); + Zone.River river = new Zone.River(1, 9, null); + Zone.Meadow meadow2 = new Zone.Meadow(2, new ArrayList<>(), null); + + List expectedZones = List.of(meadow1, river, meadow2); + TileSide.River riverSide = new TileSide.River(meadow1, river, meadow2); + + assertEquals(expectedZones, riverSide.zones()); + } +} diff --git a/test/ch/epfl/chacun/TileTest.java b/test/ch/epfl/chacun/TileTest.java index f84dd77..7ce9d45 100644 --- a/test/ch/epfl/chacun/TileTest.java +++ b/test/ch/epfl/chacun/TileTest.java @@ -2,7 +2,64 @@ import org.junit.jupiter.api.Test; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + public class TileTest { - + + @Test + void sidesWorksWithAllSidesProvided() { + Zone.Forest forest = new Zone.Forest(1, Zone.Forest.Kind.PLAIN); + Zone.Meadow meadow1 = new Zone.Meadow(2, new ArrayList<>(), null); + Zone.River river1 = new Zone.River(3, 9, null); + Zone.Meadow meadow2 = new Zone.Meadow(4, new ArrayList<>(), null); + + TileSide.Forest forestSide = new TileSide.Forest(forest); + TileSide.Meadow meadowSide1 = new TileSide.Meadow(meadow1); + TileSide.River riverSide = new TileSide.River(meadow1, river1, meadow2); + TileSide.Meadow meadowSide2 = new TileSide.Meadow(meadow2); + Tile tile = new Tile(0, null, forestSide, meadowSide1, riverSide, meadowSide2); + + List expectedSides = List.of(forestSide, meadowSide1, riverSide, meadowSide2); + assertEquals(expectedSides, tile.sides()); + } + + @Test + void sideZonesWorksWithRiverAndOtherZones() { + Zone.Forest forest = new Zone.Forest(1, Zone.Forest.Kind.PLAIN); + Zone.Meadow meadow1 = new Zone.Meadow(2, new ArrayList<>(), null); + Zone.River river1 = new Zone.River(3, 9, null); + Zone.Meadow meadow2 = new Zone.Meadow(4, new ArrayList<>(), null); + + TileSide.Forest forestSide = new TileSide.Forest(forest); + TileSide.Meadow meadowSide1 = new TileSide.Meadow(meadow1); + TileSide.River riverSide = new TileSide.River(meadow1, river1, meadow2); + TileSide.Meadow meadowSide2 = new TileSide.Meadow(meadow2); + Tile tile = new Tile(0, null, forestSide, meadowSide1, riverSide, meadowSide2); + + Set expectedSideZones = Set.of(forest, meadow1, river1, meadow2); + assertEquals(expectedSideZones, tile.sideZones()); + } + + @Test + void zonesWorksWithLake() { + Zone.Forest forest = new Zone.Forest(1, Zone.Forest.Kind.PLAIN); + Zone.Meadow meadow1 = new Zone.Meadow(2, new ArrayList<>(), null); + Zone.Lake lake = new Zone.Lake(3, 9, null); + Zone.River river1 = new Zone.River(3, 9, lake); + Zone.Meadow meadow2 = new Zone.Meadow(4, new ArrayList<>(), null); + + TileSide.Forest forestSide = new TileSide.Forest(forest); + TileSide.Meadow meadowSide1 = new TileSide.Meadow(meadow1); + TileSide.River riverSideWithLake = new TileSide.River(meadow1, river1, meadow2); + TileSide.Meadow meadowSide2 = new TileSide.Meadow(meadow2); + Tile tile = new Tile(0, null, forestSide, meadowSide1, riverSideWithLake, meadowSide2); + + Set expectedZones = Set.of(forest, meadow1, river1, lake, meadow2); + assertEquals(expectedZones, tile.zones()); + } } From e1c61de84e8cdc44b192785fbe449945da28af33 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 27 Feb 2024 13:46:41 +0100 Subject: [PATCH 15/19] Make TileDecks truly immutable --- src/ch/epfl/chacun/TileDecks.java | 46 ++++++++++++++++++------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/ch/epfl/chacun/TileDecks.java b/src/ch/epfl/chacun/TileDecks.java index 9d3f75c..4ecd9dc 100644 --- a/src/ch/epfl/chacun/TileDecks.java +++ b/src/ch/epfl/chacun/TileDecks.java @@ -2,7 +2,6 @@ import java.util.List; import java.util.function.Predicate; -import java.util.stream.Collectors; /** * Represents the three decks of tiles. @@ -14,7 +13,6 @@ * @author Balthazar Baillat (sciper: 373420) */ public record TileDecks(List startTiles, List normalTiles, List menhirTiles) { - /** * Makes a defensive copy of the tile lists. * @@ -72,14 +70,21 @@ public Tile topTile(Tile.Kind kind) { */ public TileDecks withTopTileDrawn(Tile.Kind kind) { if (deckSize(kind) <= 0) { - throw new IllegalArgumentException("The deck of the given tile is empty"); + throw new IllegalArgumentException("The deck of the given tile kind is empty"); } + + // Copy to prevent modification of the original list since this class is immutable + List startTiles = List.copyOf(this.startTiles); + List normalTiles = List.copyOf(this.normalTiles); + List menhirTiles = List.copyOf(this.menhirTiles); + switch (kind) { - case START -> this.startTiles.remove(0); - case NORMAL -> this.normalTiles.remove(0); - case MENHIR -> this.menhirTiles.remove(0); + case START -> startTiles.removeFirst(); + case NORMAL -> normalTiles.removeFirst(); + case MENHIR -> menhirTiles.removeFirst(); } - return this; + + return new TileDecks(startTiles, normalTiles, menhirTiles); } /** @@ -91,28 +96,31 @@ public TileDecks withTopTileDrawn(Tile.Kind kind) { * @return a new triplet of decks after testing a given predicate on the receiver triplet */ public TileDecks withTopTileDrawnUntil(Tile.Kind kind, Predicate predicate) { + // Copy to prevent modification of the original list since this class is immutable + List startTiles = List.copyOf(this.startTiles); + List normalTiles = List.copyOf(this.normalTiles); + List menhirTiles = List.copyOf(this.menhirTiles); + switch (kind) { - case START -> testPredicateOnDeck(this.startTiles, predicate); - case NORMAL -> testPredicateOnDeck(this.normalTiles, predicate); - case MENHIR -> testPredicateOnDeck(this.menhirTiles, predicate); + case START -> filterDeckUsingPredicate(startTiles, predicate); + case NORMAL -> filterDeckUsingPredicate(normalTiles, predicate); + case MENHIR -> filterDeckUsingPredicate(menhirTiles, predicate); } - return this; + + return new TileDecks(startTiles, normalTiles, menhirTiles); } /** - * Returns a new deck of tiles after removing the tiles that does not respect a given predicate. + * Modify a deck of tiles by removing the tiles that does not respect a given predicate. * * @param deck the deck of tiles * @param predicate the predicate to check - * @return a new deck of tiles after removing the tiles that does not respect a given predicate */ - private List testPredicateOnDeck(List deck, Predicate predicate) { - List newDeck = List.copyOf(deck); - for (int i = 0; i < newDeck.size(); ++i) { - if (!predicate.test(newDeck.get(i))) { - newDeck.remove(i); + private void filterDeckUsingPredicate(List deck, Predicate predicate) { + for (int i = 0; i < deck.size(); ++i) { + if (!predicate.test(deck.get(i))) { + deck.remove(i); } } - return newDeck; } } \ No newline at end of file From 9e71ef9c3a8ceb81ff509ba1a309b01110277c61 Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 27 Feb 2024 21:14:27 +0100 Subject: [PATCH 16/19] Better TileDecks.withTopTileDrawn and TileDecks.withTopTileDrawnUntil --- src/ch/epfl/chacun/TileDecks.java | 57 +++++++++++++++---------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/src/ch/epfl/chacun/TileDecks.java b/src/ch/epfl/chacun/TileDecks.java index 4ecd9dc..aeb13b5 100644 --- a/src/ch/epfl/chacun/TileDecks.java +++ b/src/ch/epfl/chacun/TileDecks.java @@ -1,5 +1,6 @@ package ch.epfl.chacun; +import java.util.ArrayList; import java.util.List; import java.util.function.Predicate; @@ -73,18 +74,11 @@ public TileDecks withTopTileDrawn(Tile.Kind kind) { throw new IllegalArgumentException("The deck of the given tile kind is empty"); } - // Copy to prevent modification of the original list since this class is immutable - List startTiles = List.copyOf(this.startTiles); - List normalTiles = List.copyOf(this.normalTiles); - List menhirTiles = List.copyOf(this.menhirTiles); - - switch (kind) { - case START -> startTiles.removeFirst(); - case NORMAL -> normalTiles.removeFirst(); - case MENHIR -> menhirTiles.removeFirst(); - } - - return new TileDecks(startTiles, normalTiles, menhirTiles); + return switch (kind) { + case START -> new TileDecks(removeDeckFirstTile(startTiles), normalTiles, menhirTiles); + case NORMAL -> new TileDecks(startTiles, removeDeckFirstTile(normalTiles), menhirTiles); + case MENHIR -> new TileDecks(startTiles, normalTiles, removeDeckFirstTile(menhirTiles)); + }; } /** @@ -96,18 +90,11 @@ public TileDecks withTopTileDrawn(Tile.Kind kind) { * @return a new triplet of decks after testing a given predicate on the receiver triplet */ public TileDecks withTopTileDrawnUntil(Tile.Kind kind, Predicate predicate) { - // Copy to prevent modification of the original list since this class is immutable - List startTiles = List.copyOf(this.startTiles); - List normalTiles = List.copyOf(this.normalTiles); - List menhirTiles = List.copyOf(this.menhirTiles); - - switch (kind) { - case START -> filterDeckUsingPredicate(startTiles, predicate); - case NORMAL -> filterDeckUsingPredicate(normalTiles, predicate); - case MENHIR -> filterDeckUsingPredicate(menhirTiles, predicate); - } - - return new TileDecks(startTiles, normalTiles, menhirTiles); + return switch (kind) { + case START -> new TileDecks(filterDeck(startTiles, predicate), normalTiles, menhirTiles); + case NORMAL -> new TileDecks(startTiles, filterDeck(normalTiles, predicate), menhirTiles); + case MENHIR -> new TileDecks(startTiles, normalTiles, filterDeck(menhirTiles, predicate)); + }; } /** @@ -115,12 +102,24 @@ public TileDecks withTopTileDrawnUntil(Tile.Kind kind, Predicate predicate * * @param deck the deck of tiles * @param predicate the predicate to check + * @return a new deck of tiles after removing the tiles that does not respect the given predicate */ - private void filterDeckUsingPredicate(List deck, Predicate predicate) { - for (int i = 0; i < deck.size(); ++i) { - if (!predicate.test(deck.get(i))) { - deck.remove(i); - } + private List filterDeck(List deck, Predicate predicate) { + List filteredDeck = new ArrayList<>(); + for (Tile tile : deck) { + if (predicate.test(tile)) + filteredDeck.add(tile); } + return filteredDeck; + } + + /** + * Removes the first tile from a given deck of tiles. + * + * @param deck the deck of tiles + * @return a new deck that excludes the first tile from the original deck + */ + private List removeDeckFirstTile(List deck) { + return deck.subList(1, deck.size()); } } \ No newline at end of file From ecdabc94d0209cef8440356d4514b637d848e731 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 28 Feb 2024 13:11:56 +0100 Subject: [PATCH 17/19] Fix TileDecks --- src/ch/epfl/chacun/TileDecks.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/ch/epfl/chacun/TileDecks.java b/src/ch/epfl/chacun/TileDecks.java index aeb13b5..943ee37 100644 --- a/src/ch/epfl/chacun/TileDecks.java +++ b/src/ch/epfl/chacun/TileDecks.java @@ -50,10 +50,7 @@ public int deckSize(Tile.Kind kind) { * or null if the deck is empty */ public Tile topTile(Tile.Kind kind) { - if (deckSize(kind) <= 0) { - return null; - } - return switch (kind) { + return deckSize(kind) <= 0 ? null : switch (kind) { case START -> startTiles.getFirst(); case NORMAL -> normalTiles.getFirst(); case MENHIR -> menhirTiles.getFirst(); @@ -105,10 +102,9 @@ public TileDecks withTopTileDrawnUntil(Tile.Kind kind, Predicate predicate * @return a new deck of tiles after removing the tiles that does not respect the given predicate */ private List filterDeck(List deck, Predicate predicate) { - List filteredDeck = new ArrayList<>(); - for (Tile tile : deck) { - if (predicate.test(tile)) - filteredDeck.add(tile); + List filteredDeck = List.copyOf(deck); + while (!filteredDeck.isEmpty() && !predicate.test(filteredDeck.getFirst())) { + filteredDeck = removeDeckFirstTile(filteredDeck); } return filteredDeck; } From dcb44f85c69b1912f4d47d3e875524872c7c786e Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 1 Mar 2024 13:15:29 +0100 Subject: [PATCH 18/19] Add more tests --- src/ch/epfl/chacun/PlacedTile.java | 39 +++---- test/ch/epfl/chacun/PlacedTileTest.java | 149 +++++++++++++++++++++++- test/ch/epfl/chacun/TileDecksTest.java | 99 ++++++++++++++++ test/ch/epfl/chacun/TileSideTest.java | 10 +- 4 files changed, 267 insertions(+), 30 deletions(-) create mode 100644 test/ch/epfl/chacun/TileDecksTest.java diff --git a/src/ch/epfl/chacun/PlacedTile.java b/src/ch/epfl/chacun/PlacedTile.java index 5c9c06f..4b59b7f 100644 --- a/src/ch/epfl/chacun/PlacedTile.java +++ b/src/ch/epfl/chacun/PlacedTile.java @@ -39,6 +39,11 @@ public PlacedTile(Tile tile, PlayerColor placer, Rotation rotation, Pos pos) { Objects.requireNonNull(tile); Objects.requireNonNull(rotation); Objects.requireNonNull(pos); + + // Prevent a null placer + if (placer == null && !pos.equals(Pos.ORIGIN)) { + throw new IllegalArgumentException("A tile must have a placer except for the origin."); + } } /** @@ -66,7 +71,7 @@ public Tile.Kind kind() { * @return the direction of each side of the placed tile considering the rotation */ public TileSide side(Direction direction) { - Direction rotatedDirection = direction.rotated(rotation); + Direction rotatedDirection = direction.rotated(rotation.negated()); return switch (rotatedDirection) { case N -> tile.n(); case E -> tile.e(); @@ -95,7 +100,6 @@ public Zone zoneWithId(int id) { */ public Zone specialPowerZone() { return tile.zones().stream().filter(z -> z.specialPower() != null).findFirst().orElse(null); - } /** @@ -134,27 +138,20 @@ public Set riverZones() { * @return each potential occupant of each zone of the tile */ public Set potentialOccupants() { - // The origin tile cannot be occupied - if (placer == null) { - return null; - } // Calculate all the potential occupants Set potentialOccupants = new HashSet<>(); - for (Zone zone : tile.zones()) { - // A pawn can only be placed on a meadow, a forest or a river - Occupant potentialPawn = switch (zone) { - case Zone.Meadow m -> new Occupant(Occupant.Kind.PAWN, zone.id()); - case Zone.Forest f -> new Occupant(Occupant.Kind.PAWN, zone.id()); - case Zone.River r -> new Occupant(Occupant.Kind.PAWN, zone.id()); - default -> null; - }; - // A HUT can only be placed on a river with at least one lake - if (zone instanceof Zone.River river && river.hasLake()) { - potentialOccupants.add(new Occupant(Occupant.Kind.HUT, zone.id())); - } - // Add the potential pawn - if (potentialPawn != null) { - potentialOccupants.add(potentialPawn); + // The origin tile cannot be occupied + if (placer != null) { + for (Zone zone : tile.zones()) { + // A pawn can only be placed on a meadow, a forest or a river + if (!(zone instanceof Zone.River)) { + potentialOccupants.add(new Occupant(Occupant.Kind.PAWN, zone.id())); + } + // A hut can only be placed on a lake if it is connected to a river + // or on a river if there's no lake + if (zone instanceof Zone.Lake || (zone instanceof Zone.River river && river.hasLake())) { + potentialOccupants.add(new Occupant(Occupant.Kind.HUT, zone.id())); + } } } return potentialOccupants; diff --git a/test/ch/epfl/chacun/PlacedTileTest.java b/test/ch/epfl/chacun/PlacedTileTest.java index 5a94ab6..14ec483 100644 --- a/test/ch/epfl/chacun/PlacedTileTest.java +++ b/test/ch/epfl/chacun/PlacedTileTest.java @@ -2,7 +2,13 @@ import org.junit.jupiter.api.Test; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + import static org.junit.Assert.assertThrows; +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; class PlacedTileTest { @@ -24,6 +30,147 @@ void placedTileConstructorThrowsOnNullPos() { } @Test - void placedTileContructorDoesntThrowOnNullPlacer() {} + void placedTileConstructorDoesntThrowOnNullPlacer() { + Tile tile = new Tile(0, null, null, null, null, null); + assertDoesNotThrow(() -> new PlacedTile(tile, null, Rotation.NONE, Pos.ORIGIN)); + } + + @Test + void placedTileSideWorks() { + TileSide north = new TileSide.River(null, null, null); + TileSide east = new TileSide.Forest(new Zone.Forest(0, Zone.Forest.Kind.PLAIN)); + TileSide south = new TileSide.Forest(new Zone.Forest(1, Zone.Forest.Kind.WITH_MENHIR)); + TileSide west = new TileSide.Meadow(null); + + List ALL = List.of(north, east, south, west); + Tile tile = new Tile(0, null, north, east, south, west); + for (int i = 0; i < 4; i++) { + Direction direction = Direction.ALL.get(i); + for (int j = 0; j < 4; j++) { + Rotation rotation = Rotation.ALL.get(j); + PlacedTile placedTile = new PlacedTile(tile, null, rotation, Pos.ORIGIN); + assertEquals(ALL.get(direction.rotated(rotation.negated()).ordinal()), placedTile.side(direction)); + } + } + } + + @Test + void zoneWithIdWorks() { + Zone.Forest forest1 = new Zone.Forest(0, Zone.Forest.Kind.PLAIN); + Zone.Forest forest2 = new Zone.Forest(1, Zone.Forest.Kind.WITH_MENHIR); + Zone.Meadow meadow1 = new Zone.Meadow(2, List.of(new Animal(2, Animal.Kind.AUROCHS)), Zone.Meadow.SpecialPower.HUNTING_TRAP); + Zone.River river = new Zone.River(3, 9, null); + Zone.Meadow meadow2 = new Zone.Meadow(4, List.of(new Animal(2, Animal.Kind.AUROCHS)), Zone.Meadow.SpecialPower.HUNTING_TRAP); + + TileSide north = new TileSide.Meadow(meadow1); + TileSide east = new TileSide.Forest(forest1); + TileSide south = new TileSide.River(meadow1, river, meadow2); + TileSide west = new TileSide.Forest(forest2); + Tile tile = new Tile(0, null, north, east, south, west); + PlacedTile placedTile = new PlacedTile(tile, null, Rotation.NONE, Pos.ORIGIN); + + assertEquals(forest1, placedTile.zoneWithId(0)); + assertEquals(forest2, placedTile.zoneWithId(1)); + assertEquals(meadow1, placedTile.zoneWithId(2)); + assertEquals(river, placedTile.zoneWithId(3)); + assertEquals(meadow2, placedTile.zoneWithId(4)); + } + + @Test + void zoneWithIdThrowsOnUnknownId() { + Zone.Forest forest1 = new Zone.Forest(0, Zone.Forest.Kind.PLAIN); + Zone.Forest forest2 = new Zone.Forest(1, Zone.Forest.Kind.WITH_MENHIR); + Zone.Meadow meadow1 = new Zone.Meadow(2, List.of(new Animal(2, Animal.Kind.AUROCHS)), Zone.Meadow.SpecialPower.HUNTING_TRAP); + Zone.River river = new Zone.River(3, 9, null); + Zone.Meadow meadow2 = new Zone.Meadow(4, List.of(new Animal(2, Animal.Kind.AUROCHS)), Zone.Meadow.SpecialPower.HUNTING_TRAP); + + TileSide north = new TileSide.Meadow(meadow1); + TileSide east = new TileSide.Forest(forest1); + TileSide south = new TileSide.River(meadow1, river, meadow2); + TileSide west = new TileSide.Forest(forest2); + Tile tile = new Tile(0, null, north, east, south, west); + PlacedTile placedTile = new PlacedTile(tile, null, Rotation.NONE, Pos.ORIGIN); + + assertThrows(IllegalArgumentException.class, () -> placedTile.zoneWithId(10)); + } + + @Test + public void testPotentialOccupantsReturnsCorrectValue() { + Zone.Meadow meadow = new Zone.Meadow(613, List.of(new Animal(6131, Animal.Kind.AUROCHS)), Zone.Meadow.SpecialPower.HUNTING_TRAP); + Zone.Meadow meadow2 = new Zone.Meadow(614, List.of(new Animal(6141, Animal.Kind.MAMMOTH)), null); + Zone.Forest forest2 = new Zone.Forest(615, Zone.Forest.Kind.PLAIN); + Zone.Forest forest = new Zone.Forest(612, Zone.Forest.Kind.WITH_MENHIR); + TileSide forestSide = new TileSide.Forest(forest); + TileSide meadowSide = new TileSide.Meadow(meadow); + TileSide forestSide2 = new TileSide.Forest(forest2); + TileSide meadowSide2 = new TileSide.Meadow(meadow2); + Tile tile = new Tile(1, Tile.Kind.NORMAL, forestSide, meadowSide, forestSide2, meadowSide2); + PlayerColor Habib = PlayerColor.RED; + + PlacedTile placedTile = new PlacedTile(tile, Habib, Rotation.RIGHT, new Pos(0, 0)); + + Set set = new HashSet<>(); + set.add(new Occupant(Occupant.Kind.PAWN, 613)); + set.add(new Occupant(Occupant.Kind.PAWN, 614)); + set.add(new Occupant(Occupant.Kind.PAWN, 615)); + set.add(new Occupant(Occupant.Kind.PAWN, 612)); + + assertEquals(set, placedTile.potentialOccupants()); + + PlacedTile placedTile2 = new PlacedTile(tile, null, Rotation.RIGHT, new Pos(0, 0)); + + assertEquals(new HashSet<>(), placedTile2.potentialOccupants()); + + Zone.River river = new Zone.River(623, 3, null); + Zone.Lake lake = new Zone.Lake(628, 0, Zone.SpecialPower.LOGBOAT); + Zone.River river2 = new Zone.River(624, 2, lake); + + TileSide riverSide1 = new TileSide.River(meadow, river, meadow2); + TileSide riverSide2 = new TileSide.River(meadow2, river2, meadow); + + Tile tile2 = new Tile(1, Tile.Kind.NORMAL, forestSide, riverSide1, riverSide2, meadowSide2); + PlacedTile placedTile3 = new PlacedTile(tile2, Habib, Rotation.RIGHT, new Pos(0, 0)); + + Set set2 = new HashSet<>(); + set2.add(new Occupant(Occupant.Kind.PAWN, 623)); + set2.add(new Occupant(Occupant.Kind.PAWN, 624)); + set2.add(new Occupant(Occupant.Kind.HUT, 628)); + set2.add(new Occupant(Occupant.Kind.HUT, 623)); + + set2.add(new Occupant(Occupant.Kind.PAWN, 613)); + set2.add(new Occupant(Occupant.Kind.PAWN, 614)); + set2.add(new Occupant(Occupant.Kind.PAWN, 612)); + + assertEquals(set2, placedTile3.potentialOccupants()); + } + + @Test + public void testWithOccupantWorks() { + Zone.Meadow meadow = new Zone.Meadow(613, List.of(new Animal(6131, Animal.Kind.AUROCHS)), Zone.Meadow.SpecialPower.HUNTING_TRAP); + Zone.Meadow meadow2 = new Zone.Meadow(614, List.of(new Animal(6141, Animal.Kind.MAMMOTH)), null); + Zone.Forest forest2 = new Zone.Forest(615, Zone.Forest.Kind.PLAIN); + Zone.Forest forest = new Zone.Forest(612, Zone.Forest.Kind.WITH_MENHIR); + TileSide forestSide = new TileSide.Forest(forest); + TileSide meadowSide = new TileSide.Meadow(meadow); + TileSide forestSide2 = new TileSide.Forest(forest2); + TileSide meadowSide2 = new TileSide.Meadow(meadow2); + Tile tile = new Tile(1, Tile.Kind.NORMAL, forestSide, meadowSide, forestSide2, meadowSide2); + PlayerColor Habib = PlayerColor.RED; + + PlacedTile placedTile = new PlacedTile(tile, Habib, Rotation.RIGHT, new Pos(0, 0)); + + Occupant occupant = new Occupant(Occupant.Kind.PAWN, 613); + assertThrows(IllegalArgumentException.class, () -> { + PlacedTile withOccupant = placedTile.withOccupant(occupant); + withOccupant.withOccupant(new Occupant(Occupant.Kind.PAWN, 613)); + }); + + PlacedTile withOccupant = placedTile.withOccupant(new Occupant(Occupant.Kind.PAWN, 613)); + assertEquals(occupant, withOccupant.occupant()); + + assertEquals(613, withOccupant.idOfZoneOccupiedBy(Occupant.Kind.PAWN)); + assertEquals(-1, withOccupant.idOfZoneOccupiedBy(Occupant.Kind.HUT)); + + } } diff --git a/test/ch/epfl/chacun/TileDecksTest.java b/test/ch/epfl/chacun/TileDecksTest.java new file mode 100644 index 0000000..4f5746f --- /dev/null +++ b/test/ch/epfl/chacun/TileDecksTest.java @@ -0,0 +1,99 @@ +package ch.epfl.chacun; + +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertThrows; + +public class TileDecksTest { + @Test + void deckSizeWorks(){ + List startTiles = new ArrayList<>(1); + List normalTiles = new ArrayList<>(1); + List menhirTiles = new ArrayList<>(1); + + startTiles.add(new Tile(0, Tile.Kind.START, null, null, null, null)); + normalTiles.add(new Tile(1, Tile.Kind.NORMAL, null, null, null, null)); + menhirTiles.add(new Tile(2, Tile.Kind.MENHIR, null, null, null, null)); + + TileDecks tileDecks = new TileDecks(startTiles, normalTiles, menhirTiles); + + assertEquals(startTiles.size(), tileDecks.deckSize(Tile.Kind.START)); + assertEquals(normalTiles.size(), tileDecks.deckSize(Tile.Kind.NORMAL)); + assertEquals(menhirTiles.size(), tileDecks.deckSize(Tile.Kind.MENHIR)); + + } + + @Test + void topTileReturnsNullOnEmptyDeck(){ + List startTiles = new ArrayList<>(1); + List normalTiles = new ArrayList<>(1); + List menhirTiles = new ArrayList<>(1); + + TileDecks tileDecks = new TileDecks(startTiles, normalTiles, menhirTiles); + + assertEquals(null, tileDecks.topTile(Tile.Kind.START)); + assertEquals(null, tileDecks.topTile(Tile.Kind.NORMAL)); + assertEquals(null, tileDecks.topTile(Tile.Kind.MENHIR)); + } + + @Test + void topTileWorks(){ + List startTiles = new ArrayList<>(1); + List normalTiles = new ArrayList<>(1); + List menhirTiles = new ArrayList<>(1); + + startTiles.add(new Tile(0, Tile.Kind.START, null, null, null, null)); + normalTiles.add(new Tile(1, Tile.Kind.NORMAL, null, null, null, null)); + menhirTiles.add(new Tile(2, Tile.Kind.MENHIR, null, null, null, null)); + + TileDecks tileDecks = new TileDecks(startTiles, normalTiles, menhirTiles); + + assertEquals(startTiles.getFirst().id(), tileDecks.topTile(Tile.Kind.START).id()); + assertEquals(normalTiles.getFirst().id(), tileDecks.topTile(Tile.Kind.NORMAL).id()); + assertEquals(menhirTiles.getFirst().id(), tileDecks.topTile(Tile.Kind.MENHIR).id()); + } + + @Test + void withTopTileDrawnThrowsOnEmptyDeck(){ + List startTiles = new ArrayList<>(1); + List normalTiles = new ArrayList<>(1); + List menhirTiles = new ArrayList<>(1); + + TileDecks tileDecks = new TileDecks(startTiles, normalTiles, menhirTiles); + + assertThrows(IllegalArgumentException.class, () -> tileDecks.withTopTileDrawn(Tile.Kind.START)); + assertThrows(IllegalArgumentException.class, () -> tileDecks.withTopTileDrawn(Tile.Kind.NORMAL)); + assertThrows(IllegalArgumentException.class, () -> tileDecks.withTopTileDrawn(Tile.Kind.MENHIR)); + } + + @Test + void withTopTileWorks(){ + List startTiles1 = new ArrayList<>(1); + List normalTiles1 = new ArrayList<>(1); + List menhirTiles1 = new ArrayList<>(1); + + List startTiles2 = new ArrayList<>(1); + List normalTiles2 = new ArrayList<>(1); + List menhirTiles2 = new ArrayList<>(1); + + startTiles1.add(new Tile(0, Tile.Kind.START, null, null, null, null)); + normalTiles1.add(new Tile(1, Tile.Kind.NORMAL, null, null, null, null)); + menhirTiles1.add(new Tile(2, Tile.Kind.MENHIR, null, null, null, null)); + + startTiles1.add(new Tile(3, Tile.Kind.START, null, null, null, null)); + normalTiles1.add(new Tile(4, Tile.Kind.NORMAL, null, null, null, null)); + menhirTiles1.add(new Tile(5, Tile.Kind.MENHIR, null, null, null, null)); + + TileDecks tileDecks1 = new TileDecks(startTiles1, normalTiles1, menhirTiles1); + TileDecks tileDecks2 = new TileDecks(startTiles2, normalTiles2, menhirTiles2); + + assertEquals(tileDecks2.deckSize(Tile.Kind.START), tileDecks1.withTopTileDrawn(Tile.Kind.START) + .deckSize(Tile.Kind.START)); + assertEquals(tileDecks2.deckSize(Tile.Kind.START), tileDecks1.withTopTileDrawn(Tile.Kind.START) + .deckSize(Tile.Kind.START)); + } +} diff --git a/test/ch/epfl/chacun/TileSideTest.java b/test/ch/epfl/chacun/TileSideTest.java index 2cc9d2d..76a9c07 100644 --- a/test/ch/epfl/chacun/TileSideTest.java +++ b/test/ch/epfl/chacun/TileSideTest.java @@ -1,21 +1,15 @@ package ch.epfl.chacun; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.jupiter.api.Assertions.*; public class TileSideTest { - @Test - void isSameKindDoesntWorkForNull() { - Zone.Forest forest = new Zone.Forest(0, Zone.Forest.Kind.PLAIN); - TileSide.Forest forestSide = new TileSide.Forest(forest); - assertFalse(forestSide.isSameKindAs(null)); - } - @Test void isSameKindDoesntWorkForDifferentKinds() { Zone.Forest forest = new Zone.Forest(0, Zone.Forest.Kind.PLAIN); From bc318783fc855a61dec54ebd8b3a43182a49c188 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 1 Mar 2024 13:26:52 +0100 Subject: [PATCH 19/19] Add more tests and fix potentialOccupants --- src/ch/epfl/chacun/PlacedTile.java | 4 ++-- test/ch/epfl/chacun/TileDecksTest.java | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/ch/epfl/chacun/PlacedTile.java b/src/ch/epfl/chacun/PlacedTile.java index 4b59b7f..2d66f1d 100644 --- a/src/ch/epfl/chacun/PlacedTile.java +++ b/src/ch/epfl/chacun/PlacedTile.java @@ -144,12 +144,12 @@ public Set potentialOccupants() { if (placer != null) { for (Zone zone : tile.zones()) { // A pawn can only be placed on a meadow, a forest or a river - if (!(zone instanceof Zone.River)) { + if (!(zone instanceof Zone.Lake)) { potentialOccupants.add(new Occupant(Occupant.Kind.PAWN, zone.id())); } // A hut can only be placed on a lake if it is connected to a river // or on a river if there's no lake - if (zone instanceof Zone.Lake || (zone instanceof Zone.River river && river.hasLake())) { + if (zone instanceof Zone.Lake || (zone instanceof Zone.River river && !river.hasLake())) { potentialOccupants.add(new Occupant(Occupant.Kind.HUT, zone.id())); } } diff --git a/test/ch/epfl/chacun/TileDecksTest.java b/test/ch/epfl/chacun/TileDecksTest.java index 4f5746f..b628945 100644 --- a/test/ch/epfl/chacun/TileDecksTest.java +++ b/test/ch/epfl/chacun/TileDecksTest.java @@ -84,16 +84,14 @@ void withTopTileWorks(){ normalTiles1.add(new Tile(1, Tile.Kind.NORMAL, null, null, null, null)); menhirTiles1.add(new Tile(2, Tile.Kind.MENHIR, null, null, null, null)); - startTiles1.add(new Tile(3, Tile.Kind.START, null, null, null, null)); - normalTiles1.add(new Tile(4, Tile.Kind.NORMAL, null, null, null, null)); - menhirTiles1.add(new Tile(5, Tile.Kind.MENHIR, null, null, null, null)); - TileDecks tileDecks1 = new TileDecks(startTiles1, normalTiles1, menhirTiles1); TileDecks tileDecks2 = new TileDecks(startTiles2, normalTiles2, menhirTiles2); assertEquals(tileDecks2.deckSize(Tile.Kind.START), tileDecks1.withTopTileDrawn(Tile.Kind.START) .deckSize(Tile.Kind.START)); - assertEquals(tileDecks2.deckSize(Tile.Kind.START), tileDecks1.withTopTileDrawn(Tile.Kind.START) - .deckSize(Tile.Kind.START)); + assertEquals(tileDecks2.deckSize(Tile.Kind.NORMAL), tileDecks1.withTopTileDrawn(Tile.Kind.NORMAL) + .deckSize(Tile.Kind.NORMAL)); + assertEquals(tileDecks2.deckSize(Tile.Kind.MENHIR), tileDecks1.withTopTileDrawn(Tile.Kind.MENHIR) + .deckSize(Tile.Kind.MENHIR)); } }