From 42309cf465165c9b62be4e9344f1ec452ef0b4a9 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Tue, 19 Mar 2024 17:10:30 +0100 Subject: [PATCH 01/68] Modify setupBoards to use setInitialCard rather than placeCard --- src/main/java/it/polimi/ingsw/gamemodel/Match.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index fdd0cc5d..ece71c58 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -30,6 +30,9 @@ public class Match { private boolean lastTurn = false; private boolean finished = false; + // Current match turn as an integer (incremental) + private int turn; + /** * Constructor to be used to initialize main Match attributes and allocate the attribute players List. @@ -225,10 +228,8 @@ protected void setupBoards() { player.getBoard().addHandCard(resourceCard2); // Place the initial card to the player's board - // By default, the initial card is placed on front side - Pair initialCoords = new Pair<>(0,0); - InitialCard initial = initialsDeck.pop(); - player.getBoard().placeCard(initialCoords, initial, Side.FRONT); + InitialCard initialCard = initialsDeck.pop(); + player.getBoard().setInitialCard(initialCard); } } From 2bc99dc82588a4ccf66317a06c500767611477c4 Mon Sep 17 00:00:00 2001 From: gixium Date: Tue, 19 Mar 2024 17:25:35 +0100 Subject: [PATCH 02/68] Add methods in classes ResourceCard, GoldCard, PlacedCard, Objective --- .../it/polimi/ingsw/gamemodel/GoldCard.java | 18 ++++++++++++++++++ .../it/polimi/ingsw/gamemodel/Objective.java | 16 ++++++++++++++++ .../it/polimi/ingsw/gamemodel/PlacedCard.java | 16 ++++++++++++++++ .../polimi/ingsw/gamemodel/ResourceCard.java | 7 +++++++ 4 files changed, 57 insertions(+) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java index cdf89e71..007f4267 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -1,5 +1,7 @@ package it.polimi.ingsw.gamemodel; +import java.util.concurrent.SynchronousQueue; + /** * The front side of these cards always gives points, but needs a certain requirement to be met in order to be played * @see CardFace @@ -25,6 +27,22 @@ public GoldCard(CardFace front, CardFace back, Symbol multiplier, int points, Qu this.req = req; } + /** + * Getter for the GoldCard class + * @return the multiplier. + */ + public Symbol getMultiplier(){ + return this.multiplier; + } + + /** + * Getter for the GoldCard class + * @return the quantity requirement for the gold card to be played. + */ + public QuantityRequirement getRequirement(){ + return this.req; + } + /** * Will compute the total points this card gives based on the board it's played on * @param board The board on which we want to compute the points this card will give diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Objective.java b/src/main/java/it/polimi/ingsw/gamemodel/Objective.java index ec04d950..8b024fb1 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Objective.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Objective.java @@ -16,5 +16,21 @@ public class Objective { public Objective(int points, Requirement req) { this.points = points; this.req = req; } + + /** + * Getter for the Objective class + * @return the points held by the objective card. + */ + public int getPoints() { + return this.points; + } + + /** + * Getter for the Objective class + * @return the requirement for the objective card to be placed. + */ + public Requirement getReq() { + return this.req; + } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java b/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java index 248d485d..a01b9305 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java @@ -16,4 +16,20 @@ public class PlacedCard { public PlacedCard(Card card, int turn) { this.card = card; this.turn = turn; } + + /** + * Getter for the PlacedCard class + * @return the card just placed. + */ + public Card getCard() { + return this.card; + } + + /** + * Getter for the PlacedCard class + * @return turn in which the card was just placed. + */ + public int getTurn() { + return this.turn; + } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java b/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java index 1b1533ab..46c5103b 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java @@ -13,4 +13,11 @@ public ResourceCard(CardFace front, CardFace back, int points) { this.front = front; this.back = back; this.points = points; } + /** + * Getter for the ResourceCard class + * @return the quantity requirement for the gold card to be played. + */ + public int getPoints(){ + return this.points; + } } From 9e02b8e9802ebae3c41a8bf5d2b0d0772e4cda8a Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Tue, 19 Mar 2024 17:29:59 +0100 Subject: [PATCH 03/68] Implement Board logic --- .../java/it/polimi/ingsw/gamemodel/Board.java | 32 +++++++++++++++---- .../polimi/ingsw/gamemodel/CardException.java | 8 +++++ .../polimi/ingsw/gamemodel/HandException.java | 8 +++++ 3 files changed, 42 insertions(+), 6 deletions(-) create mode 100644 src/main/java/it/polimi/ingsw/gamemodel/CardException.java create mode 100644 src/main/java/it/polimi/ingsw/gamemodel/HandException.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 667e102f..995284f4 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -4,6 +4,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; + + import it.polimi.ingsw.utils.Pair; /** @@ -35,7 +37,10 @@ public List getCurrentHand() { * Adds a card to the player's hand (which is visible to every player) * @param card the card to put in the hand */ - protected void addHandCard(PlayableCard card) { + protected void addHandCard(PlayableCard card) throws HandException { + if (currentHand.size() > 2) { // la mano ha 3 carte max + throw new HandException("Tried to draw a card with a full hand!"); + } currentHand.addLast(card); } @@ -45,26 +50,38 @@ protected void addHandCard(PlayableCard card) { * @param side the desired side for the initial card */ public void setInitialSide(Side side) { - + } /** * Removes a card from the hand of the player * @param card the card that must be removed from the player's hand */ - protected void removeHandCard(PlayableCard card) { + protected void removeHandCard(PlayableCard card) throws HandException { + if (currentHand.size() <= 0) { + throw new HandException("Tried to remove a card from an empty hand!"); + } currentHand.remove(card); } /** - * This method will add to the board the given card (if requirements are met and the position is valid), and update the player's resources + * This method will add to the board the given card (assuming the positioning is valid), and update the player's resources * @param coord the x and y coordinates in which the card must be placed * @param card the card to be placed * @param side the side of the card to be placed + * @param turn the turn of the game in which the card is played * @return the points gained from playing card */ - protected int placeCard(Pair coord, Card card, Side side) { - return 0; + protected int placeCard(Pair coord, PlayableCard card, Side side, int turn) throws CardException { + PlacedCard last = new PlacedCard(card, turn); + placed.put(coord, last); + if (card instanceof GoldCard) { + return ((GoldCard)card).totPoints(this); + } else if (card instanceof ResourceCard) { + return ((ResourceCard)card).getPoints(); + } else { + throw new CardException("Unknow card type!"); + } } /** @@ -74,6 +91,9 @@ protected int placeCard(Pair coord, Card card, Side side) { * @return whether the given coordinates are valid or not */ public boolean verifyCardPlacement(Pair coord, Card card, Side side) { + // if (placed) { + // + // } return true; } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/CardException.java b/src/main/java/it/polimi/ingsw/gamemodel/CardException.java new file mode 100644 index 00000000..b2bc9ae6 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/CardException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.gamemodel; + +public class CardException extends Exception { + public CardException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/gamemodel/HandException.java b/src/main/java/it/polimi/ingsw/gamemodel/HandException.java new file mode 100644 index 00000000..a36f2402 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/HandException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.gamemodel; + +public class HandException extends Exception { + public HandException(String s) { + super(s); + } +} + From f31273c3249eb913b716f20886b18b958088a607 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 11:13:17 +0100 Subject: [PATCH 04/68] Add PlacementOutcome enum, edit some methods --- deliveries/UML/class_diagram.mmd | 14 ++++++++++---- deliveries/UML/model.pdf | Bin 88494 -> 92210 bytes 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index b9edabc3..23ea210f 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -26,6 +26,7 @@ classDiagram QuantityRequirement <-- GoldCard Board --> PlacedCard + Board -- PlacementOutcome Player --> Board Player --> Objective CardFace <-- Card @@ -101,7 +102,7 @@ classDiagram + GoldCard(CardFace front, CardFace back, int points, Symbol multiplier, QuantityRequirement req) + getMultiplier() Symbol + getRequirement() QuantityRequirement - + totPoints(Board board) int + + calculatePoints(Board board) int } class Requirement { @@ -170,9 +171,9 @@ classDiagram # addHandCard(PlayableCard card) void # removeHandCard(PlayableCard card) void # setInitialSide(Side side) void - # placeCard(Pair~Integer, Integer~, PlayableCard card, Side side) int + # placeCard(Pair~Integer, Integer~, PlayableCard card, Side side, int turn) int # setInitialCard(InitialCard card) void - + verifyCardPlacement(Pair~Integer, Integer~ coords, Card card, Side side) bool + + verifyCardPlacement(Pair~Integer, Integer~ coords, Card card, Side side) PlacementOutcome } class PlacedCard { <> @@ -287,7 +288,12 @@ classDiagram + transition() void } - + class PlacementOutcome { + <> + VALID, + INVALID_COORDS, + INVALID_ENOUGH_RESOURCES, + } class GameDeck { <> - int size diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index a9db4c7dee2ea5bde642022deaf9e3f83f308f38..6d5c67ce16b604eb17ce1743fc5c59abd3aa80c3 100644 GIT binary patch delta 34772 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 Y(Dxp={~O1flFH@yO*eh=u1_8RKMq@Q(EtDd delta 31834 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 From 270ff06d12005449b6dfd74c1cb67bdea9952e8b Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Thu, 21 Mar 2024 11:16:35 +0100 Subject: [PATCH 05/68] Refactor: add exceptions package --- .../java/it/polimi/ingsw/exceptions/CardException.java | 8 ++++++++ .../java/it/polimi/ingsw/exceptions/HandException.java | 8 ++++++++ .../polimi/ingsw/exceptions/InvalidResourceException.java | 8 ++++++++ .../it/polimi/ingsw/exceptions/WrongStateException.java | 7 +++++++ 4 files changed, 31 insertions(+) create mode 100644 src/main/java/it/polimi/ingsw/exceptions/CardException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/HandException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java diff --git a/src/main/java/it/polimi/ingsw/exceptions/CardException.java b/src/main/java/it/polimi/ingsw/exceptions/CardException.java new file mode 100644 index 00000000..144438a0 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/CardException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class CardException extends Exception { + public CardException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/HandException.java b/src/main/java/it/polimi/ingsw/exceptions/HandException.java new file mode 100644 index 00000000..09f6d6b6 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/HandException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class HandException extends Exception { + public HandException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java b/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java new file mode 100644 index 00000000..2897523c --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class InvalidResourceException extends Exception { + public InvalidResourceException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java b/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java new file mode 100644 index 00000000..88a42b76 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java @@ -0,0 +1,7 @@ +package it.polimi.ingsw.exceptions; + +public class WrongStateException extends Exception{ + public WrongStateException(String message) { + super(message); + } +} From db6d6bb733c1f887863af89d2ffa547019793179 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Thu, 21 Mar 2024 11:18:59 +0100 Subject: [PATCH 06/68] Refactor: add exceptions package --- .../java/it/polimi/ingsw/exceptions/CardException.java | 8 ++++++++ .../java/it/polimi/ingsw/exceptions/HandException.java | 8 ++++++++ .../polimi/ingsw/exceptions/InvalidResourceException.java | 8 ++++++++ .../it/polimi/ingsw/exceptions/WrongStateException.java | 7 +++++++ 4 files changed, 31 insertions(+) create mode 100644 src/main/java/it/polimi/ingsw/exceptions/CardException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/HandException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java diff --git a/src/main/java/it/polimi/ingsw/exceptions/CardException.java b/src/main/java/it/polimi/ingsw/exceptions/CardException.java new file mode 100644 index 00000000..144438a0 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/CardException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class CardException extends Exception { + public CardException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/HandException.java b/src/main/java/it/polimi/ingsw/exceptions/HandException.java new file mode 100644 index 00000000..09f6d6b6 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/HandException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class HandException extends Exception { + public HandException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java b/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java new file mode 100644 index 00000000..2897523c --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class InvalidResourceException extends Exception { + public InvalidResourceException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java b/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java new file mode 100644 index 00000000..88a42b76 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java @@ -0,0 +1,7 @@ +package it.polimi.ingsw.exceptions; + +public class WrongStateException extends Exception{ + public WrongStateException(String message) { + super(message); + } +} From 09ad35a774037e568e3f09444c153fbcdb143918 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:43:40 +0100 Subject: [PATCH 07/68] Add ChooseInitialSideState to UML diagram --- deliveries/UML/class_diagram.mmd | 12 +++++++++++- deliveries/UML/model.pdf | Bin 92210 -> 95489 bytes 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index 23ea210f..4a89e1d0 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -9,6 +9,7 @@ classDiagram MatchState <|-- WaitState MatchState <|-- SetupState MatchState <|-- NextTurnState + MatchState <|-- ChooseInitialSideState MatchState <|-- ChooseSecretObjectiveState MatchState <|-- AfterMoveState MatchState <|-- AfterDrawState @@ -84,7 +85,9 @@ classDiagram class PlayableCard { <> + # reign: Symbol # points: int + + getReign() Symbol } class InitialCard { @@ -140,6 +143,7 @@ classDiagram + playCard(Pair coord, PlayableCard card, Side side) void + drawCard(DrawSource draw) void + chooseObjective(Objective objective) void + + chooseInitialSide(Side side) void + getBoard() Board + getPoints() int + getSecretObjective() Objective @@ -215,6 +219,7 @@ classDiagram # proposeSecretObjectives() Pair~Objective, Objective~ # makeMove(Pair~Integer, Integer~ coords, PlayableCard card, Side side) void # drawCard(DrawSource draw) PlayableCard + # chooseInitialSide(Side side) void # doStart() void # doFinish() void # setState() void @@ -243,6 +248,7 @@ classDiagram + removePlayer() void + proposeSecretObjectives() void + chooseSecretObjective() void + + chooseInitialSide(Side side) void + makeMove() void + drawCard() void @@ -265,7 +271,11 @@ classDiagram + makeMove() void + transition() void } - + class ChooseInitialSideState{ + + ChooseInitialSideState(Match match) void + + chooseInitialSide(Side side) void + + transition() void + } class ChooseSecretObjectiveState { + ChooseSecretObjectiveState() void + chooseSecretObjective() void diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index 6d5c67ce16b604eb17ce1743fc5c59abd3aa80c3..23193c9235828cfaae866d14a2a8c3ccb8713400 100644 GIT binary patch delta 35332 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 c;YYZ3 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 A^Z)<= From 052a4e12713a12dd88009274a562d930642a963a Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Thu, 21 Mar 2024 12:45:50 +0100 Subject: [PATCH 08/68] Add (refactor) Javadoc in Match Added and in some cases refactored javadoc comments in most of Match methods --- .../java/it/polimi/ingsw/gamemodel/Match.java | 101 +++++++++++------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index ece71c58..747b3d7c 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -35,8 +35,8 @@ public class Match { /** - * Constructor to be used to initialize main Match attributes and allocate the attribute players List. - * @param maxPlayers maximum number of players to be added to the match, chosen by the first player to join + * Initialize main Match attributes and allocate the attribute players List, assuming no parameter is null. + * @param maxPlayers maximum number of players to be added to the match, chosen by the first player joining the match * @param initialsDeck deck of initial cards * @param resourcesDeck deck of resource cards * @param goldsDeck deck of gold cards @@ -54,9 +54,10 @@ public Match(int maxPlayers, GameDeck initialsDeck, GameDeck 2 -> 3 -> 1 -> etc. + * Note It's called by NextTurnState every time a new turn starts. */ protected void nextPlayer() { // If player has never been initialized OR the current player is the last one @@ -102,56 +104,71 @@ protected void nextPlayer() { currentPlayer = players.get(currentPlayerIndex + 1); } } - // Called by the state + /** - * + * Mark the match as finished, assuming the match hasn't finished yet. + * Note: It's called by FinalState once the match is ready to finish. */ protected void doFinish() { finished = true; } - // Called by the controller /** - * - * @return + * Verify if the match is finished. + * Note: It's called by the Controller. + * @return true if the match is finished, false otherwise */ public boolean isFinished() { return finished; } + /** + * Mark the match as started, assuming the match hasn't started yet. + * Note: It's called by ChooseSecretObjectiveState once the match is ready to start. + */ protected void doStart() { started = true; } + /** + * Verify if the match is started. + * Note: It's called by NextTurnState to check when to effectively start the match. + * @return true if the match is started, false otherwise + */ public boolean isStarted() { return started; } - // Called by the controller /** - * - * @return + * Get the player who's playing (or choosing the secret objective) at the moment. + * Note: It's used by the Controller. + * @return the player playing at the moment, null if the match has never reached NextTurnState */ public Player getCurrentPlayer() { return currentPlayer; } + /** + * Get the match players. + * @return the match players in a List, dynamically defined as an ArrayList + */ public List getPlayers() { return players; } - // Called by the state /** - * - * @param state + * Set the current match state, assuming it's not null. + * Note: It's called by each state to let the match enter to the next state. + * @param state the state in which the match has to be */ protected void setState(MatchState state) { this.currentState = state; } /** - * - * @return + * Extract two cards from the deck of objectives and return them. + * Note: It's called by the controller. + * @return the two cards extracted */ protected Pair proposeSecretObjectives() { Objective obj1 = objectivesDeck.pop(); @@ -160,10 +177,11 @@ protected Pair proposeSecretObjectives() { return currentProposedObjectives; } - // Called by the controller /** - * - * @param objective + * Check that the given objective is one of the proposed ones to the current player + * and put the discarded objective back in the objectives deck. + * Note: Called by Player + * @param objective the accepted objective by the player */ protected void chooseSecretObjective(Objective objective) { // Put back the player's refused secret objective @@ -171,7 +189,8 @@ protected void chooseSecretObjective(Objective objective) { } /** - * + * Shuffle the players turn order and give them their pawn color + * Note: It's called by SetupState */ protected void setupPlayers() { // Shuffle players List @@ -184,7 +203,8 @@ protected void setupPlayers() { } /** - * + * Shuffle cards decks and place the visible cards on the board + * Note: It's called by SetupState */ protected void setupDecks() { // Shuffle each deck @@ -212,7 +232,9 @@ protected void setupDecks() { } /** - * + * Give one gold card and two resource cards to each player hand + * and set the initial card + * Note: It's called by WaitState */ protected void setupBoards() { // Give starting cards to players @@ -235,12 +257,13 @@ protected void setupBoards() { } /** - * - * @param coords - * @param card - * @param side - * @throws WrongStateException - * @throws WrongCardPlacementException + * Check if the placement is valid, then place the card on the board and add points to the player + * Note: It's called by Player + * @param coords coordinates in which to place the card + * @param card card to place + * @param side side of the card to be placed + * @throws WrongStateException if called during a state that does not allow making moves + * @throws WrongCardPlacementException if the placement is not valid */ protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongCardPlacementException { Board currentPlayerBoard = currentPlayer.getBoard(); From 3832b64a260758d2bb5d48f1fb9f097fb60c67e4 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Thu, 21 Mar 2024 11:18:59 +0100 Subject: [PATCH 09/68] Refactor: add exceptions package --- .../java/it/polimi/ingsw/exceptions/CardException.java | 8 ++++++++ .../java/it/polimi/ingsw/exceptions/HandException.java | 8 ++++++++ .../polimi/ingsw/exceptions/InvalidResourceException.java | 8 ++++++++ .../it/polimi/ingsw/exceptions/WrongStateException.java | 7 +++++++ 4 files changed, 31 insertions(+) create mode 100644 src/main/java/it/polimi/ingsw/exceptions/CardException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/HandException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java diff --git a/src/main/java/it/polimi/ingsw/exceptions/CardException.java b/src/main/java/it/polimi/ingsw/exceptions/CardException.java new file mode 100644 index 00000000..144438a0 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/CardException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class CardException extends Exception { + public CardException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/HandException.java b/src/main/java/it/polimi/ingsw/exceptions/HandException.java new file mode 100644 index 00000000..09f6d6b6 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/HandException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class HandException extends Exception { + public HandException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java b/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java new file mode 100644 index 00000000..2897523c --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/InvalidResourceException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class InvalidResourceException extends Exception { + public InvalidResourceException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java b/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java new file mode 100644 index 00000000..88a42b76 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/WrongStateException.java @@ -0,0 +1,7 @@ +package it.polimi.ingsw.exceptions; + +public class WrongStateException extends Exception{ + public WrongStateException(String message) { + super(message); + } +} From a0c78ed3d3e5e44e83649125bd9dcdb36476b1aa Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 12:52:48 +0100 Subject: [PATCH 10/68] Move exceptions to exceptions package --- .../WrongCardPlacementException.java | 2 +- .../it/polimi/ingsw/gamemodel/WrongStateException.java | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) rename src/main/java/it/polimi/ingsw/{gamemodel => exceptions}/WrongCardPlacementException.java (80%) delete mode 100644 src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java b/src/main/java/it/polimi/ingsw/exceptions/WrongCardPlacementException.java similarity index 80% rename from src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java rename to src/main/java/it/polimi/ingsw/exceptions/WrongCardPlacementException.java index f8829974..caeea2c6 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java +++ b/src/main/java/it/polimi/ingsw/exceptions/WrongCardPlacementException.java @@ -1,4 +1,4 @@ -package it.polimi.ingsw.gamemodel; +package it.polimi.ingsw.exceptions; public class WrongCardPlacementException extends Exception { public WrongCardPlacementException(String message) { diff --git a/src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java b/src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java deleted file mode 100644 index a7f6ff6e..00000000 --- a/src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java +++ /dev/null @@ -1,7 +0,0 @@ -package it.polimi.ingsw.gamemodel; - -public class WrongStateException extends Exception{ - public WrongStateException(String message) { - super(message); - } -} From 9ee7199fd44e3b64967562f1070fc05aa88756ea Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Thu, 21 Mar 2024 13:46:54 +0100 Subject: [PATCH 11/68] Add reign attribute to PlayableCard, minor refactoring --- .../ingsw/gamemodel/AfterMoveState.java | 4 +- .../java/it/polimi/ingsw/gamemodel/Board.java | 22 ++++++-- .../polimi/ingsw/gamemodel/CardException.java | 8 --- .../it/polimi/ingsw/gamemodel/GoldCard.java | 15 +++--- .../polimi/ingsw/gamemodel/HandException.java | 8 --- .../java/it/polimi/ingsw/gamemodel/Match.java | 23 +++++--- .../it/polimi/ingsw/gamemodel/MatchState.java | 1 + .../polimi/ingsw/gamemodel/NextTurnState.java | 1 + .../polimi/ingsw/gamemodel/PlayableCard.java | 26 ++++++++- .../ingsw/gamemodel/PositionRequirement.java | 12 ++++- .../ingsw/gamemodel/QuantityRequirement.java | 23 ++++++-- .../polimi/ingsw/gamemodel/Requirement.java | 6 ++- .../polimi/ingsw/gamemodel/ResourceCard.java | 12 +++-- .../it/polimi/ingsw/gamemodel/Symbol.java | 53 ++++++++++++++++++- .../WrongCardPlacementException.java | 7 --- .../ingsw/gamemodel/WrongStateException.java | 7 --- 16 files changed, 167 insertions(+), 61 deletions(-) delete mode 100644 src/main/java/it/polimi/ingsw/gamemodel/CardException.java delete mode 100644 src/main/java/it/polimi/ingsw/gamemodel/HandException.java delete mode 100644 src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java delete mode 100644 src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java index 245790d6..53657eee 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java @@ -1,5 +1,7 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.*; + public class AfterMoveState extends MatchState { public AfterMoveState(Match match) { @@ -7,7 +9,7 @@ public AfterMoveState(Match match) { } @Override - public void drawCard() throws WrongStateException { + public void drawCard() { this.transition(); } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 995284f4..e036fdaa 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -1,5 +1,6 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -23,6 +24,16 @@ public Board() { currentHand = new ArrayList<>(); placed = new HashMap<>(); availableResources = new HashMap<>(); + for (Symbol s : Symbol.getBasicResources()) { + availableResources.put(s, 0); + } + } + + /** + * Getter for the total resources of a player + */ + public Map getAvailableResources() { + return this.availableResources; } /** @@ -76,7 +87,7 @@ protected int placeCard(Pair coord, PlayableCard card, Side si PlacedCard last = new PlacedCard(card, turn); placed.put(coord, last); if (card instanceof GoldCard) { - return ((GoldCard)card).totPoints(this); + return ((GoldCard)card).calculatePoints(this); } else if (card instanceof ResourceCard) { return ((ResourceCard)card).getPoints(); } else { @@ -91,9 +102,12 @@ protected int placeCard(Pair coord, PlayableCard card, Side si * @return whether the given coordinates are valid or not */ public boolean verifyCardPlacement(Pair coord, Card card, Side side) { - // if (placed) { - // - // } + if (placed.keySet().contains(coord)) { + return false; + } + Pair cmp = new Pair(coord.first()-1, coord.second()); + + return true; } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/CardException.java b/src/main/java/it/polimi/ingsw/gamemodel/CardException.java deleted file mode 100644 index b2bc9ae6..00000000 --- a/src/main/java/it/polimi/ingsw/gamemodel/CardException.java +++ /dev/null @@ -1,8 +0,0 @@ -package it.polimi.ingsw.gamemodel; - -public class CardException extends Exception { - public CardException(String s) { - super(s); - } -} - diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java index 007f4267..2ce3d677 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -1,7 +1,6 @@ package it.polimi.ingsw.gamemodel; -import java.util.concurrent.SynchronousQueue; - +import it.polimi.ingsw.exceptions.InvalidResourceException; /** * The front side of these cards always gives points, but needs a certain requirement to be met in order to be played * @see CardFace @@ -11,17 +10,17 @@ public class GoldCard extends PlayableCard{ private QuantityRequirement req; /** - * The front side always gives points based on the quantity of a certain resource, while the back always gives a - * resource of the card's faction + * Class constructor. It needs to only take the front as an argument, since the back is handled by its superclass {@link PlayableCard} * @param front the front side of the card - * @param back the back side of the card + * @param reign the reign of the card * @param multiplier the symbol whose number of resources multiplies the points parameter * @param points the number every resource of the given type is worth * @param req the requirement that must be met in order to be able to play the card + * @throws InvalidResourceException if the passed resource is not in {@link Symbol#getReigns()} */ - public GoldCard(CardFace front, CardFace back, Symbol multiplier, int points, QuantityRequirement req) { + public GoldCard(CardFace front, Symbol reign, Symbol multiplier, int points, QuantityRequirement req) throws InvalidResourceException { + super(reign); this.front = front; - this.back = back; this.points = points; this.multiplier = multiplier; this.req = req; @@ -47,7 +46,7 @@ public QuantityRequirement getRequirement(){ * Will compute the total points this card gives based on the board it's played on * @param board The board on which we want to compute the points this card will give */ - public int totPoints(Board board) { + public int calculatePoints(Board board) { return this.points; // will need to compute tot resources of board and get the tot resource } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/HandException.java b/src/main/java/it/polimi/ingsw/gamemodel/HandException.java deleted file mode 100644 index a36f2402..00000000 --- a/src/main/java/it/polimi/ingsw/gamemodel/HandException.java +++ /dev/null @@ -1,8 +0,0 @@ -package it.polimi.ingsw.gamemodel; - -public class HandException extends Exception { - public HandException(String s) { - super(s); - } -} - diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index fdd0cc5d..e4293e23 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -1,6 +1,7 @@ package it.polimi.ingsw.gamemodel; import it.polimi.ingsw.utils.Pair; +import it.polimi.ingsw.exceptions.*; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -9,6 +10,7 @@ public class Match { private final List players; private final int maxPlayers; private Player currentPlayer; + private int turn; private MatchState currentState; @@ -220,15 +222,14 @@ protected void setupBoards() { ResourceCard resourceCard2 = resourcesDeck.pop(); // Add each card to the player's hand - player.getBoard().addHandCard(goldCard); - player.getBoard().addHandCard(resourceCard1); - player.getBoard().addHandCard(resourceCard2); + // player.getBoard().addHandCard(goldCard); + // player.getBoard().addHandCard(resourceCard1); + // player.getBoard().addHandCard(resourceCard2); // Place the initial card to the player's board // By default, the initial card is placed on front side Pair initialCoords = new Pair<>(0,0); InitialCard initial = initialsDeck.pop(); - player.getBoard().placeCard(initialCoords, initial, Side.FRONT); } } @@ -252,11 +253,21 @@ protected void makeMove(Pair coords, PlayableCard card, Side s // Place the card in the current player's board // and save the points possibly gained because of the move - int gainedPoints = currentPlayerBoard.placeCard(coords, card, side); + int gainedPoints = 0; + try { + gainedPoints = currentPlayerBoard.placeCard(coords, card, side, turn); + } catch(Exception e) { + + } // Remove the card from the player's hand // since it has been placed on the board - currentPlayerBoard.removeHandCard(card); + try { + currentPlayerBoard.removeHandCard(card); + } catch (Exception e) { + + } + // Update the current player's points currentPlayer.addPoints(gainedPoints); diff --git a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java index e26251f3..a1e56301 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java @@ -1,4 +1,5 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.*; public abstract class MatchState { Match match; diff --git a/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java index 878c4175..fcb2cdf6 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java @@ -1,4 +1,5 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.*; public class NextTurnState extends MatchState { diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java b/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java index ff6b1c31..03fe13ee 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java @@ -1,4 +1,7 @@ package it.polimi.ingsw.gamemodel; +import java.util.Set; + +import it.polimi.ingsw.exceptions.InvalidResourceException; /** * Class that represents every kind of card that can be played during the game. @@ -6,7 +9,28 @@ * @see CardFace */ -public abstract class PlayableCard extends Card{ +public abstract class PlayableCard extends Card { protected int points; + protected Symbol reign; + + /** + * Constructor for PlayableCard. Since the only common aspects are that they have a reign and that the back is made up of + * only full corners (no resources) and the center gives a resource of their reign + * @param reign the reign of the card + * @throws InvalidResourceException if the passed resource is not in {@link Symbol#getReigns()} + */ + public PlayableCard(Symbol reign) throws InvalidResourceException { + if (Symbol.getReigns().contains(reign)) { + this.points = 0; + this.reign = reign; + this.back = new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(reign)); + } else { + throw new InvalidResourceException("Resource " + reign.toString() + " is not valid for a " + this.getClass().toString()); + } + } + + public Symbol getReign() { + return this.reign; + } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java index 08abf3f0..f6b44870 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java @@ -1,7 +1,9 @@ package it.polimi.ingsw.gamemodel; +import java.util.EnumSet; import java.util.Map; +import it.polimi.ingsw.exceptions.InvalidResourceException; import it.polimi.ingsw.utils.Pair; /** @@ -15,7 +17,14 @@ public class PositionRequirement extends Requirement{ * Note that, since this requirement only cares about relative positioning, there must always be * an element whose key is (0, 0) */ - public PositionRequirement(Map, Symbol> reqs) { + public PositionRequirement(Map, Symbol> reqs) throws InvalidResourceException { + EnumSet validResources = Symbol.getReigns(); + for (Symbol s : reqs.values()) { + if ( !validResources.contains(s) ) { + throw new InvalidResourceException("Resource " + s.toString() + " is not valid for a " + this.getClass().toString()); + } + } + this.reqs = reqs; } @@ -26,6 +35,7 @@ public PositionRequirement(Map, Symbol> reqs) { */ @Override public boolean isSatisfied(Board board) { + return true; } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java index f1436fe6..fb5c651f 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java @@ -1,8 +1,9 @@ package it.polimi.ingsw.gamemodel; +import java.util.EnumSet; import java.util.Map; -import it.polimi.ingsw.utils.Pair; +import it.polimi.ingsw.exceptions.*; /** * This class handles requirements involving relative positioning of cards, e.g. three red cards placed in the top right corner of each other @@ -11,19 +12,33 @@ public class QuantityRequirement extends Requirement{ private Map reqs; /** - * @param reqs how many resources of a certain type are needed to fulfill the requirement + * @param reqs how many resources of a certain type are needed to fulfill the requirement. + * Only valid symbols are the ones returned by {@link Symbol#getValidRequirement()} + * @throws InvalidResourceException if a requirement is not made up only of those symbols */ - public QuantityRequirement(Map reqs) { + public QuantityRequirement(Map reqs) throws InvalidResourceException { + EnumSet validResources = Symbol.getBasicResources(); + for (Symbol s : reqs.keySet()) { + if ( !validResources.contains(s) ) { + throw new InvalidResourceException("Resource " + s.toString() + " is not valid for a " + this.getClass().toString()); + } + } this.reqs = reqs; } /** * The requirement will be satisfied if the board has enough resources of the specified type * @param board the board on which we check the requirement - * @return whether the requirement is satisfied + * @return whether the requirement is satisfied or not */ @Override public boolean isSatisfied(Board board) { + for (Symbol s : reqs.keySet()) { + if (board.getAvailableResources().get(s) < reqs.get(s)) { + return false; + } + } + return true; } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java index df254369..b8e2989c 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java @@ -1,5 +1,6 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.*; /* * A condition must be met in order to play a golden card and to get points from the objectives. Those requirements are both represented by this class */ @@ -10,6 +11,9 @@ public abstract class Requirement { * @param board the board that will be used to check if the requirement is met * @return whether the requirement is met or not */ - public abstract boolean isSatisfied(Board board); + public Requirement() throws InvalidResourceException { + + } + public abstract boolean isSatisfied(Board board); } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java b/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java index 46c5103b..49620e36 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java @@ -1,16 +1,20 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.InvalidResourceException; + /* * Card that does not require any conditions to be played */ -public class ResourceCard extends PlayableCard{ +public class ResourceCard extends PlayableCard { /** + * Class constructor. It needs to only take the front as an argument, since the back is handled by its superclass {@link PlayableCard} * @param front the front side of the card - * @param back the back side of the card + * @param reign the reign of the card * @param points the number of points the card gives (must be an absolute value) + * @throws InvalidResourceException if the passed resource is not in {@link Symbol#getReigns()} */ - public ResourceCard(CardFace front, CardFace back, int points) { - this.front = front; this.back = back; this.points = points; + public ResourceCard(CardFace front, Symbol reign, int points) throws InvalidResourceException { + super(reign); this.front = front; this.points = points; } /** diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java b/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java index 35dbd68a..f8c974bb 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java @@ -1,4 +1,5 @@ package it.polimi.ingsw.gamemodel; +import java.util.EnumSet; /** * Contains all the possible symbols a corner can store or a multiplier can have. @@ -16,5 +17,55 @@ public enum Symbol { PARCHMENT, EMPTY_CORNER, FULL_CORNER, - CORNER_OBJ + CORNER_OBJ; + + static public EnumSet getReigns() { + return EnumSet.of( + ANIMAL, + PLANT, + INSECT, + FUNGUS + ); + } + + static public EnumSet getBasicResources() { + return EnumSet.of( + ANIMAL, + PLANT, + INSECT, + FUNGUS, + FEATHER, + INKWELL, + PARCHMENT + ); + } + + static public EnumSet getValidCorner() { + return EnumSet.of( + ANIMAL, + PLANT, + INSECT, + FUNGUS, + FEATHER, + INKWELL, + PARCHMENT, + EMPTY_CORNER, + FULL_CORNER + ); + } + + static public EnumSet getValidMultiplier() { + return EnumSet.of( + ANIMAL, + PLANT, + INSECT, + FUNGUS, + FEATHER, + INKWELL, + PARCHMENT, + CORNER_OBJ + ); + } + } + diff --git a/src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java b/src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java deleted file mode 100644 index f8829974..00000000 --- a/src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java +++ /dev/null @@ -1,7 +0,0 @@ -package it.polimi.ingsw.gamemodel; - -public class WrongCardPlacementException extends Exception { - public WrongCardPlacementException(String message) { - super(message); - } -} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java b/src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java deleted file mode 100644 index a7f6ff6e..00000000 --- a/src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java +++ /dev/null @@ -1,7 +0,0 @@ -package it.polimi.ingsw.gamemodel; - -public class WrongStateException extends Exception{ - public WrongStateException(String message) { - super(message); - } -} From aead73e5ca1cf6efabfbe20ab72e7adf2698c7b6 Mon Sep 17 00:00:00 2001 From: gixium Date: Thu, 21 Mar 2024 13:48:59 +0100 Subject: [PATCH 12/68] Minor corrections --- deliveries/UML/class_diagram.mmd | 10 ++++++---- deliveries/UML/model.pdf | Bin 95489 -> 96137 bytes 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index 4a89e1d0..3dc0d8d9 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -87,6 +87,7 @@ classDiagram <> # reign: Symbol # points: int + + PlayableCard(Symbol reign) + getReign() Symbol } @@ -95,14 +96,14 @@ classDiagram } class ResourceCard { - + ResourceCard(CardFace front, CardFace back, int points) - + getPoints() : int + + ResourceCard(CardFace front, Symbol reign, int points) + + getPoints() int } class GoldCard { - multiplier: Symbol - req: QuantityRequirement - + GoldCard(CardFace front, CardFace back, int points, Symbol multiplier, QuantityRequirement req) + + GoldCard(CardFace front, Symbol reign, int points, Symbol multiplier, QuantityRequirement req) + getMultiplier() Symbol + getRequirement() QuantityRequirement + calculatePoints(Board board) int @@ -111,10 +112,11 @@ classDiagram class Requirement { <> + isSatisfied(Board board) bool + - Requirement() } class QuantityRequirement { - - reqs : Map~Integer, Symbol~ + - reqs : Map~Symbol, Integer~ + QuantityRequirement(Symbol simbol, Integer quantity) + isSatisfied(Board board) bool } diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index 23193c9235828cfaae866d14a2a8c3ccb8713400..c3f25d17011883dd066ff7d6797eaefd8b09162b 100644 GIT binary patch delta 34904 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 h*Dq`O;MN?JtV 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 U>&>OD&gHESfB2U_^W@h516yt$H~;_u From 9c7540315b479a061b6c17a74de644e4077c34a8 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Thu, 21 Mar 2024 15:54:46 +0100 Subject: [PATCH 13/68] Add poll to GameDeck, minor refactor --- .../java/it/polimi/ingsw/gamemodel/Board.java | 9 ++++++--- .../java/it/polimi/ingsw/gamemodel/GameDeck.java | 16 ++++++++++++---- .../polimi/ingsw/gamemodel/PlacementOutcome.java | 8 ++++++++ 3 files changed, 26 insertions(+), 7 deletions(-) create mode 100644 src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index e036fdaa..18288cbc 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -91,7 +91,7 @@ protected int placeCard(Pair coord, PlayableCard card, Side si } else if (card instanceof ResourceCard) { return ((ResourceCard)card).getPoints(); } else { - throw new CardException("Unknow card type!"); + throw new CardException("Unknow card type: " + card.getClass().toString() + "!"); } } @@ -101,9 +101,12 @@ protected int placeCard(Pair coord, PlayableCard card, Side si * @param coord the x and y coordinates to check * @return whether the given coordinates are valid or not */ - public boolean verifyCardPlacement(Pair coord, Card card, Side side) { + public PlacementOutcome verifyCardPlacement(Pair coord, Card card, Side side) throws CardException { + if (!currentHand.contains(card)) { + throw new CardException("The card " + card.getClass().toString() + " is not in the player's hand!"); + } if (placed.keySet().contains(coord)) { - return false; + return PlacementOutcome.INVALID_COORDS; } Pair cmp = new Pair(coord.first()-1, coord.second()); diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java index 7b22a295..13e31afe 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java @@ -23,15 +23,23 @@ public void add(T card) { } - public T pop() { - return null; + public T pop() throws Exception { + if (this.isEmpty()) + throw new Exception("Tried to draw from an empty deck!"); + return cardsList.removeLast(); + } + + public T poll() { + if (this.isEmpty()) + return null; + return cardsList.removeLast(); } public void shuffle() { } - public void isEmpty() { - + public boolean isEmpty() { + return false; } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java b/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java new file mode 100644 index 00000000..66f97e2b --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.gamemodel; + +public enum PlacementOutcome { + VALID, + INVALID_COORDS, + INVALID_ENOUGH_RESOURCES +} + From 712d673cd851359fa7fa92367b948979ae6800cf Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:25:30 +0100 Subject: [PATCH 14/68] Add class MutablePair --- .../it/polimi/ingsw/utils/MutablePair.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 src/main/java/it/polimi/ingsw/utils/MutablePair.java diff --git a/src/main/java/it/polimi/ingsw/utils/MutablePair.java b/src/main/java/it/polimi/ingsw/utils/MutablePair.java new file mode 100644 index 00000000..a64fe82f --- /dev/null +++ b/src/main/java/it/polimi/ingsw/utils/MutablePair.java @@ -0,0 +1,27 @@ +package it.polimi.ingsw.utils; + +public class MutablePair { + private T first; + private U second; + + public MutablePair(T first, U second) { + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public void setFirst(T first) { + this.first = first; + } + + public U getSecond() { + return second; + } + + public void setSecond(U second) { + this.second = second; + } +} From 930f22395c3063421ffc8c878b6bdaae834bc4b2 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:27:10 +0100 Subject: [PATCH 15/68] Add WrongTurnException and WrongChoiceException --- .../it/polimi/ingsw/exceptions/WrongChoiceException.java | 7 +++++++ .../it/polimi/ingsw/exceptions/WrongTurnException.java | 7 +++++++ 2 files changed, 14 insertions(+) create mode 100644 src/main/java/it/polimi/ingsw/exceptions/WrongChoiceException.java create mode 100644 src/main/java/it/polimi/ingsw/exceptions/WrongTurnException.java diff --git a/src/main/java/it/polimi/ingsw/exceptions/WrongChoiceException.java b/src/main/java/it/polimi/ingsw/exceptions/WrongChoiceException.java new file mode 100644 index 00000000..1cf8b782 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/WrongChoiceException.java @@ -0,0 +1,7 @@ +package it.polimi.ingsw.exceptions; + +public class WrongChoiceException extends Exception { + public WrongChoiceException(String s) { + super(s); + } +} diff --git a/src/main/java/it/polimi/ingsw/exceptions/WrongTurnException.java b/src/main/java/it/polimi/ingsw/exceptions/WrongTurnException.java new file mode 100644 index 00000000..004e1147 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/WrongTurnException.java @@ -0,0 +1,7 @@ +package it.polimi.ingsw.exceptions; + +public class WrongTurnException extends Exception{ + public WrongTurnException(String message) { + super(message); + } +} From 05762aa54bf40973d3f2d47b05447997fe5717b1 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:28:42 +0100 Subject: [PATCH 16/68] Implement ChooseInitialSideState --- .../gamemodel/ChooseInitialSideState.java | 21 ++++++++++++++++++- .../it/polimi/ingsw/gamemodel/MatchState.java | 5 +++++ .../polimi/ingsw/gamemodel/NextTurnState.java | 3 +++ 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java b/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java index 2c453cb9..ddab83eb 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java @@ -1,5 +1,24 @@ package it.polimi.ingsw.gamemodel; -public class ChooseInitialSideState { +public class ChooseInitialSideState extends MatchState { + public ChooseInitialSideState(Match match) { + super(match); + } + + @Override + public void chooseInitialSide() { + Player lastPlayer = match.getPlayers().getLast(); + + if (match.getCurrentPlayer().equals(lastPlayer)) + match.doInitialTurnFinish(); + + this.transition(); + } + + @Override + public void transition() { + MatchState nextState = new NextTurnState(match); + match.setState(nextState); + } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java index e26251f3..2f44021e 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java @@ -1,4 +1,5 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.WrongStateException; public abstract class MatchState { Match match; @@ -16,6 +17,10 @@ public void addPlayer() throws WrongStateException{ public void removePlayer() throws WrongStateException{ throw new WrongStateException("removePlayer not allowed from the current match state!"); } + + public void chooseInitialSide() throws WrongStateException{ + throw new WrongStateException("chooseInitialSide not allowed from the current match state!"); + } public void makeMove() throws WrongStateException{ throw new WrongStateException("makeMove not allowed from the current match state!"); diff --git a/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java index 878c4175..af466835 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java @@ -1,4 +1,5 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.WrongStateException; public class NextTurnState extends MatchState { @@ -30,6 +31,8 @@ public void transition() { if (match.isStarted()) nextState = new AfterMoveState(match); + else if (!match.isInitialTurnFinished()) + nextState = new ChooseInitialSideState(match); else nextState = new ChooseSecretObjectiveState(match); From 4cf7a2447f1cb3b7c09dd0a9c6627290c5e01aaf Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:32:40 +0100 Subject: [PATCH 17/68] Implement chooseSecretObjective, drawCard and more Implemented: - chooseSecretObjective - drawCard - doInitialTurnFinish - isInitialTurnFinished --- .../java/it/polimi/ingsw/gamemodel/Match.java | 117 ++++++++++++++++-- 1 file changed, 105 insertions(+), 12 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index 747b3d7c..db189344 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -2,8 +2,10 @@ import it.polimi.ingsw.utils.Pair; import java.util.ArrayList; +import it.polimi.ingsw.utils.MutablePair; import java.util.Collections; import java.util.List; +import it.polimi.ingsw.exceptions.*; public class Match { private final List players; @@ -19,14 +21,15 @@ public class Match { private final GameDeck objectivesDeck; // All the visible cards on the common table - private Pair visibleResources; - private Pair visibleGolds; + private MutablePair visibleResources; + private MutablePair visibleGolds; private Pair visibleObjectives; private Pair currentProposedObjectives; // Denotes if the match has been started or has finished private boolean started = false; + private boolean initialTurnFinished = false; private boolean lastTurn = false; private boolean finished = false; @@ -115,13 +118,30 @@ protected void doFinish() { /** * Verify if the match is finished. - * Note: It's called by the Controller. + * Note: It's called by the Controller and NextTurnState. * @return true if the match is finished, false otherwise */ public boolean isFinished() { return finished; } + /** + * Mark the initial turn as finished, assuming the initial turn hasn't finished yet. + * Note: It's called by ChooseInitialCardState once the initial turn is finished. + */ + protected void doInitialTurnFinish() { + initialTurnFinished = true; + } + + /** + * Verify if the initial turn is finished. + * Note: It's called by NextTurnState. + * @return true if the initial turn is finished, false otherwise + */ + public boolean isInitialTurnFinished() { + return initialTurnFinished; + } + /** * Mark the match as started, assuming the match hasn't started yet. * Note: It's called by ChooseSecretObjectiveState once the match is ready to start. @@ -180,12 +200,23 @@ protected Pair proposeSecretObjectives() { /** * Check that the given objective is one of the proposed ones to the current player * and put the discarded objective back in the objectives deck. - * Note: Called by Player + * Note: It's called by Player * @param objective the accepted objective by the player */ - protected void chooseSecretObjective(Objective objective) { - // Put back the player's refused secret objective - objectivesDeck.add(objective); + protected void chooseSecretObjective(Objective objective) throws WrongThreadException { + // Get proposed objectives + Objective firstProposedObjective = currentProposedObjectives.first(); + Objective secondProposedObjective = currentProposedObjectives.second(); + // Check if the chosen objective is one of the proposed ones and put it back in the deck + if (objective.equals(firstProposedObjective)) { + objectivesDeck.add(secondProposedObjective); + } else if (objective.equals(secondProposedObjective)) { + objectivesDeck.add(firstProposedObjective); + } else { + // If the objective is not one of the proposed ones + // throw an exception + throw new WrongThreadException("The chosen objective is not one of the proposed ones"); + } } /** @@ -226,8 +257,8 @@ protected void setupDecks() { Objective objective2 = objectivesDeck.pop(); // Set popped cards in Match attributes - visibleGolds = new Pair<>(goldCard1, goldCard2); - visibleResources = new Pair<>(resourceCard1, resourceCard2); + visibleGolds = new MutablePair(goldCard1, goldCard2); + visibleResources = new MutablePair(resourceCard1, resourceCard2); visibleObjectives = new Pair<>(objective1, objective2); } @@ -265,9 +296,9 @@ protected void setupBoards() { * @throws WrongStateException if called during a state that does not allow making moves * @throws WrongCardPlacementException if the placement is not valid */ - protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongCardPlacementException { + protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException { Board currentPlayerBoard = currentPlayer.getBoard(); - + // TODO: Fix implementation with new verifyCardPlacement method // If placing the card in the current player's board is allowed by rules if (currentPlayerBoard.verifyCardPlacement(coords, card, side)) { @@ -276,7 +307,7 @@ protected void makeMove(Pair coords, PlayableCard card, Side s // Place the card in the current player's board // and save the points possibly gained because of the move - int gainedPoints = currentPlayerBoard.placeCard(coords, card, side); + int gainedPoints = currentPlayerBoard.placeCard(coords, card, side, turn); // Remove the card from the player's hand // since it has been placed on the board @@ -300,4 +331,66 @@ protected void makeMove(Pair coords, PlayableCard card, Side s throw new WrongCardPlacementException("Card placement not valid!"); } } + + /** + * Draw a card from the requested source, and pick a new card from the deck if a visible card is chosen. + * Note: It's called by Player + * @param source represents the source of the draw + * @throws WrongStateException if called during a state that does not allow making moves + * @throws WrongChoiceException if the source does not have cards + * @return the card drawn + */ + protected PlayableCard drawCard(DrawSource source) throws WrongStateException, WrongChoiceException { + currentState.drawCard(); + PlayableCard card; + switch (source) { + case GOLDS_DECK: + if (goldsDeck.isEmpty()) + throw new WrongChoiceException("Golds deck is empty!"); + card = goldsDeck.pop(); + break; + case RESOURCES_DECK: + if (resourcesDeck.isEmpty()) { + throw new WrongChoiceException("Resources deck is empty!"); + } + card = resourcesDeck.pop(); + break; + case FIRST_VISIBLE_GOLDS: + card = visibleGolds.getFirst(); + if (card == null) + throw new WrongChoiceException("There is no visible gold in position one!"); + visibleGolds.setFirst(goldsDeck.poll()); + break; + case SECOND_VISIBLE_GOLDS: + card = visibleGolds.getSecond(); + if (card == null) + throw new WrongChoiceException("There is no visible gold in position two!"); + visibleGolds.setSecond(goldsDeck.poll()); + break; + case FIRST_VISIBLE_RESOURCES: + card = visibleResources.getFirst(); + if (card == null) + throw new WrongChoiceException("There is no visible resource in position one!"); + visibleResources.setFirst(resourcesDeck.poll()); + break; + case SECOND_VISIBLE_RESOURCES: + card = visibleResources.getSecond(); + if (card == null) + throw new WrongChoiceException("There is no visible resource in position two!"); + visibleResources.setSecond(resourcesDeck.poll()); + break; + default: + throw new WrongChoiceException("Unexpected value: " + source); + } + + if (goldsDeck.isEmpty() && resourcesDeck.isEmpty()) + lastTurn = true; + return card; + } + + protected void chooseInitialSide(Side side) throws WrongStateException { + // TODO + currentState.chooseInitialSide(); + currentPlayer.getBoard().setInitialSide(side); + } } From 312aae3b5c0f7f38fc99447d4e1522f36c2f1067 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 16:33:22 +0100 Subject: [PATCH 18/68] Implement Playcard, Drawcard, ChooseSecretObjective --- .../it/polimi/ingsw/gamemodel/Player.java | 29 ++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Player.java b/src/main/java/it/polimi/ingsw/gamemodel/Player.java index 695b13ed..388481ec 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Player.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Player.java @@ -1,5 +1,6 @@ package it.polimi.ingsw.gamemodel; import it.polimi.ingsw.utils.Pair; +import it.polimi.ingsw.exceptions.*; /** * Player represents each in-game user. The class also manages the board's logic @@ -28,26 +29,40 @@ public Player(String nickname, Match match) { * @param card the card to be placed * @param side whether the card should be placed on the front or on the back */ - public void playCard(Pair coord, PlayableCard card, Side side) { - //TODO + public void playCard(Pair coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException { + if (match.getCurrentPlayer().equals(this)) { + match.makeMove(coords, card, side); + } else { + throw new WrongTurnException("Only the current player can play cards"); + } } /** * Adds a card to the player's hand, popping it from the required source * @param source represents the source of the draw, which can be either one of the two decks or one of the four cards on the table */ - public void drawcard(DrawSource source) { - // TODO + public void drawcard(DrawSource source) throws WrongTurnException { + if (match.getCurrentPlayer().equals(this)) { + PlayableCard card = match.drawCard(source); + board.addHandCard(card); + } else { + throw new WrongTurnException("Only the current player can draw cards"); + } } /** * Sets the player private objective (only at the start of the game) * @param objective the chosen objective between the two proposed */ - public void chooseSecretObjective(Objective objective) { - // TODO + public void chooseSecretObjective(Objective objective) throws WrongTurnException { + if (match.getCurrentPlayer().equals(this)) { + match.chooseSecretObjective(objective); + secretObjective = objective; + } else { + throw new WrongTurnException("Only the current player can choose an objective"); + } } - + /** * Getter for the player's board */ From 3c8e0bfbb2f5b3be45a1a63f7ecab85bc39b2c11 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Thu, 21 Mar 2024 18:19:37 +0100 Subject: [PATCH 19/68] Update UML diagram: add some minor methods --- deliveries/UML/class_diagram.mmd | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index 3dc0d8d9..8ff55ec3 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -204,12 +204,14 @@ classDiagram - visibleObjectives: Pair~Objective, Objective~ - currentProposedObjectives: Pair~Objective, Objective~ - started: bool + - initialTurn: bool - lastTurn: bool - finished: bool + Match(int maxPlayers, resourceDeck ) void + isFull() bool + isStarted() bool + + isInitialTurnFinished() bool + isFinished() bool + addPlayer(Player player) void + removePlayer(Player player) void @@ -223,6 +225,7 @@ classDiagram # drawCard(DrawSource draw) PlayableCard # chooseInitialSide(Side side) void # doStart() void + # doInitialTurnFinish() void # doFinish() void # setState() void # setupDecks() void @@ -309,10 +312,11 @@ classDiagram class GameDeck { <> - int size - - cardsList: List~U~ + - cardsList: List~C~ + GameDeck(int size) - + add(U card) void - + pop() U + + add(C card) void + + pop() C + + poll() C + shuffle() void + isEmpty() void } From 51e952731d28c4a6813d5e39dcf2c85295a74b09 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Thu, 21 Mar 2024 15:54:46 +0100 Subject: [PATCH 20/68] Add poll to GameDeck, minor refactor --- .../java/it/polimi/ingsw/gamemodel/Board.java | 23 +++++++++++++++---- .../it/polimi/ingsw/gamemodel/GameDeck.java | 16 +++++++++---- .../ingsw/gamemodel/PlacementOutcome.java | 8 +++++++ 3 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 667e102f..76199420 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -63,8 +63,16 @@ protected void removeHandCard(PlayableCard card) { * @param side the side of the card to be placed * @return the points gained from playing card */ - protected int placeCard(Pair coord, Card card, Side side) { - return 0; + protected int placeCard(Pair coord, PlayableCard card, Side side, int turn) throws CardException { + PlacedCard last = new PlacedCard(card, turn); + placed.put(coord, last); + if (card instanceof GoldCard) { + return ((GoldCard)card).calculatePoints(this); + } else if (card instanceof ResourceCard) { + return ((ResourceCard)card).getPoints(); + } else { + throw new CardException("Unknow card type: " + card.getClass().toString() + "!"); + } } /** @@ -73,8 +81,15 @@ protected int placeCard(Pair coord, Card card, Side side) { * @param coord the x and y coordinates to check * @return whether the given coordinates are valid or not */ - public boolean verifyCardPlacement(Pair coord, Card card, Side side) { - return true; + public PlacementOutcome verifyCardPlacement(Pair coord, Card card, Side side) throws CardException { + if (!currentHand.contains(card)) { + throw new CardException("The card " + card.getClass().toString() + " is not in the player's hand!"); + } + if (placed.keySet().contains(coord)) { + return PlacementOutcome.INVALID_COORDS; + } + Pair cmp = new Pair(coord.first()-1, coord.second()); + return PlacementOutcome.VALID; } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java index 7b22a295..13e31afe 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java @@ -23,15 +23,23 @@ public void add(T card) { } - public T pop() { - return null; + public T pop() throws Exception { + if (this.isEmpty()) + throw new Exception("Tried to draw from an empty deck!"); + return cardsList.removeLast(); + } + + public T poll() { + if (this.isEmpty()) + return null; + return cardsList.removeLast(); } public void shuffle() { } - public void isEmpty() { - + public boolean isEmpty() { + return false; } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java b/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java new file mode 100644 index 00000000..66f97e2b --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.gamemodel; + +public enum PlacementOutcome { + VALID, + INVALID_COORDS, + INVALID_ENOUGH_RESOURCES +} + From 41c96a980ee97b9f3f69ef71f2d5fc603bc3a6b3 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Fri, 22 Mar 2024 00:16:50 +0100 Subject: [PATCH 21/68] Implement Board main methods (still missing resource), write first tests --- .../java/it/polimi/ingsw/gamemodel/Board.java | 123 ++++++++++++++++-- .../it/polimi/ingsw/gamemodel/CardFace.java | 13 +- .../java/it/polimi/ingsw/gamemodel/Match.java | 14 +- .../it/polimi/ingsw/gamemodel/PlacedCard.java | 18 ++- .../it/polimi/ingsw/utils/MutablePair.java | 27 ++++ .../it/polimi/ingsw/gamemodel/BoardTest.java | 74 +++++++++++ 6 files changed, 246 insertions(+), 23 deletions(-) create mode 100644 src/main/java/it/polimi/ingsw/utils/MutablePair.java create mode 100644 src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 18288cbc..fef693e1 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -6,12 +6,18 @@ import java.util.List; import java.util.Map; - import it.polimi.ingsw.utils.Pair; + +/* +- implement resource elaboration on placed cards +- Update Javadoc +- More elegant way for diagonl controls (they are very similar to resource update controls) +*/ + /** - * Board is the class that contains all the informations relative to a {@link Player}'s status - */ +* Board is the class that contains all the informations relative to a {@link Player}'s status +*/ public class Board { private List currentHand; private Map, PlacedCard> placed; @@ -60,8 +66,19 @@ protected void addHandCard(PlayableCard card) throws HandException { * During the first turn of the player, he will be asked if he wants to switch side with this method * @param side the desired side for the initial card */ - public void setInitialSide(Side side) { - + protected void setInitialCard(InitialCard card) throws CardException { + if (placed.get(new Pair<>(0,0)) != null) { + throw new CardException("Tried to add initial card, but one already exists!"); + } + placed.put(new Pair<>(0, 0), new PlacedCard(card, Side.FRONT, 0)); + } + /** + * The initial card will be added by {@link Match} at the start of the game, and it will be set on the front side by default. + * During the first turn of the player, he will be asked if he wants to switch side with this method + * @param side the desired side for the initial card + */ + protected void setInitialSide(Side side) { + placed.get(new Pair<>(0, 0)); } /** @@ -84,15 +101,36 @@ protected void removeHandCard(PlayableCard card) throws HandException { * @return the points gained from playing card */ protected int placeCard(Pair coord, PlayableCard card, Side side, int turn) throws CardException { - PlacedCard last = new PlacedCard(card, turn); - placed.put(coord, last); + PlacedCard last = new PlacedCard(card, side, turn); + this.placed.put(coord, last); + int points = 0; + if (card instanceof GoldCard) { - return ((GoldCard)card).calculatePoints(this); + points = ((GoldCard)card).calculatePoints(this); } else if (card instanceof ResourceCard) { - return ((ResourceCard)card).getPoints(); + points = ((ResourceCard)card).getPoints(); } else { throw new CardException("Unknow card type: " + card.getClass().toString() + "!"); } + + + + return points; + } + + private boolean hasDiagonalAdjacent(Pair coord) { + Integer[] offsets = {-1, +1}; + Pair cmp; + + for (Integer xOffset : offsets) { + for (Integer yOffset : offsets) { + cmp = new Pair<>(coord.first() + xOffset, coord.second() + yOffset); + if (placed.keySet().contains(cmp)) { + return true; + } + } + } + return false; } /** @@ -102,16 +140,77 @@ protected int placeCard(Pair coord, PlayableCard card, Side si * @return whether the given coordinates are valid or not */ public PlacementOutcome verifyCardPlacement(Pair coord, Card card, Side side) throws CardException { + if (coord.equals(new Pair<>(0, 0))) { + return PlacementOutcome.INVALID_COORDS; + } + if (!currentHand.contains(card)) { throw new CardException("The card " + card.getClass().toString() + " is not in the player's hand!"); } if (placed.keySet().contains(coord)) { return PlacementOutcome.INVALID_COORDS; } - Pair cmp = new Pair(coord.first()-1, coord.second()); - + if (card instanceof GoldCard && !((GoldCard)card).getRequirement().isSatisfied(this)) { + return PlacementOutcome.INVALID_ENOUGH_RESOURCES; + } + + + Integer[] offsets = {-1, +1}; + + Pair cmp; + + // cross check: none exists + for (Integer offset : offsets) { + cmp = new Pair<>(coord.first()+offset, coord.second()); + if (placed.keySet().contains(cmp)) { + return PlacementOutcome.INVALID_COORDS; + } + + cmp = new Pair<>(coord.first(), coord.second()+offset); + if (placed.keySet().contains(cmp)) { + return PlacementOutcome.INVALID_COORDS; + } + } + + // diagonal check: at least one exists and none has an empty corner + if (!hasDiagonalAdjacent(coord)) { + return PlacementOutcome.INVALID_COORDS; + } + + Symbol adjacentCornerSymbol; + cmp = new Pair(coord.first()-1, coord.second()+1); + if (placed.get(cmp) != null ) { + adjacentCornerSymbol = placed.get(cmp).getPlayedCardFace().getCorner(Corner.BOTTOM_RIGHT); + if (adjacentCornerSymbol == Symbol.EMPTY_CORNER) { + return PlacementOutcome.INVALID_COORDS; + } + } + + cmp = new Pair(coord.first()+1, coord.second()+1); + if (placed.get(cmp) != null ) { + adjacentCornerSymbol = placed.get(cmp).getPlayedCardFace().getCorner(Corner.BOTTOM_LEFT); + if (adjacentCornerSymbol == Symbol.EMPTY_CORNER) { + return PlacementOutcome.INVALID_COORDS; + } + } + + cmp = new Pair(coord.first()-1, coord.second()-1); + if (placed.get(cmp) != null ) { + adjacentCornerSymbol = placed.get(cmp).getPlayedCardFace().getCorner(Corner.TOP_RIGHT); + if (adjacentCornerSymbol == Symbol.EMPTY_CORNER) { + return PlacementOutcome.INVALID_COORDS; + } + } + + cmp = new Pair(coord.first()-1, coord.second()+1); + if (placed.get(cmp) != null ) { + adjacentCornerSymbol = placed.get(cmp).getPlayedCardFace().getCorner(Corner.BOTTOM_RIGHT); + if (adjacentCornerSymbol == Symbol.EMPTY_CORNER) { + return PlacementOutcome.INVALID_COORDS; + } + } - return true; + return PlacementOutcome.VALID; } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java b/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java index 92b0ad5c..088e4453 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java @@ -26,7 +26,18 @@ public CardFace(Symbol topLeft, Symbol topRight, Symbol bottomLeft, Symbol botto * @return the symbol the specified corner contains */ public Symbol getCorner(Corner corner) { - return topLeft; + switch (corner) { + case TOP_LEFT: + return this.topLeft; + case TOP_RIGHT: + return this.topRight; + case BOTTOM_LEFT: + return this.bottomLeft; + case BOTTOM_RIGHT: + return this.bottomRight; + default: + return null; + } } /** diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index e4293e23..b64ef96c 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -152,7 +152,7 @@ protected void setState(MatchState state) { * * @return */ - protected Pair proposeSecretObjectives() { + protected Pair proposeSecretObjectives() throws Exception { Objective obj1 = objectivesDeck.pop(); Objective obj2 = objectivesDeck.pop(); currentProposedObjectives = new Pair<>(obj1, obj2); @@ -185,7 +185,7 @@ protected void setupPlayers() { /** * */ - protected void setupDecks() { + protected void setupDecks() throws Exception { // Shuffle each deck initialsDeck.shuffle(); resourcesDeck.shuffle(); @@ -213,7 +213,7 @@ protected void setupDecks() { /** * */ - protected void setupBoards() { + protected void setupBoards() throws Exception { // Give starting cards to players for (Player player : players) { // Pop a card from the resources deck and one from the golds deck @@ -242,11 +242,11 @@ protected void setupBoards() { * @throws WrongStateException * @throws WrongCardPlacementException */ - protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongCardPlacementException { + protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException { Board currentPlayerBoard = currentPlayer.getBoard(); // If placing the card in the current player's board is allowed by rules - if (currentPlayerBoard.verifyCardPlacement(coords, card, side)) { + /* if (currentPlayerBoard.verifyCardPlacement(coords, card, side)) { // Trigger current state behavior currentState.makeMove(); @@ -283,8 +283,8 @@ protected void makeMove(Pair coords, PlayableCard card, Side s // the match is now finished if (currentPlayer.equals(players.getLast()) && lastTurn) finished = true; - } else { + } else { throw new WrongCardPlacementException("Card placement not valid!"); - } + }*/ } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java b/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java index a01b9305..0ada882a 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java @@ -6,15 +6,15 @@ public class PlacedCard { private Card card; private int turn; - + private Side playedSide; /** * @param card the {@link Card} played * @param turn the turn said card is played. Needed to know which card covers which, since a card played in a certain turn will * always cover one played before */ - public PlacedCard(Card card, int turn) { - this.card = card; this.turn = turn; + public PlacedCard(Card card, Side playedSide, int turn) { + this.card = card; this.turn = turn; this.playedSide = playedSide; } /** @@ -32,4 +32,16 @@ public Card getCard() { public int getTurn() { return this.turn; } + + /** + * Getter for the PlacedCard class + * @return side the card was played + */ + public Side getPlayedSide() { + return this.playedSide; + } + + public CardFace getPlayedCardFace() { + return this.card.getSide(this.playedSide); + } } diff --git a/src/main/java/it/polimi/ingsw/utils/MutablePair.java b/src/main/java/it/polimi/ingsw/utils/MutablePair.java new file mode 100644 index 00000000..a64fe82f --- /dev/null +++ b/src/main/java/it/polimi/ingsw/utils/MutablePair.java @@ -0,0 +1,27 @@ +package it.polimi.ingsw.utils; + +public class MutablePair { + private T first; + private U second; + + public MutablePair(T first, U second) { + this.first = first; + this.second = second; + } + + public T getFirst() { + return first; + } + + public void setFirst(T first) { + this.first = first; + } + + public U getSecond() { + return second; + } + + public void setSecond(U second) { + this.second = second; + } +} diff --git a/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java b/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java new file mode 100644 index 00000000..7ad981c6 --- /dev/null +++ b/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java @@ -0,0 +1,74 @@ +package it.polimi.ingsw.gamemodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.junit.Test; + +import it.polimi.ingsw.gamemodel.*; +import it.polimi.ingsw.utils.*; + +/** + * Unit test for simple App. + */ +public class BoardTest +{ + /** + * Rigorous Test :-) + */ + @Test + public void shouldAnswerWithTrue() { + Board board = new Board(); + + + try { + + InitialCard initial = new InitialCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.PLANT)), + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.PLANT)) + ); + + PlayableCard init = new ResourceCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.PLANT)), + Symbol.PLANT, 1 + ); + + PlayableCard empty = new ResourceCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.EMPTY_CORNER, Set.of(Symbol.PLANT)), + Symbol.PLANT, 1 + ); + Map m = new HashMap(); + m.put(Symbol.PLANT, 2); + + PlayableCard gold = new GoldCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.EMPTY_CORNER, Set.of(Symbol.PLANT)), + Symbol.PLANT, Symbol.INKWELL, 2, new QuantityRequirement(m) + ); + + board.addHandCard(init); + board.addHandCard(empty); + board.addHandCard(gold); + assertEquals(board.verifyCardPlacement(new Pair<>(0, 0), initial, Side.FRONT), PlacementOutcome.INVALID_COORDS); + board.setInitialCard(initial); + board.placeCard(new Pair(1, 1), init, Side.FRONT, 0); + board.placeCard(new Pair(2, 0), empty, Side.FRONT, 0); + board.placeCard(new Pair(-1, -1), init, Side.FRONT, 0); + board.placeCard(new Pair(0, -2), init, Side.FRONT, 0); + board.placeCard(new Pair(1, -3), init, Side.FRONT, 0); + board.placeCard(new Pair(2, -2), init, Side.FRONT, 0); + assertEquals(board.verifyCardPlacement(new Pair<>(3, -1), init, Side.FRONT), PlacementOutcome.INVALID_COORDS); + assertEquals(board.verifyCardPlacement(new Pair<>(-1, -1), init, Side.FRONT), PlacementOutcome.INVALID_COORDS); + assertEquals(board.verifyCardPlacement(new Pair<>(0, -1), init, Side.FRONT), PlacementOutcome.INVALID_COORDS); + assertEquals(board.verifyCardPlacement(new Pair<>(2, 2), empty, Side.FRONT), PlacementOutcome.VALID); + assertEquals(board.verifyCardPlacement(new Pair<>(2, 2), gold, Side.FRONT), PlacementOutcome.INVALID_ENOUGH_RESOURCES); + + } catch (Exception e) { + System.err.println(e); + assertTrue(false); // not expecting any exception for now, if exception was expected remove this and adapt + } + } +} From 49e6466eba0df808f38bfc5dbbf7cdf93854ab8b Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Fri, 22 Mar 2024 17:40:38 +0100 Subject: [PATCH 22/68] Update Javadoc of Match --- .../java/it/polimi/ingsw/gamemodel/Match.java | 115 ++++++++++-------- 1 file changed, 66 insertions(+), 49 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index db189344..20c7f132 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -7,6 +7,15 @@ import java.util.List; import it.polimi.ingsw.exceptions.*; +/** + * Represents the match being played by {@link Player} instances, therefore implements a slice of game logic + * using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc. + * Other methods serve the purpose of being called by {@link MatchState} subclasses in order to notify the change + * of the current game state or trigger some changes in the match, such as setupBoards(...), dofinish(...), + * doStart(...), etc. + * Few methods are called by the current player of the match, used to trigger a change in the match and so notify that + * an event occurred, such as nextPlayer(...). + */ public class Match { private final List players; private final int maxPlayers; @@ -38,7 +47,7 @@ public class Match { /** - * Initialize main Match attributes and allocate the attribute players List, assuming no parameter is null. + * Initializes main Match attributes and allocate the attribute players List, assuming no parameter is null. * @param maxPlayers maximum number of players to be added to the match, chosen by the first player joining the match * @param initialsDeck deck of initial cards * @param resourcesDeck deck of resource cards @@ -55,10 +64,9 @@ public Match(int maxPlayers, GameDeck initialsDeck, GameDeck(); } - // Called by the controller /** - * Add a new player to the match, assuming it's not null. - * Note: It's called by the Controller when a player joins the match. + * Adds a new player to the match, assuming it's not null. + * Note: Called by the Controller when a player joins the match. * @param player player to be added to the match * @throws IllegalArgumentException if the player is already in the match */ @@ -72,8 +80,8 @@ public void addPlayer(Player player) throws IllegalArgumentException, WrongState } /** - * Remove a player from the match, assuming the player is in the match. - * Note: It's called by the Controller when a player quits the match. + * Removes a player from the match, assuming the player is in the match. + * Note: Called by the Controller when a player quits the match. * @param player player to be removed from the match */ public void removePlayer(Player player) { @@ -81,8 +89,8 @@ public void removePlayer(Player player) { } /** - * Verify if the match is full, thus no more players can join. - * Note: It's used by the Controller + * Verifies if the match is full, thus no more players can join. + * Note: Used by the Controller * @return true if the match is full, false otherwise */ public boolean isFull() { @@ -90,11 +98,11 @@ public boolean isFull() { } /** - * Modify the current player according to the next turn: + * Modifies the current player according to the next turn: * If it's the first turn, the current player gets initialized as the * first one in the players List; the turn order then follows the players List order, in a circular way. * Ex. 1 -> 2 -> 3 -> 1 -> etc. - * Note It's called by NextTurnState every time a new turn starts. + * Note: Called by NextTurnState every time a new turn starts. */ protected void nextPlayer() { // If player has never been initialized OR the current player is the last one @@ -109,16 +117,16 @@ protected void nextPlayer() { } /** - * Mark the match as finished, assuming the match hasn't finished yet. - * Note: It's called by FinalState once the match is ready to finish. + * Marks the match as finished, assuming the match hasn't finished yet. + * Note: Called by FinalState once the match is ready to finish. */ protected void doFinish() { finished = true; } /** - * Verify if the match is finished. - * Note: It's called by the Controller and NextTurnState. + * Verifies if the match is finished. + * Note: Called by the Controller and NextTurnState. * @return true if the match is finished, false otherwise */ public boolean isFinished() { @@ -126,16 +134,16 @@ public boolean isFinished() { } /** - * Mark the initial turn as finished, assuming the initial turn hasn't finished yet. - * Note: It's called by ChooseInitialCardState once the initial turn is finished. + * Marks the initial turn as finished, assuming the initial turn hasn't finished yet. + * Called by ChooseInitialCardState once the initial turn is finished. */ protected void doInitialTurnFinish() { initialTurnFinished = true; } /** - * Verify if the initial turn is finished. - * Note: It's called by NextTurnState. + * Verifies if the initial turn is finished. + * Note: Called by NextTurnState. * @return true if the initial turn is finished, false otherwise */ public boolean isInitialTurnFinished() { @@ -143,16 +151,16 @@ public boolean isInitialTurnFinished() { } /** - * Mark the match as started, assuming the match hasn't started yet. - * Note: It's called by ChooseSecretObjectiveState once the match is ready to start. + * Marks the match as started, assuming the match hasn't started yet. + * Note: Called by ChooseSecretObjectiveState once the match is ready to start. */ protected void doStart() { started = true; } /** - * Verify if the match is started. - * Note: It's called by NextTurnState to check when to effectively start the match. + * Verifies if the match is started. + * Note: Called by NextTurnState to check when to effectively start the match. * @return true if the match is started, false otherwise */ public boolean isStarted() { @@ -160,8 +168,8 @@ public boolean isStarted() { } /** - * Get the player who's playing (or choosing the secret objective) at the moment. - * Note: It's used by the Controller. + * Gets the player who's playing (or choosing the secret objective) at the moment. + * Note: Used by the Controller. * @return the player playing at the moment, null if the match has never reached NextTurnState */ public Player getCurrentPlayer() { @@ -169,7 +177,7 @@ public Player getCurrentPlayer() { } /** - * Get the match players. + * Gets the match players. * @return the match players in a List, dynamically defined as an ArrayList */ public List getPlayers() { @@ -177,8 +185,8 @@ public List getPlayers() { } /** - * Set the current match state, assuming it's not null. - * Note: It's called by each state to let the match enter to the next state. + * Sets the current match state, assuming it's not null. + * Note: Called by each state to let the match enter to the next state. * @param state the state in which the match has to be */ protected void setState(MatchState state) { @@ -186,27 +194,29 @@ protected void setState(MatchState state) { } /** - * Extract two cards from the deck of objectives and return them. - * Note: It's called by the controller. - * @return the two cards extracted + * Extracts two cards from the deck of objectives and returns them. + * Note: Called by the Controller. + * @return two objective cards extracted from the objectives deck */ protected Pair proposeSecretObjectives() { Objective obj1 = objectivesDeck.pop(); Objective obj2 = objectivesDeck.pop(); + currentProposedObjectives = new Pair<>(obj1, obj2); return currentProposedObjectives; } /** - * Check that the given objective is one of the proposed ones to the current player + * Checks that the given objective is one of the proposed ones to the current player * and put the discarded objective back in the objectives deck. - * Note: It's called by Player - * @param objective the accepted objective by the player + * Note: Called by the current player + * @param objective the accepted objective by the player (NOT the discarded one) */ protected void chooseSecretObjective(Objective objective) throws WrongThreadException { // Get proposed objectives Objective firstProposedObjective = currentProposedObjectives.first(); Objective secondProposedObjective = currentProposedObjectives.second(); + // Check if the chosen objective is one of the proposed ones and put it back in the deck if (objective.equals(firstProposedObjective)) { objectivesDeck.add(secondProposedObjective); @@ -215,13 +225,13 @@ protected void chooseSecretObjective(Objective objective) throws WrongThreadExce } else { // If the objective is not one of the proposed ones // throw an exception - throw new WrongThreadException("The chosen objective is not one of the proposed ones"); + throw new WrongThreadException("The chosen objective is not one of the proposed ones"); } } /** - * Shuffle the players turn order and give them their pawn color - * Note: It's called by SetupState + * Shuffles the players turns order and gives them their pawn color. + * Note: Called by SetupState. */ protected void setupPlayers() { // Shuffle players List @@ -234,8 +244,8 @@ protected void setupPlayers() { } /** - * Shuffle cards decks and place the visible cards on the board - * Note: It's called by SetupState + * Shuffles all thr cards decks and places the visible cards on the board + * Note: Called by SetupState. */ protected void setupDecks() { // Shuffle each deck @@ -263,9 +273,9 @@ protected void setupDecks() { } /** - * Give one gold card and two resource cards to each player hand - * and set the initial card - * Note: It's called by WaitState + * Gives one gold card and two resource cards to each player (hand) + * and sets the initial card for each of them. + * Note: Called by WaitState. */ protected void setupBoards() { // Give starting cards to players @@ -288,8 +298,10 @@ protected void setupBoards() { } /** - * Check if the placement is valid, then place the card on the board and add points to the player - * Note: It's called by Player + * Makes the chosen move on the board of the current player (known because of the internal Match state); + * in particular checks if the placement is valid, then places the card on the player's board and add points + * to the player. + * Note: Called by the current player. * @param coords coordinates in which to place the card * @param card card to place * @param side side of the card to be placed @@ -333,10 +345,11 @@ protected void makeMove(Pair coords, PlayableCard card, Side s } /** - * Draw a card from the requested source, and pick a new card from the deck if a visible card is chosen. - * Note: It's called by Player + * Draws a card from the requested source; if a visible card is chosen, this one gets substituted by a new one + * drawn by a deck. + * Note: Called by the current player. * @param source represents the source of the draw - * @throws WrongStateException if called during a state that does not allow making moves + * @throws WrongStateException if called while in a state that doesn't allow making moves * @throws WrongChoiceException if the source does not have cards * @return the card drawn */ @@ -350,9 +363,8 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W card = goldsDeck.pop(); break; case RESOURCES_DECK: - if (resourcesDeck.isEmpty()) { + if (resourcesDeck.isEmpty()) throw new WrongChoiceException("Resources deck is empty!"); - } card = resourcesDeck.pop(); break; case FIRST_VISIBLE_GOLDS: @@ -360,7 +372,7 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W if (card == null) throw new WrongChoiceException("There is no visible gold in position one!"); visibleGolds.setFirst(goldsDeck.poll()); - break; + break; case SECOND_VISIBLE_GOLDS: card = visibleGolds.getSecond(); if (card == null) @@ -388,6 +400,11 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W return card; } + /** + * Sets the current player's initial card side + * @param side the side to put the initial card on + * @throws WrongStateException if called while in a state that doesn't allow choosing the initial card side + */ protected void chooseInitialSide(Side side) throws WrongStateException { // TODO currentState.chooseInitialSide(); From ca9edf1d4e7970d813987010e6ab043df60c8f87 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Fri, 22 Mar 2024 17:57:20 +0100 Subject: [PATCH 23/68] Implement drawInitialCard method and rename some methods - Add drawInitialCard method and relative state handling - Rename Match.chooseSecretObjective to Match.getSecretObjective - rename match.getInitialSide to match.setInitialSide --- .../gamemodel/ChooseSecretObjectiveState.java | 2 +- .../java/it/polimi/ingsw/gamemodel/Match.java | 100 +++++++++++------- .../it/polimi/ingsw/gamemodel/MatchState.java | 6 +- .../polimi/ingsw/gamemodel/NextTurnState.java | 9 +- .../it/polimi/ingsw/gamemodel/Player.java | 18 +++- 5 files changed, 90 insertions(+), 45 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java b/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java index 0699042d..9ba40749 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java @@ -7,7 +7,7 @@ public ChooseSecretObjectiveState(Match match) { } @Override - public void chooseSecretObjectives() { + public void chooseSecretObjective() { Player lastPlayer = match.getPlayers().getLast(); if (match.getCurrentPlayer().equals(lastPlayer)) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index 20c7f132..e79a76fd 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -35,7 +35,7 @@ public class Match { private Pair visibleObjectives; private Pair currentProposedObjectives; - + private InitialCard currentGivenInitialCard; // Denotes if the match has been started or has finished private boolean started = false; private boolean initialTurnFinished = false; @@ -109,6 +109,7 @@ protected void nextPlayer() { if (currentPlayer == null || currentPlayer.equals(players.getLast())) { // Set currentPlayer as the first one currentPlayer = players.getFirst(); + turn++; } else { // Get the index of the current player and choose the next one int currentPlayerIndex = players.indexOf(currentPlayer); @@ -193,6 +194,11 @@ protected void setState(MatchState state) { this.currentState = state; } + protected InitialCard drawInitialCard() throws Exception, WrongStateException { + currentState.drawInitialCard(); + currentGivenInitialCard = initialsDeck.pop(); + return currentGivenInitialCard; + } /** * Extracts two cards from the deck of objectives and returns them. * Note: Called by the Controller. @@ -212,7 +218,8 @@ protected Pair proposeSecretObjectives() { * Note: Called by the current player * @param objective the accepted objective by the player (NOT the discarded one) */ - protected void chooseSecretObjective(Objective objective) throws WrongThreadException { + protected void setSecretObjective(Objective objective) throws WrongChoiceException, WrongStateException { + currentState.chooseSecretObjective(); // Get proposed objectives Objective firstProposedObjective = currentProposedObjectives.first(); Objective secondProposedObjective = currentProposedObjectives.second(); @@ -225,7 +232,7 @@ protected void chooseSecretObjective(Objective objective) throws WrongThreadExce } else { // If the objective is not one of the proposed ones // throw an exception - throw new WrongThreadException("The chosen objective is not one of the proposed ones"); + throw new WrongChoiceException("The chosen objective is not one of the proposed ones"); } } @@ -289,11 +296,6 @@ protected void setupBoards() { player.getBoard().addHandCard(goldCard); player.getBoard().addHandCard(resourceCard1); player.getBoard().addHandCard(resourceCard2); - - // Place the initial card to the player's board - InitialCard initialCard = initialsDeck.pop(); - player.getBoard().setInitialCard(initialCard); - } } @@ -310,37 +312,43 @@ protected void setupBoards() { */ protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException { Board currentPlayerBoard = currentPlayer.getBoard(); - // TODO: Fix implementation with new verifyCardPlacement method + // TODO: Improve error handling and identation // If placing the card in the current player's board is allowed by rules - if (currentPlayerBoard.verifyCardPlacement(coords, card, side)) { - - // Trigger current state behavior - currentState.makeMove(); - - // Place the card in the current player's board - // and save the points possibly gained because of the move - int gainedPoints = currentPlayerBoard.placeCard(coords, card, side, turn); - - // Remove the card from the player's hand - // since it has been placed on the board - currentPlayerBoard.removeHandCard(card); - - // Update the current player's points - currentPlayer.addPoints(gainedPoints); - - // If the current player reaches 20 points or more - // the last turn of the match starts - if (currentPlayer.getPoints() >= 20) - lastTurn = true; - - // If the current player is the last one in the match turns rotation - // i.e. the last one in the players List - // AND the current turn is the last one - // the match is now finished - if (currentPlayer.equals(players.getLast()) && lastTurn) - finished = true; - } else { - throw new WrongCardPlacementException("Card placement not valid!"); + PlacementOutcome outcome = currentPlayerBoard.verifyCardPlacement(coords, card, side); + switch (outcome) { + case VALID: + // Trigger current state behavior + currentState.makeMove(); + + // Place the card in the current player's board + // and save the points possibly gained because of the move + int gainedPoints = currentPlayerBoard.placeCard(coords, card, side, turn); + + // Remove the card from the player's hand + // since it has been placed on the board + currentPlayerBoard.removeHandCard(card); + + // Update the current player's points + currentPlayer.addPoints(gainedPoints); + + // If the current player reaches 20 points or more + // the last turn of the match starts + if (currentPlayer.getPoints() >= 20) + lastTurn = true; + + // If the current player is the last one in the match turns rotation + // i.e. the last one in the players List + // AND the current turn is the last one + // the match is now finished + if (currentPlayer.equals(players.getLast()) && lastTurn) + finished = true; + break; + case INVALID_COORDS: + throw new WrongChoiceException("Invalid coordinates!"); + break; + case INVALID_ENOUGH_RESOURCES: + throw new WrongChoiceException("Not enough resources!"); + break; } } @@ -405,9 +413,19 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W * @param side the side to put the initial card on * @throws WrongStateException if called while in a state that doesn't allow choosing the initial card side */ - protected void chooseInitialSide(Side side) throws WrongStateException { - // TODO + protected void setInitialSide(Side side) throws WrongStateException { currentState.chooseInitialSide(); - currentPlayer.getBoard().setInitialSide(side); + currentPlayer.getBoard().setInitialCard(currentGivenInitialCard, side); + currentGivenInitialCard = null; + } + + /** + * Returns the visible objectives + * + * @return a pair containing two objectives that are visible + * + */ + public Pair getVisibleObjectives() { + return visibleObjectives; } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java index 2f44021e..ac0d0ef5 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java @@ -18,6 +18,10 @@ public void removePlayer() throws WrongStateException{ throw new WrongStateException("removePlayer not allowed from the current match state!"); } + public void drawInitialCard() throws WrongStateException{ + throw new WrongStateException("deawInitialCard not allowed from the current match state!"); + } + public void chooseInitialSide() throws WrongStateException{ throw new WrongStateException("chooseInitialSide not allowed from the current match state!"); } @@ -34,7 +38,7 @@ public void proposeSecretObjectives() throws WrongStateException{ throw new WrongStateException("proposeSecretObjective not allowed from the current match state!"); } - public void chooseSecretObjectives() throws WrongStateException { + public void chooseSecretObjective() throws WrongStateException { throw new WrongStateException("chooseSecretObjective not allowed from the current match state!"); } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java index af466835..2be3df22 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java @@ -5,7 +5,6 @@ public class NextTurnState extends MatchState { public NextTurnState(Match match) { super(match); - match.nextPlayer(); } @@ -25,6 +24,14 @@ public void makeMove() throws WrongStateException { throw new WrongStateException("makeMove called when match was not started yet"); } + @Override + public void drawInitialCard() throws WrongStateException { + if (match.isInitialTurnFinished()) + throw new WrongStateException("drawInitialCard called after the initial turn was finished"); + else + this.transition(); + } + @Override public void transition() { MatchState nextState; diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Player.java b/src/main/java/it/polimi/ingsw/gamemodel/Player.java index 388481ec..0a9dab77 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Player.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Player.java @@ -37,11 +37,27 @@ public void playCard(Pair coords, PlayableCard card, Side side } } + public InitialCard drawInitialCard() throws WrongTurnException, Exception, WrongStateException { + if (match.getCurrentPlayer().equals(this)) { + InitialCard card = match.drawInitialCard(); + return card; + } else { + throw new WrongTurnException("Only the current player can draw the initial card"); + } + } + + public void chooseInitialCardSide(Side side) throws WrongTurnException, WrongStateException { + if (match.getCurrentPlayer().equals(this)) { + match.setInitialSide(side); + } else { + throw new WrongTurnException("Only the current player can choose the initial card side"); + } + } /** * Adds a card to the player's hand, popping it from the required source * @param source represents the source of the draw, which can be either one of the two decks or one of the four cards on the table */ - public void drawcard(DrawSource source) throws WrongTurnException { + public void drawcard(DrawSource source) throws WrongTurnException, WrongChoiceException { if (match.getCurrentPlayer().equals(this)) { PlayableCard card = match.drawCard(source); board.addHandCard(card); From afa8f411f915e5824f366cc4a73cde642f619d01 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Fri, 22 Mar 2024 18:02:39 +0100 Subject: [PATCH 24/68] Update UML diagram: change some methods --- deliveries/UML/class_diagram.mmd | 14 ++++++++------ deliveries/UML/model.pdf | Bin 96137 -> 98249 bytes 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index 8ff55ec3..49726b3c 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -176,11 +176,10 @@ classDiagram # getPlacedMap() Map<Pair<Integer,Integer>, PlacedCard> # addHandCard(PlayableCard card) void # removeHandCard(PlayableCard card) void - # setInitialSide(Side side) void # placeCard(Pair~Integer, Integer~, PlayableCard card, Side side, int turn) int - # setInitialCard(InitialCard card) void + # setInitialCard(InitialCard card, Side side) void + verifyCardPlacement(Pair~Integer, Integer~ coords, Card card, Side side) PlacementOutcome - } + } class PlacedCard { <> - card: Card @@ -203,6 +202,7 @@ classDiagram - visibleResources: Pair~ResourceCard, ResourceCard~ - visibleObjectives: Pair~Objective, Objective~ - currentProposedObjectives: Pair~Objective, Objective~ + - currentGivenInitialCard: InitialCard - started: bool - initialTurn: bool - lastTurn: bool @@ -219,11 +219,11 @@ classDiagram + getPlayers() List~Player~ # getPoints() int # addPoints() void - # chooseSecretObjective(Objective obj) void + # setSecretObjective(Objective obj) void # proposeSecretObjectives() Pair~Objective, Objective~ # makeMove(Pair~Integer, Integer~ coords, PlayableCard card, Side side) void # drawCard(DrawSource draw) PlayableCard - # chooseInitialSide(Side side) void + # setInitialSide(Side side) void # doStart() void # doInitialTurnFinish() void # doFinish() void @@ -253,7 +253,8 @@ classDiagram + removePlayer() void + proposeSecretObjectives() void + chooseSecretObjective() void - + chooseInitialSide(Side side) void + + chooseInitialSide() void + + drawInitialCard() void + makeMove() void + drawCard() void @@ -274,6 +275,7 @@ classDiagram + NextTurnState(Match match) void + proposeSecretObjectives() void + makeMove() void + + drawInitialCard() void + transition() void } class ChooseInitialSideState{ diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index c3f25d17011883dd066ff7d6797eaefd8b09162b..dd5fff13ac230fec2ba0f05379645b1e81e4236b 100644 GIT binary patch delta 36487 zcmZ5`byQVR(>|@ly%?0VA}HORBF!ZPY3c6nxODfXI|TuyLAnL$5Tv_PDFqaNhxhya z@qXW0uBFdD=gyv)J@Y(sjwj*-^Xos%czp&Ac3w6P4qiq!winPJdK^{-dlLym7gOq& z5&|4-99(Ri9RL58gop|x4?C}j2(`0|lc}LCjz`v(@4_c?{+gwh_JSPP%dlU#Y$Rfn z2&&&}C=KrDxBk^*?9oeD+rR&zRuXy|n>lJEd`*16b!=B(rdd&H!bbAzzTy|NtDW~h zPIf+cl-H`N$~AWUyWFG7{r%zcOSz5;z2fa9)xWbTD!$}{rmk(33*NIee>%dW9K^@qO$_JBIM1v{?8LpkrDlAy&bmV-Jd!$3OqT; zk*%WB%cwKKjTff2@V)oO>!p!CpNlig^QqbF1bsZWz zkb_oE^Ykc_3>mWbx|y&77Qe3NYQjma{r7uPKk}&ZA?M6Ei_cQCr+^Z<5Q>9`@2}T- zecq*dVW!)tD5WR^9y$#bawM}K!gRil{dAeL&b9NPmVcf>)_#I?a(lWMxW-sHBJh_g zm(KN%w7D&N33b5O%@F>J%;=+;LE?_^JP96RDTJeF_k3BB>b&g=K+%vpq(?RnzeWl^u(ZgwP4UZ z)863GZ}ymbGP6SV951`**sRIw+t)DA8(9+cqVZSh)j_}ZC!{lyY--JRv|A(U>7Q=g zUB^?I4;z5GIEk`omyI{(>!1j+7chW9W8AP=i6^n6zi|4dz-3#-re~aOiVKWdar0h5A zAAu^1B9MMaVS&4-gK0jX@N}+IAYc0x``W}1%Aw5uxR8e;6Q6x|HO}Jm`l$22QO^>&}IH74pJq|iad&E_PI>YVM*|=$}wa-I>TCO#l4O2(WcD46GYku z+y0N_ulM>)SDo)o^i2qsKA*Z;UDKpjik>IKFv1yk<33;cyLEddoQ9Q{v{5kSq&MlK za=UjgB#B5?YF^C77(IF0m=yK|PKRAkH;&O|R7fm`KKBGXN(~5F>#XV;Lo047Q4SU7 zlT=U;@tQ~CDEqU^P~IPfQJ&Ll{;;c8)^1L=_hgD{e1LZQJB0d1SW(O4$9-k^wUCE% zq_}nfIlyVH3ys_TswKBzY-s&0*Zgr^Z`NFvT+VL1&6pMYxKV>RX+> z<=1}Jn&c?EPSpCCQ5b7q=G>AEBHdt|o@IunTVZW51zz9m8=&>?|xHyb2=nq0#5A_Zsu)!yibbck#;VdlK1*cV4W?@{{^o`>lo6 z63T|j?jBOwy)utKL}kE?El6(-R7Ydpa{lS`uN7;zq_{3mNHV+Th1}!m3A26cdvds; z>aettfN$LueuOpT`)zzKZW9o&WE z-)Vv@eI`~p`e%Yn;YqmSizJLLex)BSJQHqZ*-YD$ z{;ubW>qwG7#|rZ>g$JB-Z|+;oPUdOCiF?)YkjHiEcqv8dcpc%BPzAx7`wH>@q?y9)1HjzhaUy+%7d&=y4+qWRO#~P|vcy4^g zbb(Sf9h+gg*%VkG)MH{m{QB4Ey6Cg|*}N_bAy3@o>Z+=SEX=wfrjVZ^wN>7ww7RDF za7i8Ph9m{WfEP#>y_3gQ{(e^Tg9MRm^m|C}NJS4#f?R^p@|W8Fj26?_d0?`LFO!at zJ1r|;i=x7VUd&VZZ+;Ca`W&jB*aaRL!E1taY*q3|MZBn2TJ5y9g!UTq^I>{YI;2YS z3OcI14!E|KRobCmo^<$| z_PwDdaTC07kxpg*5zvM5P(C-4r_*bI&hbGF$4gqY$xc3n_0MeD2{}=}W)ca=`qo9Ng3`xBtw}#Fe7h}d-EEz&$NcJ*;k>HMrzVAr%Oxcdb~*50%BS5KQRopan=qJGR@Kr3`Y)P`o)?yk$uB>jVp~o z)6Ve$#Zi{{9{=axEDR3v+r`}QGPG+{%tK0-Kc2`Y*~5-rJgoGk9&6?? zetGk9n&q&W(f9|pTiY;~!CQ4qhJO(skZ5dGBnl`mf_?I_?s3}=x?F=-Vw>ZsDfPL} z1@dGgH5+)o9slIV09CB_wZhe%ED@= z5TPvupedD2PB?oHth^X{`mzfVcnE^4Vh&pTzqY!oxx2nCB!kb@#H^+4zKpAx6IS#2g(P z+^gc4RpV=;#wMRJcoY&Dz%4Q%Y+M=?!i3e*g(d#4{~BiOH9;mlid9ZVWx$jukNLoC zFzO=auj4F>G(N_+E`%ewjJf5*LZRUqONM{W8M2$2M!!RYOL$~)4y*%TAsGuR!_l8B zImPI08t1L}5280rgDFkA2!3)-E%yR%anq_Si4JkB?;E8do)11`ty>tH&L?zRIpCYo zCCI2!qU4vwJBbii{=()K6J?A+O?9E2R7;_KX&G*_&m{DwR?h4v-#TuXKW_8~5)y16 zFR;k8IheHfGSGJQ{Qwna6SC6;cT->P8BfZy331|pd?^mYbJg7sBvVlbhiqogb6F`n zU@gBeC9vw_8t~jW$4Bs_o)N`&r}yv&9#aVE1yGgnb5g5Xj;(D!_I)BdjIpgaQJ8Hn z!cdEK&niCdDIza(@gc$BA5vzloy-4`4q8q&PXX#^&6J`lCxZ#()Czu~2ouJe=nzvg z{**^@=*6zktj~UDx*vHwSc}u% z&KQ@J{ZNV`m6)1_6voJBkexxn%HP|gEkPfhz@kn4b$%*9cZEZR!a7*%eSCuO3x3qk zL|>TxaX9)taeUk*|JcmaB}G*k&)92g(&rgc#TyzydO^2Fl&?j^!N3z76RTaJ>`^jTMPYZLl*`V3vg`vM23|Tu29ilIPh9Kc5W7oG+>HmZV7ik}% zD|Ap`1?JWl{_3&dayL?{7}GioI9ADvG;_-8e|bqTof>2m%3kXAU^v8x&X^@p9d`sF z)wQPgKHZd#JTsh=C?*sKjNgnSd-@P?l-UJ;-pC znq46IIVVZE`g)~lsHjYor#R2qz4X&hmhPt8d#(N#PnvZLWfW)KSM|*lOA7Vug<^H6 zZ<58BwWwfb{Sq70>HI>H9C!`cu;FABUlfa3?g@mifs+VTyvdvk8Mwm90QP|uv z$mi~1g=!y4*#cgnidS(_WEIP8QRlc|u=ONGyGL^v2ShN%>t)1A8p>UyTr!qs>bc!_ zk!B6YNO;nPJ={{@Q=hOGAK{W|uFcZFzP`-ZKj|b8yE5v`(yXejlz7FGp7P*St|BMP z6XnmN^D(_G4HKTd{^Z_}ov~Q;ceZ&Pyymbz!ArW_0^VbJ%U72b;*qxL&Qer5JN7P~I{ULuU(bNN z#CJ7H8{stnK5>H+H1X4rf?h0(AQ4$WuIlVy)~e}?-26;LAhSYYA*774olNpG_G<$* zC7o7+%{j(MoHhyI6C2f&8%jHR_U$cvyJ}pQePHp&RBGXyp(}-}HX07KQ|(HnNg~ph|}KVnr17i8ewK?j1>Z z6M0&?StYRbb6BLNC}^UCDlg4?Ouw)Fq~p3@8959)Am83p&P*HC#;Z~^`?TXJif1p` zv6bhHi_3#bPKYIZuV*+=!GQd}xpwdEwW-s_s*LaR;;o^HKwSnRmRHzIk8okKY|`82 zH>vSjCWPd=Oo~GIaGV8k=m};eYA|txiDD@K_p~Thay3)FQGN>=b!s;u;0?LvFh9&Qpu`|Pdy(}Y_ShW|Fx{bk=%~Cy(fLGi@OqNPMBz_ zwRNoXWAfIYzC1eqchGCylDzMg$Hwd*^_TjQlc$gS?RPYi#H~I)$v1lYfQWp6A&sg4 z5FYvueJK-+}L2mkvY)fh(zBG$LAJz?WiuRm+|OrA?#|X-KQE~ z)hq5Y6?orDps!qRWVnkTF4K);bnW>Pd1+qA@#86xugVi+$7u6V*!OstbN|-ANwCLP zRk_W1){%Fy;BtfD%?q2r&}I6;p62&-DT8jzi9|hsM4hjvj=Hy{D?5@bubturyFD@! zYsho=FW1D%$k{>NlTf0A8@dK)%&+{sKG%Y%9F$o;Y4LYHBJB(eu zh6epd-}OcR?sY8Q{@D@zd(imrkA}}_YKIT>Pw3@QNJvnl{y*6BrwX5c69;cpcKBY` zst{q|NR|8auAm~@Z?`TF{@tE8BL5;U_v|}ZSnB`q>9VV~4#0JklR$=X3 z<6Ja!|1bzSH19I)Hc&oJHLYLy;oiMMiLA2v``t6v=hl{zMv7~B;ob`8^}_~od>Lhx z?2nG)RoUO;>(QHInWn-jl9}2?q&h?So+&)G#DCarXUJ*_2m^2Y$P6Tlj|H*g|W+=BSIaYof zp29P%CW;KEZx1JY)9W%RN}uP5!f zj^pOPPqsP_{903-t=)#IBZYm#icL_h4Z;LI7D@|#8;GMkRzZ)pH4pU!W~V`kp}rX+ z3?DbPHdH!EXJbUp4$^U$ksD?WmRlzwX0j!sYj&1OBbkogdqzBYJ;ZrfJl4Y;OuX_E zju9S9CF%Ch+O7ogA2R#Vj=jZXyd`V4V}0gzkK2D&q{r!Eup&x#J`Fvx&!Y_$e?oF)Fe8S9|-TUB_+QLM|@Dbm{{$bZ(^KUJL{23d+S{ z9v0h-Ikuf$NJu0R6Te_%hVPRUa^cKDGj+^n`W;lJqdcstZWcj%Ui5>5+gVkDD1YELjfu|LlQ&`nTUnPFGnMfw|T>t=0MRL ze`;U=e2uu7=o!Z4Kgy$!ySo@%sQuEMJ=;&Tl)Dyb*5rsGH}d5LO|UG! zmAedjsVygYuu2Ehkp5y^+neTj)RVYbSyLQ>5f|``k~grOa1!Nuz9O9SnzGE3kSjfQ zIIw^D(Q9Jq;W}0{Ze%lac^;WJSsve8cHipE3a77va#XphT*|&g zCOpNnWOe~%u&?7)*m(9$6WI<~X#gTsiiT!*@|5P}bb!)F0{P{i&q-~5Dy^A%xXhKh zLuy<C=}1nMH^uJL_#=LLM5d7oT$5-~Qn`U+Tg!?Qeeorap|(O1bKun)al}UV zllI-4^2aSMzKdUlN@6FzJJ;C-XvM64tS-~cS~;7&;5oLH5=-)}YY(VyIH5j9k*<2~c7Gra-w3+Cr$&&ypXwit_0 zlX&O-U%a^(hdI)XRjy=5qx-&B#EB07>Y=5(hypXHo4|V)G5f7eH|Y97`Qp|js+;Yg z5x%~}S1d~u+L&#ffJ7|OR2NDZl_a-_(`NK~@~!gD#>gOYjY7GLR|eg~w(dZ#)sL#K zsHy&W=0jPkvIC1bYFJ+Jl7V3P%8t#>tGcQ;i3Dj)>8!TP-O~JzoQbJ}o z4djRNHWbNu<;RJnZo)3(ZZhaE_p9vB0}Cq478tMagwY8dTIGgnBkJy{X4PmX&Sm1R zI*_K;MUNe(epzSIBTFhedjCs=6qhfr!p&<`@}s_ihW^&z zFGIAQjr@|_O&4sRE;i%Xb%MCWEN!u-QfZM}?ia|)x3o#MqK+&-MV4*Tzf!IKu5A^jK_t!5h|Ic?0VUq z;h4VB({&NA&o(_n?=;=pYbIP==g5izOx?|*LcBjX5hr*&_>hOGe^x*kRb%riuLu{J z`FzUKn^2gn2=%<~o@)-F{M$2M_}kZeXAihziP=0-Jqqbz{s&35uSmrd3Q-+!Y@)_q z`mDws5VUUv{?7O~K9a>qe648?n?$ji=j`G6{`oKUA~7Y)f*_|QYopiCs-p21FRkOT zQkHT+JO)XyB3SIi;kWMclo-hcgSN#^L9;v!8LY~!x zG+Q{ek3G~zriC~AMy_-sVK)K^PQkK>$#<ZH@%^I6_iRdbMT+vBElGu zPgl+b61iwXBZ$x0SqDlLgG$gWP-L?s|KTHW_r*%j17og<=6VF+)GnA@$nlJ#!Zc&< z7sChf{Gl>Et47_|Z6xG}NOxEQM2Zq_vt(4%HN3bR0_#jYajn(!QQp$}Qj1s?D2^0r z6gy1$_wJXQK7dh2qslgWZNqb0@^+kXEHYHS-?-TEm8%++-95RZ?%GK=$q4K|H40Xs23M4z3xa@ko{Ph z{J)UY^-G#m z0=TLUdAvi|pD+mFNv3mOtqfO=Br03&J}V_4Ai~c=zad_zx==op_bGivs_B&}+5Jhv zYyv$3EiHny~vdlZu%Oo`HQA8D|_rlimdqKcn&^d%z7+m3F zK2Qb`sif^?ml}>0f3VVc-vZo)y16$aaUtMSzI70n|0kYugW!Uk!oy;(oQBr*L}J2s z-_GzV_crZ)Q{EpQ4zP@&&>TA9pY~I)3_cMUu~dV2YJ&?&rc`T^H_%o?`gSXQ(emTs z;sZ~4cy@#2ub(`W`XSt6&N2BhUrr5SH&`}3KLo6oAE$AjRDIsGs0bw0v@m~M-q*XV z5>hX)nQOBa!|}O3V_Tn+=8JdJ+U_S|RZ%^48FY)iA!X8-^CWyKGhEuGXc=cg|1j7$ zlxG5DL`Y?Hq|Y&i24k3rP!kC~*X%X{c=)^`4=a9rE~G3GyY1=faczJ2s_sNOPG$NO z;k^gd0i@*+5$rU0~YIe~Z`uu`aGPlDQ+=NJ{maL%>(T;lkXZAsXcYK)! zV_aj!LV>iAsLI9Xt~7yWeP4osKd~|VB3ZmusG)O3>nnWhe{W&4VlW#qX^d@2)bW!M z9%S@v(xIO_{k{e!xBqICZY@KkB{z1nlB_G6MA!X#JuiVHI(bniXbu}_T7G;$sGB`F z&8INdg46i2JwL&?3CagTx?L?IjrfHt9>Zt5ORrMq86gJ(US)I{+p+0pYXf(VnEi|U zx@L))x>SQqd*n1eR_NURS+Su_w0*);$X)BV(r`*j>DDH;zQt9*zID>Fec!Lt zq2CkSX0}K^GO34YltY~E(8ffT`1h-p>qnr?5dH8K*Pc*7)QAs-$!)(v96PKefJ+}>|>6cTx6J4$lcJWxs zQ+CsT!s~t|EDR?Ps(u^QB?^B)XpKb8N+lIcO(7N3yY*{wIeGG}apQSy%0_nR^4R00 zrB-9oT*X%M`6~WmuF`syE~yLQbdkjJSESTU4G12cT^${rJgmJEbhwOL&Ris!c}zjN zvENEFR2f9ecDN19w>MEOkI>0zM811m(3}wpwH4~^2+FlS3{5shBjfn~gb4XsGM)-? zCNq3)x>CIP`#_-gAfkWl?%CMZv%e(&*I6$u@0Oi~xfY*+n5>Cx1knadFYVRm?;UE; z2w~F{W14-VL`WZ>4TV~f-75N0j>F+YD&huu155Fz<1+EW9;C5ltipKS3Fz~XCRcJ`AuQ}_h*lHDFF!y zcnNv?`H(FPnfr*gbh9LF(li1sAj#Z)XZU)q*6VnVpIRB2gXSwZtkwI~bMr5k?L)eq z1l<&rF)3}GtX;4f8LQN6);(c0?Ftg|&zHd6KQ2j;WmZUssZ;acpz;Cni1RbjS z{2GsRYYl6Y9EYP-U#y0?Yrm*A@291D|JfnTt^9_RO zMyR*)+>km4Mvgl5)%|>!T4J#if9-mFPQJf@G@R_t8KpTXu{;(!7|0PL!Y+~k_G1hJ z-01eUd`Nst|KVsWW2LQR-w>LgmMR~=IwpR*jS0(IxZ+{Cp&TD5TZma{NY+Q#FA{MN zctgpmI-AO@jZNiW^$6C9H_QFfv|J(8cN+@5ChtCdW7HMHCzE@3^n>y>>yA*$kt0QC zTp)Jz%?5@2ACAVnIWC?--yMz5N>HU2*Y^#v>Ps1L$4bgjqjQ2+?`yFRj(^#yi^VW4 z)_aFgCGiND4ik_t;-xe`ml*fV;>=RXoq=Zin)b^DH|uITs@MvnDD?TQ?Q3Tnt2X|> zCY3m6df^i5M#(Mpk;#HMZ;gK<%2)TCy;+&Bxuk7&m zOh%x>`}Wl9fD7mX!P z-FbVjuOGVlAk?Ok%OXTtQyd{atun5VK{{NrMzw$dKJEi8(%CB}n$OEPX|`wun2U zhQV60BV2FGNdK+D&y=a_>?y_!2Pl~>Z;y|$8H>T@;f9d zb%}rT1fOrBeO3X3bnjoTxW2F|=J@>?f%Ps z72H3O9aFt@wSSA3vkAp$dZ#CTHWJ*%L|gZYGZ1BXaC^ZUncUgW1!5WfrPyZP-0DZ zE%jPmx)bKe)H5=d?W29CrM0YxPQKJlc=RSO@;?(yQsZ{;*1(OWHs6>)=r~s;ZtvXm z9!uL(cZSbu$}}ePj_4bh(n2{zR-cvL%g9wRyt90u9l%v{{KZTSJzBP0LX|h55QDKA zJu`JNOi!T!$I9e_LjA1Kv)}e5$t1`2DGM7f%r^MRN9~1s;`I_eDIEs3t?{pQF}VYg z6PF9`u0!^s{=(s^&ObDQ3ZKm{5nf6oRt=DAiXo!-a79^GxM#PcSl|1u^c|sF6_pffOfnT{o)-ef0X}Zw7 zAmIYl2HaJ%p@RnXFKN_T*}F-{2nyzBz2d?tq3bkGdr!t2d$QD6vZa>ern)UBziKP< zSM}$^?sZALb=7~RjLt2da-`vDXZkz&-3|N4`Fl|z7#jDJn6ARxJ|#1hK13e6EdTRN zRG=E9Er;`lJ02RV^YLcJy^`yn%HH`={XjL0sr^jhKKE0N)v9 zgdMR))78x1E`+(p=|A#4)@uoK<=0&ILp+$Fjzcv&@Ag^8 zW`^0JOv;b|)+5dTh=4ZPePnLfveM%o&v{wO!4S5uV#`EY6^~etE_0^EVjh_5;9M8$ zWRREIMCS0AzR$H+YB_OreM=ov(R8vrpYW}3{VVU)VO@ul`8!OAzy)Zw>NS6a!|Ohp zr2^T+$1V1f=!@Xb%TxK#`>sX{|)Fz9;_Aa97fqG*~4Z!iTW-N{KjUK72wIGHsTkU zCw6Q=*!1$&0{OE^mor^*=*h!e*3Z={{g?Zzr#xev3uQAwZZ)N9{Jxv0eqMcg5jg{R zG-q)=QSfss)3i)ER-bw`<>;$t?4Ub;u~W#=2pS~*hL=iP zx_zy5v!AO~`Bsy%;k+Jc&tds0T32mTQ>b#-v$~SI+e#I$PtOkeI@fx8XrQQW<}+K! zRYUE;1+sZ8A9~o}udOo@x~rspkDQW?uM7LNhLyf4=7Y@MVjyAnU87(gmepo-1HVx_ z!OV=50PdW6)Dt{TvMl4#&vmc6zhx#=%pRxNz`hMclA%v1q`bD>N>L}hpD<0#v^eD1 z=G^MNMW{@}c!DdPJEz)~y80Ne>H)s?AGI9~B<$OLEPOS zR_$q18?$a@qCXOp_~C}cEL-XjT^QtlJUcIsLAM2wpqC35Ta>0i&^r&y8iUqWswA5? z!!_oU3rnrjccw1|54$cru2L>el7^6@`eg!8q*}h20%Ox!ZH8UH%`H$E2_hsI5=8wd z8`t@qNOQK|x|IBbezJHu z3Cqg7QEj$(63^SJ{kaW>pH429pVt@5wUm^gqo(V-itRWXxN*$TDACfRKn826AM$s0 z;>Pzx9TYTK1bwHDd0>*MHDevPLBC`&ThznoL^O7xcR-*H3Epz7yo>0e{#$EttGe^& zKea8giG{x~?mJ+PcUn1{6WYCVa~q+Sv1$=1@5n!Ce(?I^^34&v=eWJX9n$YfGp=bi zBaL5dBcag$=o;40dKTaYAvLv7V+@K%EatN~t=}jxu@&jba;7Mt3Rm~c6S`9flI1-- ztov{?R&}O*?dbH6-X4o^VsYi@H+=kl==GVb`~{LbQqHC#^s0~`U37RS=T0fjDJ;{lBQDCrYU%fa`HZ`VZoyC_>0Li zZ$>HloS^!UjUKU{L!E=3S9f|t>djo=@VCg-v`fjRD7}xzS%BhX!E$L|Q0%)#M3}8J ze$(6(joq_)uOP~g9AB~BQ;OYd2Yj`5{*5GJ8t4LKvFYI1M1;`zqgX7#IUtlt+qP{HN>@u~}LQxKnzHDn@N{F=iO+1)Ew%6lt8@%kEavMMRZ~ky2AH zy>D%&V|@0UP8m}jgOgu@MWpds8!m=9bDz}3Ie2gOyu4x9Hi(C{L!FqD;`mhzQ;kB0 zZqMlZ6y)m$(TVM%j@FQ0c~V5*OJ}are?^g((v{gL)nCoJwmw4t_=;3Zzfk<9cvft} znDWzv>i6EWH;Mk$_!W-Oqr#Ujh%Ec!_sud5td*mi^3cb+#9xY2HwTiPw!Qkl?I_1h zGUkS8w5RIIllEHX4euEj#%=~NdK^t>wd0gN475a6N|L1L!HJFtl8iM%(w|M5w(CLi z9{L3>3nJ;L_#TvAmXpKW^ zN2(isoItn-#5BNDeH)t^%kr>f`l9KNM{JZ{VGY9Yc{9z-ulD0u6y+Il7^tZmiPY zzMFc#+_)Cx8+={Gun=maROXbHA24^ZKZX0vF5gVbW^!N(L zjt^q)I6Xqw9QFO3%u3~3Q{_`tki>yl!ZOgycEA0HTA$jN2;#U~u=u&ySorvWGS57y zl!OC5UO6~FC%uRWj;WoAy^$3TJ8-mN0qwj3z)uPeu=t?MT>tBGz9%b);o}Di(s1yN zPY#Wr8&LDR0CE{PDBzbzZuQ`CDAPLYH z!vo2JmLNh74)BEJ0m~~qU@T+_H09x7UPu<)yn+LEVN37=0SBeRvS3ggjtad%QD+9s zB0}I70tVbfWI_7t|E%gWgBuZHGU z1%)N>3W6M<yfk3o3c?hMFrX}J4P-T8U_e$L94X>K zUfxBnASVn+v|u1rP9C%>{bv=5oLpWQ>_V$%^70@``EJ$Gh8Y|{tIgUlz>Sax+Hdec zG{PFh=)eGmf*g1W#iXNP4GiAFz!ZdkL6||&8jX_=q$;Z2W#(AX4ULl@m?=Ss6SOJ0 zfjK=G5L1@BOHQ=18>rBS0mnj4ApV9Jje{HLym13a0~nOR1UnA!P=y5Yfc5mN7cJD52(6<-$pPX|5hG2XyJi^x7J|D7zRky z$T5L|JvBKrPIkbp4*ix7d{kG1b8`GwY4U?tP&R}$gweS8fsck7XnF^Oj_fWOc$&h1 z*c=AbHRV9G?%is>Co@=pR`;P*S}i%CuLlRdTGpV=0tPO$5OALVG7KH@UG{ynML@hI z91Lh9pa=+cJkYqgK%9;ym@|NZa~&JNV+{w6vz#FR-4jq@2m}4^Yyh?m)Cjurz|RN{ zGIVXgB7_O_sHbC0tu3rreIYF%{F=(}dgIQ>m z)D;d?%;Z5F6l}Sf4LE`@t+_lt_y39j>QrctkLJRl(+v)Oo6Dnda{@^V4>Vpj&|#r* zmpKwk4`Aj22WyF(V8W6F+_!^+UzRq2-xJEB6#^{U!N8!E4Z!n)0|{#csI-THY-<~^ z0%2ks1XMRBHjq7Tuxz6V9Vo9Y1b9HTtp<4E1P4TRHbB!C%01MRJ$5AE8d}?f))@WZ zz}Q|Mj5xzVtGx}lg)onUJjjEB<^CV)a}Y-3V*`I3H16`F?&yKW#}39FHG#An)J;w{ zz&+sZ)&x6Fu;D}kj@)73zOxOG3xtC}X9Vc zVQ?=72Kf9CU_1aH06%N60AXAt0*#LYXd$7(j)mRPahEqIcWYm{2@#H;Jv>l z#Ge2B-GO>M90Ug_0E!U2yIb!J!vci>LjoLV1S)_ZA#hL=Xa#N|%n+miZS4`{j?cmN z-;4s4=SNP!9{dFIR6f`pya)JTB3K@cpAArkxC5^wd{7i34}>D%;BSaEP)x=L?xFJF zK_qlrs1-T~8!r$EyYnYC%pF{&-~)v31j3yWjsFAWOp2u6?12U8!W}#Im8p|0aCK@L0c>WDmI)r4`7vp4-(=KKr0ar z4&rQpXf7O>#{VC8?)r`y9L9^@Wt2JL&YRE#Pq32*2e3r>JF(JE^aL&WcWoyRN}+rR z0vixl00;Fz9w?{6p{?(@PAEzAj`)3(?tGbn9*%j7;6OJS0j|KMGol-amNS6mc zvLWGzFzW}XaWbF}`TzGp2gtC1#SGCq0iej#0BKP4ftj}8dl_`JnRhvo%kl)}IOwKKYOde$bY$fyT)RhzdM`do>JX6(|7y_b_l- zU<>4HV8EeJ0l=V`hYD=~RV@ri6e;{Kb>ZOqPsWFfMA5i70jyXPvTj!F3Hs}xPAygd zx@AzOzPAOL^)S%+9s$^(7)eWPfqerEB$XiGT%0fZlvA%H&=rDd5N=xBn0&9eXF+5gC-O}QudsnTY%pN2i%|J!FV+smF+*Z-TDbK!43X? zf|#Ejs8@P|#&$SZs)S^L129y1f~XESsIHO+9<}%Yw;Cd#PB;juM!>lLD>hC@7FhsZ z4P*+MK5On6?R||WxYrE_%(V#c70O3wtu0u9FlybMJ}B0Cg32B^n6887iqCgqc30Z( z>P69bI6-zj#P96jvfdMD^g-I+AP-2I;b5l07BGH+gEx)x;1I%Pjke$)glRwjUuW2b zSwO{S$QCa+`m6~o+h9PY$rh9hKqTCR0K)AsK-X*wyuZRhe)F9>$IYHVX%G%vTmH8y zK(%wnA%9v#(RkT`aH|IR3`JkiY72f1!2v~^JTQPla%-~%L&I?JvrYbv_eI)0LGB0~ z^tH=_`@L}B0O<;%1G2ykVmdUyBD8ecVGE?k;J~)?u1%XeJptMHogm&#FR@*o-~>X1 z-4LPl!=U!LE9k6l$N)cB>ed9z127=oV+&Fz;h?QYJ`sw7sMi))Pr*S#ul$`PF86u@ z(Qgn3^+7~32#2<~%kE*H=pETJf6)LwkoSpSY{AYnL_YlxyFlKX_1l7$85mgYhm?XF zunc&DxbHAfH=qEnM()PPH?k1a}H+UxkWrLmocOC}V zhVJCaZwMkE2w@B(fXoCO2m<_oFxDu8m~zq+AlG1^Xi@>}eaE|Fgu7faO+gm;f$fwgXq&|Y6H~T;ZUYVw-=NWH z4q~Nmw%{7V)YJ0ld~EE%ZW@}_HsRoWS|0E&;6r_Lmrb7;5p+&AXaJv42e^y);MsQ@ z;I@qqvcJoL6$oEKSZ)U&nmXkmE8L)c)&o%P;seP!1c+F^Q}(-j4$s|L;RUdHO`r=! z;WTdpzU<+HqxrkC6j<;88T<;ij@}fMLU4ugw?rM{K39bZ zs91-C?j;)ta{%^b4d4$&k+OV;IRWO11{(C2-iikVx&B{UR~Kv7ah0{N?VIE#wr_mT zy@^SDeH*IH8!BG^VN)Vr-C#DdNNuuZWlzmm( z<9~ns=y=Z+0iO8u=^sibZv6Eh9vxr5vSqyOvb^NSXDLT!A3e74z$ZU3-1E%Ir%u%Pr;MpPn;b?s_^3wEefPU78zY+1uk5bw z82`b;_ds^;)Si2_*6a1r4FY)Z6@$kYPp@o^rr+}DC+|ng%U|6i-Z|SWZ8WH%(mT7W z9e?!TJ!ADJD~qvLRM`yc4HjGF%Mb6_Zr2-I0(E-URCl2Z%;e=G%h25HP2Y;p{L@c} z<`@4l4|wW#mllob@(Jkw@W(N|-xF)?-gM?rwL|-&Y!E6 z2Q_tQ>+^NBeE!vH)2OB%L7)tkq8`CNK};H30hi(t;8LZ?r&hq%Mc>V`ao664^OvfX zJ4I>DLp0}aRLdiWMW7gQ4qjFaIglvW3djm4Rxk#gSivB4V#Nf7X@0I+On+WgOPj>y zHr+p0t)xr%{pYa=6l2VbzLs3zMPCM;7kyoNK|1}!T(xc9i%KrNDuUB<)zXtPDx=AZ z&lpZ#bn@iNAUZL=g6QN!I<*1Ig&`G8T13ly5S2puvkg$`;D&1Hn9L>vCy1tj)>RNq zBh5$uA0y%6v8|9oSf{jL6(53NoI+2|>(#ih_)=2n97G ztbr>*p>z zRfC-%N~FD=Ryq(;4jz}gN0f&CfEfDLYiO~YVlzgb}jCM zs?#-Svt419HHEvNOj@1CVta1B+VoiIoghI+Q^X4rbu_^-DAA1I7*vza&0}p#h|wY6PLNuW>NDR@0&Y)#p37tWUiW78ho<)UV>U8|3YI#)x ziY9mlWSPiBRC))`JWeq$NKDZb^nwwM3A{m~j3)2~6H;|^wX#X(F{VGn|4sU4cnOpV z1hhINSm;PAP=bY~SQxCRLcuUdu+W6wV8wY8^me^uV|opGS*U0Tu0f&)>c4UsOY|_c zOHbTVEq|;gJ0x5ff@_etfFh1M^Xb$r2xCwX)fu(M^gKpMiwg+5j82IQhEmWValtTH zy(q?D^`aPq)x(pf3)b}43)QXZ;|mC8SaZgtA1uiJNjgisFpSF}N*S8oAWBhU8YFBO z#%T~;yEskP7OI=>6jd3fA*8LhRx3}6wv?L&i6MrP(-1{_hOmSb!w`;Qlxx!Mw@DDB z$&EI>bStJ0hlQI46hcGv=|7?1F`0XYXcWBz_bGZaVkNW~Ml8bIVd$2wi3SW&M3@np zzkoyLp`m~p8gVz}szD-;VE{|yF$`ddJO+B4-CPZp4M8^6bsC~+1Rh2nEb$a`Aw(fI z;v`~d3A0fm68=JJ0VJ?9A}-fzl!#4(=Bt+ZN|vDu9?mCqJ5-d_;dlTn4#xw) z97pEJ(wHzw9e>HnaAc0G3*Q~BRZ-T3r{RoEm!aa_k_mY-KoTJz)7O@&!PBAJWj5Nt zfWSS%fItRV7!as`$(#ZiV0AjTts2Zr0)-mq;6OH0?hzVC?hzVC?hzBvkp33>H>AJh zPE9%52IcB>5N_BZ$x=fDwWqgG_QL2 zS209$U2b%Gb_HHxjOId|L7EG3Mycdm%F-mFE`Jmzt>(Jil8Yt5Qr4$kcfbluKFwu1 zruGpu?_)6Lx}4F7y8K=7a<0o6fLOCTVex9N%NcSJJEDSPuia4%en~Ota>4FN)m*SM z2qmjhjKbKY?}!ePMkxkfgH^0TOy}+t9U^%(lgN>lgQQi8p(tsUVk|a3{i?WK@+igd zYfwX>d9{&+ONi4_z$9xYS(d!k!Qajw01?!A_k^dBPlS zbf7mx!|G>X2Vo8*hBVvs0Gjs^=0Ijhb2*b*>2wK$9s;X@b^;wb6mhwfL3W6^1h&ql zKI+lepu^*YK@5+#4EaN%rY1=0%m5H1anO{$wjEO?krIEezt|;orU|fXpSnvxMv7-% z02q88&HD(0;8oBmg+~pO4vN<$S<-|-4L;x`np-}AFbIqBmao%+yWthWAS{<=(lqk{ zgh-wF074|VnzRU$RwWK~&cj z1VIh2zb-1ad;v|IL_po>^wJJdkufNQfo^EYNetACNC|?55$Q~N7PGJ_LC`P+GmlXF zba)k$%?NZ@9R>&-p?M!8(80+#8!lHQW`5TW^p^<*MxfW}5j3y13<5b-XlJ8s8t%m6 zVZ^me0>N5>AWmON75EsZq9kt8HBq5u7s$mD1dWO5B2?JUh-+B}o!&w7K1N(iD{!Yi zof9uh)H94Y#Dj06d9`H{$fqrnfI4jmuQ1{=bixdiT1G>`F* z?-30t=m9lorleC#3^as4fJwyV7-06&YePgWBQekt;z03~6ffdW+mO`Rw$px~jB^PLnTuTyOL0+QoNi{j+Yn5y= zmt~T2{YV9*(LQb6Ee91cB?MU0(hKCRmR_Ihrr>(95Nk&z?SU0tfVYA$IOR zfaJfvyZXvp&==0!TP<$I^|kb@tp@3NQ~hA6-4nWxTWS5!e9JJVW3k$K;rq6_Wlrw4 zZLU6a;kvK>s`}Y>)4*k#&z}6;Ghg}wZqs1sOvQBj@)y1ztBXr>U}D#XTuFcX>FV#6 z+VI|eHs*2h=!F*#RaY@wlZX4xqtz?b{q3$(440{#Sr@;3`oym-)I*DE;~Ph+dP9Q( z@XC=YpzHw0IK=LX|9xh-kpEAN;M|h?Om#1gBV^m@wFd8m!0*XCSjF-=k39&qRR1|$ z3uD@oDVz81LY5Dl>Jg+&<7#203t8TF3t2uYDrC6qLho8R$}OG)?@N|AFYI#vL0pp|OI#E>xp*#D5QVH3 z{u|iu zGAsK|@h-l#B$=1{FvW9jB6StD6%@p6<9-;?3afzaoLtd_&|LS`f*m4AA=`auH-jU@j%GAHL& zeOWsD#cFAUVqc<1zp3`Z3Kgd(t*P5-%( zc5?L87ixqxk_AZ+`_G}SB=@7RB^i#3$umegWlF^VGxr-ydkgMv$VqK6u$NF?$ed91 zg)H__idOIk-7)g~ zt1S-I%I7efQZ}Z2U?}2#5<%8uYcB`JRUDdu|eBrar*Lq)Va z1`>o3xhvw%Ee+C84ufQX2Wi0Ym+v+;Z)VLj8yL-2f5V9q?f?n9*SD^*>kJa)#zFC% zmr|xD1GP|W%kV$g2^s_(1EqX;nFhfQOeq40h+>c$5-+!73T2#XMkI_BdcjTbAgb=B z|G8&y6g&tbYB?p?L#Jbm)=DX`4OH+*j3~n~Xa-8Cz(}k!NJHTmpQe;!jPReqMaEEi z0hck$c}LDhkq!R|T^TQb$|Ziq2}ohz4ajp?DS67TXR;X!$o&OlN-GkB7P0&SE4fJLUzXFTUxpe88GFdm_b z2KJQCnUQZOpT+{BtPvO4gvIT<6&HeMmbB?K*k~bOi{}vMNal0JCjOtfKUm@eKv?to z49Eb=4smmg)<5(D@2C?z%xvP&8xU%l(ICrG<x2U_U9NpRcQ!@AYucV zxZF^izMK6IW{&U_wgm>BOdxnVrk;W6=ar!`b z$8|=+yVan>I`b(L3u2D&ZgB;?Ly)QBJ0ZMVEcDLK)mmaL z;TD>C zGh6-mz=al9U={&M6XPAh!*$h$&%n1?>LH~DFFdND1mWg z2rgciQ!=t(yox!gExz0)OCk<%O1DPGc*pKdhQzRj_nD^~6lTNBf^{|@aDik=lkc*~ zba!4>jCTY@GNeJmRw~`#Tdk>VlK&yOWxO{H;~mUcxwsq-R>~=H(6%&0$f$Bk+z91v zIVTSEN@_|`vjX%d>woH2hkB4KK`BC(sC-GF zHznAQ2=6EbO1z^e>s7lg{&09kc*o0xckBv;ca-vkcW{uD&Lg-}cz1e~KbY@Z0^*gX zM0>)!L%B;+0&-@yxF*wta(9aN3EXkK(ZU_}&)kozPqesj!-uBCI3 z-myGrN)YmCN+L-3Qke!McPYcM?%I@0^FJ^ujCUOCk|ii=$P#e>g!jf%IuBB48 zKqNBWn?Ug$v<<~~9H|iAJt*IDN^tjq!aKG+z&-DOvn?1e6W%?@uySz;63Zz;+DUjv z{OjW#>j-CDGq|tie_qvucU1m_ca$K6caQS1oD#sD(s>+(^b delta 35333 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 From e4ca5255413327768da69716f7a4c4ef61abc898 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Fri, 22 Mar 2024 18:20:46 +0100 Subject: [PATCH 25/68] Update few Match exceptions and some Player Javadoc --- .../java/it/polimi/ingsw/gamemodel/Match.java | 22 ++++++--- .../it/polimi/ingsw/gamemodel/Player.java | 48 +++++++++++-------- 2 files changed, 43 insertions(+), 27 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index e79a76fd..fad92354 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -17,6 +17,8 @@ * an event occurred, such as nextPlayer(...). */ public class Match { + // TODO: Fix exception throwing/handling + private final List players; private final int maxPlayers; private Player currentPlayer; @@ -307,12 +309,12 @@ protected void setupBoards() { * @param coords coordinates in which to place the card * @param card card to place * @param side side of the card to be placed - * @throws WrongStateException if called during a state that does not allow making moves + * @throws WrongStateException if called while in a state that doesn't allow making moves * @throws WrongCardPlacementException if the placement is not valid */ - protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException { + protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException { Board currentPlayerBoard = currentPlayer.getBoard(); - // TODO: Improve error handling and identation + // If placing the card in the current player's board is allowed by rules PlacementOutcome outcome = currentPlayerBoard.verifyCardPlacement(coords, card, side); switch (outcome) { @@ -342,13 +344,11 @@ protected void makeMove(Pair coords, PlayableCard card, Side s // the match is now finished if (currentPlayer.equals(players.getLast()) && lastTurn) finished = true; - break; + break; case INVALID_COORDS: throw new WrongChoiceException("Invalid coordinates!"); - break; case INVALID_ENOUGH_RESOURCES: throw new WrongChoiceException("Not enough resources!"); - break; } } @@ -362,7 +362,9 @@ protected void makeMove(Pair coords, PlayableCard card, Side s * @return the card drawn */ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, WrongChoiceException { + currentState.drawCard(); + PlayableCard card; switch (source) { case GOLDS_DECK: @@ -428,4 +430,12 @@ protected void setInitialSide(Side side) throws WrongStateException { public Pair getVisibleObjectives() { return visibleObjectives; } + + /** + * Getter for the current match state. + * @return the current state of the match + */ + public MatchState getCurrentState() { + return currentState; + } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Player.java b/src/main/java/it/polimi/ingsw/gamemodel/Player.java index 0a9dab77..6dcb8f52 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Player.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Player.java @@ -3,7 +3,9 @@ import it.polimi.ingsw.exceptions.*; /** -* Player represents each in-game user. The class also manages the board's logic + * Represents each in-game user, so acts also as a gateway receiving input + * by the Controller. + * It's also responsible for the board's logic, which is a slice of the game logic. */ public class Player { private String nickname; @@ -25,16 +27,15 @@ public Player(String nickname, Match match) { /** * Places on the board the desired card (on the specified side) in the given position, if it is a valid one - * @param coord x and y position in which the card is played (where 0, 0 is the initial card) + * @param coords x and y position in which the card is played (where 0, 0 is the initial card) * @param card the card to be placed * @param side whether the card should be placed on the front or on the back */ - public void playCard(Pair coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException { - if (match.getCurrentPlayer().equals(this)) { + public void playCard(Pair coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException, WrongChoiceException { + if (match.getCurrentPlayer().equals(this)) match.makeMove(coords, card, side); - } else { + else throw new WrongTurnException("Only the current player can play cards"); - } } public InitialCard drawInitialCard() throws WrongTurnException, Exception, WrongStateException { @@ -42,22 +43,25 @@ public InitialCard drawInitialCard() throws WrongTurnException, Exception, Wrong InitialCard card = match.drawInitialCard(); return card; } else { - throw new WrongTurnException("Only the current player can draw the initial card"); + throw new WrongTurnException("Only the current player can draw the initial card"); } } public void chooseInitialCardSide(Side side) throws WrongTurnException, WrongStateException { - if (match.getCurrentPlayer().equals(this)) { - match.setInitialSide(side); - } else { - throw new WrongTurnException("Only the current player can choose the initial card side"); - } + if (match.getCurrentPlayer().equals(this)) + match.setInitialSide(side); + else + throw new WrongTurnException("Only the current player can choose the initial card side"); } + /** * Adds a card to the player's hand, popping it from the required source * @param source represents the source of the draw, which can be either one of the two decks or one of the four cards on the table + * @throws WrongTurnException if called by the player when it's not its turn + * @throws WrongChoiceException if called on a drawing source which is empty (e.g. empty deck) + * @throws WrongStateException if called during the wrong match state */ - public void drawcard(DrawSource source) throws WrongTurnException, WrongChoiceException { + public void drawCard(DrawSource source) throws WrongTurnException, WrongStateException, WrongChoiceException { if (match.getCurrentPlayer().equals(this)) { PlayableCard card = match.drawCard(source); board.addHandCard(card); @@ -67,8 +71,9 @@ public void drawcard(DrawSource source) throws WrongTurnException, WrongChoiceEx } /** - * Sets the player private objective (only at the start of the game) + * Sets the player private objective (only at the start of the game). * @param objective the chosen objective between the two proposed + * @throws WrongTurnException if called by the player when it's not its turn */ public void chooseSecretObjective(Objective objective) throws WrongTurnException { if (match.getCurrentPlayer().equals(this)) { @@ -80,34 +85,35 @@ public void chooseSecretObjective(Objective objective) throws WrongTurnException } /** - * Getter for the player's board + * Getter for the player's board. */ public Board getBoard() { return board; } /** - * Getter for the player's points + * Getter for the player's points. */ public int getPoints() { return points; } + /** - * Getter for the player's pawn color + * Getter for the player's pawn color. */ public Color getPawnColor() { return pawnColor; } /** - * Setter for the player's color + * Setter for the player's color. */ protected void setColor(Color color) { this.pawnColor = color; } /** - * Getter for the player's objective + * Getter for the player's secret objective. * @see #chooseSecretObjective(Objective) */ protected Objective getSecretObjective() { @@ -115,7 +121,7 @@ protected Objective getSecretObjective() { } /** - * Adds points to the player + * Adds points to the player. * @param points number of points to add to the player */ protected void addPoints(int points) { @@ -123,7 +129,7 @@ protected void addPoints(int points) { } /** - * Getter for the player's nickname + * Getter for the player's nickname. */ public String getNickname() { return nickname; From 2470ff0c1773aa708d43a1588d0f26b85bcbcb76 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Sat, 23 Mar 2024 00:02:33 +0100 Subject: [PATCH 26/68] Create an initial test for match --- .../it/polimi/ingsw/gamemodel/MatchTest.java | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) create mode 100644 src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java new file mode 100644 index 00000000..ef423fdd --- /dev/null +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -0,0 +1,122 @@ +package it.polimi.ingsw.gamemodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import java.util.EnumSet; +import java.util.Map; +import java.util.Set; + +import org.junit.Test; + +import it.polimi.ingsw.exceptions.WrongStateException; + +public class MatchTest { + + @Test + public void shouldAnswerWithTrue() { + GameDeck initialsDeck = createInitialsDeck(); + GameDeck resourcesDeck = createResourcesDeck(); + GameDeck goldsDeck = createGoldsDeck(); + GameDeck objectivesDeck = createObjectivesDeck(); + + Match match = new Match(2, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + Player p1 = new Player("Oingo", match); + Player p2 = new Player("Boingo", match); + + try { + match.addPlayer(p1); + match.addPlayer(p2); + } catch (WrongStateException e) { + e.printStackTrace(); + } + assertTrue( true ); + } + + private GameDeck createObjectivesDeck() { + GameDeck objectivesDeck = new GameDeck(4); + for (int i = 0; i < 4; i++) { + objectivesDeck.add(generateRandomObjective()); + } + return objectivesDeck; + } + + private GameDeck createResourcesDeck() { + GameDeck resourcesDeck = new GameDeck(40); + for (int i = 0; i < 40; i++) { + resourcesDeck.add(generateRandomResourceCard()); + } + return resourcesDeck; + } + + private GameDeck createGoldsDeck() { + GameDeck goldsDeck = new GameDeck(40); + for (int i = 0; i < 40; i++) { + goldsDeck.add(generateRandomGoldCard()); + } + return goldsDeck; + } + + private GameDeck createInitialsDeck() { + GameDeck initialsDeck = new GameDeck(4); + for (int i = 0; i < 4; i++) { + initialsDeck.add(generateRandomInitialCard()); + } + return initialsDeck; + } + + private ResourceCard generateRandomResourceCard() { + EnumSet reigns = Symbol.getReigns(); + EnumSet corners = Symbol.getValidCorner(); + return new ResourceCard( + new CardFace(randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), Set.of(randomSymbol(reigns))), + new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Set.of(randomSymbol(reigns))), + (int) Math.random() * 2 + ); + } + + private GoldCard generateRandomGoldCard() { + EnumSet reigns = Symbol.getReigns(); + EnumSet corners = Symbol.getValidCorner(); + EnumSet multipliers = Symbol.getValidMultiplier(); + EnumSet resources = Symbol.getBasicResources(); + return new GoldCard( + new CardFace(randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), Set.of(randomSymbol(reigns))), + new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Set.of(randomSymbol(reigns))), + randomSymbol(multipliers), + (int) Math.random() * 2, + new QuantityRequirement(Map.of(randomSymbol(resources), 1)) + ); + } + + private InitialCard generateRandomInitialCard() { + EnumSet reigns = Symbol.getReigns(); + // Generate a random number between 0 and 3 + int index = (int) (Math.random() * 3); + + return new InitialCard( + new CardFace(randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), Set.of(randomSymbol(reigns))), + new CardFace(randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), Set.of(randomSymbol(reigns))) + ); + } + + private Objective generateRandomObjective() { + EnumSet resources = Symbol.getBasicResources(); + return new Objective((int) (Math.random() * 2), new QuantityRequirement(Map.of(randomSymbol(resources), 3))); + } + + private Symbol randomSymbol(EnumSet validSymbols) { + int size = validSymbols.size(); + // Generate a random number between 0 and 3 + int index = (int) (Math.random() * size); + int i = 0; + for (Symbol element : validSymbols) { + if (i == index) { + return element; + } + i++; + } + return null; + } +} + From 6abcdcd86dbc7347c24832122cafda4062e35f87 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Sat, 23 Mar 2024 13:15:42 +0100 Subject: [PATCH 27/68] Change Match so that now use List of PlayableCard for visible cards --- .../it/polimi/ingsw/gamemodel/DrawSource.java | 10 +- .../java/it/polimi/ingsw/gamemodel/Match.java | 107 +++++++++++------- 2 files changed, 69 insertions(+), 48 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/DrawSource.java b/src/main/java/it/polimi/ingsw/gamemodel/DrawSource.java index 37859df2..7982f469 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/DrawSource.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/DrawSource.java @@ -1,13 +1,13 @@ package it.polimi.ingsw.gamemodel; /** -* All the sources a player can draw from: the decks and the four visible cards +* All the sources a player can draw from: the decks and the four visible cards. */ public enum DrawSource { GOLDS_DECK, RESOURCES_DECK, - FIRST_VISIBLE_GOLDS, - SECOND_VISIBLE_GOLDS, - FIRST_VISIBLE_RESOURCES, - SECOND_VISIBLE_RESOURCES + FIRST_VISIBLE, + SECOND_VISIBLE, + THIRD_VISIBLE, + FOURTH_VISIBLE } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index fad92354..2ab6d710 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -1,10 +1,9 @@ package it.polimi.ingsw.gamemodel; import it.polimi.ingsw.utils.Pair; -import java.util.ArrayList; -import it.polimi.ingsw.utils.MutablePair; -import java.util.Collections; -import java.util.List; + +import java.util.*; + import it.polimi.ingsw.exceptions.*; /** @@ -32,8 +31,7 @@ public class Match { private final GameDeck objectivesDeck; // All the visible cards on the common table - private MutablePair visibleResources; - private MutablePair visibleGolds; + private final Map visiblePlayableCards; private Pair visibleObjectives; private Pair currentProposedObjectives; @@ -64,6 +62,7 @@ public Match(int maxPlayers, GameDeck initialsDeck, GameDeck(); + this.visiblePlayableCards = new HashMap<>(); } /** @@ -111,6 +110,7 @@ protected void nextPlayer() { if (currentPlayer == null || currentPlayer.equals(players.getLast())) { // Set currentPlayer as the first one currentPlayer = players.getFirst(); + turn++; } else { // Get the index of the current player and choose the next one @@ -198,9 +198,12 @@ protected void setState(MatchState state) { protected InitialCard drawInitialCard() throws Exception, WrongStateException { currentState.drawInitialCard(); + currentGivenInitialCard = initialsDeck.pop(); + return currentGivenInitialCard; } + /** * Extracts two cards from the deck of objectives and returns them. * Note: Called by the Controller. @@ -227,15 +230,13 @@ protected void setSecretObjective(Objective objective) throws WrongChoiceExcepti Objective secondProposedObjective = currentProposedObjectives.second(); // Check if the chosen objective is one of the proposed ones and put it back in the deck - if (objective.equals(firstProposedObjective)) { + if (objective.equals(firstProposedObjective)) objectivesDeck.add(secondProposedObjective); - } else if (objective.equals(secondProposedObjective)) { + else if (objective.equals(secondProposedObjective)) objectivesDeck.add(firstProposedObjective); - } else { - // If the objective is not one of the proposed ones - // throw an exception - throw new WrongChoiceException("The chosen objective is not one of the proposed ones"); - } + else + // If the objective is not one of the proposed ones, throw an exception + throw new WrongChoiceException("The chosen objective is not one of the proposed ones"); } /** @@ -275,9 +276,12 @@ protected void setupDecks() { Objective objective1 = objectivesDeck.pop(); Objective objective2 = objectivesDeck.pop(); - // Set popped cards in Match attributes - visibleGolds = new MutablePair(goldCard1, goldCard2); - visibleResources = new MutablePair(resourceCard1, resourceCard2); + // Put golds and resources in visiblePlayableCards + visiblePlayableCards.put(DrawSource.FIRST_VISIBLE, resourceCard1); + visiblePlayableCards.put(DrawSource.SECOND_VISIBLE, resourceCard2); + visiblePlayableCards.put(DrawSource.THIRD_VISIBLE, goldCard1); + visiblePlayableCards.put(DrawSource.FOURTH_VISIBLE, goldCard2); + visibleObjectives = new Pair<>(objective1, objective2); } @@ -310,7 +314,6 @@ protected void setupBoards() { * @param card card to place * @param side side of the card to be placed * @throws WrongStateException if called while in a state that doesn't allow making moves - * @throws WrongCardPlacementException if the placement is not valid */ protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException { Board currentPlayerBoard = currentPlayer.getBoard(); @@ -362,51 +365,69 @@ protected void makeMove(Pair coords, PlayableCard card, Side s * @return the card drawn */ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, WrongChoiceException { - currentState.drawCard(); PlayableCard card; switch (source) { - case GOLDS_DECK: + case GOLDS_DECK -> { if (goldsDeck.isEmpty()) throw new WrongChoiceException("Golds deck is empty!"); + card = goldsDeck.pop(); - break; - case RESOURCES_DECK: + } + case RESOURCES_DECK -> { if (resourcesDeck.isEmpty()) throw new WrongChoiceException("Resources deck is empty!"); + card = resourcesDeck.pop(); - break; - case FIRST_VISIBLE_GOLDS: - card = visibleGolds.getFirst(); + } + case FIRST_VISIBLE, SECOND_VISIBLE -> { + // Get the chosen card + card = visiblePlayableCards.get(source); + + // If not present (e.g. on the last turn both decks are empty, so remaining turns will be played + // drawing the four visible cards, but they won't be substituted by others) throw an exception if (card == null) throw new WrongChoiceException("There is no visible gold in position one!"); - visibleGolds.setFirst(goldsDeck.poll()); - break; - case SECOND_VISIBLE_GOLDS: - card = visibleGolds.getSecond(); - if (card == null) - throw new WrongChoiceException("There is no visible gold in position two!"); - visibleGolds.setSecond(goldsDeck.poll()); - break; - case FIRST_VISIBLE_RESOURCES: - card = visibleResources.getFirst(); - if (card == null) - throw new WrongChoiceException("There is no visible resource in position one!"); - visibleResources.setFirst(resourcesDeck.poll()); - break; - case SECOND_VISIBLE_RESOURCES: - card = visibleResources.getSecond(); + + // If the golds deck is NOT empty, substitute the first/second visible + // card with a new gold + if(!goldsDeck.isEmpty()) + visiblePlayableCards.put(source, goldsDeck.poll()); + // If the golds deck is empty, substitute the first/second visible + // card with a resource + else + visiblePlayableCards.put(source, resourcesDeck.poll()); + // If the resources deck is empty too, the GameDeck.poll() method returns null, + // then the corresponding visible card will be null + } + case THIRD_VISIBLE, FOURTH_VISIBLE -> { + // Get the chosen card + card = visiblePlayableCards.get(source); + + // If not present (e.g. on the last turn both decks are empty, so remaining turns will be played + // drawing the four visible cards, but they won't be substituted by others) throw an exception if (card == null) - throw new WrongChoiceException("There is no visible resource in position two!"); - visibleResources.setSecond(resourcesDeck.poll()); - break; + throw new WrongChoiceException("There is no visible gold in position one!"); + + // If the resources deck is NOT empty, substitute the third/fourth visible + // card with a new resource + if(!goldsDeck.isEmpty()) + visiblePlayableCards.put(source, resourcesDeck.poll()); + // If the resources deck is empty, substitute the third/fourth visible + // card with a gold + else + visiblePlayableCards.put(source, goldsDeck.poll()); + // If the golds deck is empty too, the GameDeck.poll() method returns null, + // then the corresponding visible card will be null + } default: throw new WrongChoiceException("Unexpected value: " + source); } if (goldsDeck.isEmpty() && resourcesDeck.isEmpty()) lastTurn = true; + return card; } From 54eb67a9a91e40be045623a17e6aa4d69b6f0333 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Sat, 23 Mar 2024 13:40:15 +0100 Subject: [PATCH 28/68] Remove SetupState from UML diagram --- deliveries/UML/class_diagram.mmd | 16 ++++++---------- deliveries/UML/model.pdf | Bin 98249 -> 110401 bytes 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index 49726b3c..a1678654 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -7,7 +7,6 @@ classDiagram Match --> DrawSource Match -- Player MatchState <|-- WaitState - MatchState <|-- SetupState MatchState <|-- NextTurnState MatchState <|-- ChooseInitialSideState MatchState <|-- ChooseSecretObjectiveState @@ -217,6 +216,7 @@ classDiagram + removePlayer(Player player) void + getCurrentPlayer() Player + getPlayers() List~Player~ + + getMaxPlayers() int # getPoints() int # addPoints() void # setSecretObjective(Objective obj) void @@ -238,10 +238,10 @@ classDiagram <> GOLDS_DECK, RESOURCES_DECK, - FIRST_VISIBLE_GOLDS, - SECOND_VISIBLE_GOLDS, - FIRST_VISIBLE_RESOURCES, - SECOND_VISIBLE_RESOURCES + FIRST_VISIBLE, + SECOND_VISIBLE, + THIRD_VISIBLE, + FOURTH_VISIBLE } class MatchState { @@ -266,11 +266,6 @@ classDiagram + addPlayer() void + removePlayer() void } - class SetupState{ - + SetupState(Match match) void - + transition() void - - } class NextTurnState { + NextTurnState(Match match) void + proposeSecretObjectives() void @@ -321,5 +316,6 @@ classDiagram + poll() C + shuffle() void + isEmpty() void + + getSize() int } diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index dd5fff13ac230fec2ba0f05379645b1e81e4236b..dde786b0cd1fd66b9ce582bd4fde561f1b0f922e 100644 GIT binary patch literal 110401 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 literal 98249 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 From 59285ccafda7593cdae0fe01af95692e064782dd Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Sat, 23 Mar 2024 13:52:29 +0100 Subject: [PATCH 29/68] Enhance and extend Player Javadoc, make some attributes final --- .../it/polimi/ingsw/gamemodel/Player.java | 56 +++++++++++++------ 1 file changed, 38 insertions(+), 18 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Player.java b/src/main/java/it/polimi/ingsw/gamemodel/Player.java index 6dcb8f52..dcf0b5f9 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Player.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Player.java @@ -1,4 +1,5 @@ package it.polimi.ingsw.gamemodel; + import it.polimi.ingsw.utils.Pair; import it.polimi.ingsw.exceptions.*; @@ -8,13 +9,18 @@ * It's also responsible for the board's logic, which is a slice of the game logic. */ public class Player { - private String nickname; - private Match match; + private final String nickname; + private final Match match; private int points; - private Board board; + private final Board board; private Color pawnColor; private Objective secretObjective; + /** + * Initializes the main player's attributes. + * @param nickname the player's nickname + * @param match the match the player belongs to + */ public Player(String nickname, Match match) { this.nickname = nickname; this.match = match; @@ -26,11 +32,12 @@ public Player(String nickname, Match match) { } /** - * Places on the board the desired card (on the specified side) in the given position, if it is a valid one - * @param coords x and y position in which the card is played (where 0, 0 is the initial card) - * @param card the card to be placed - * @param side whether the card should be placed on the front or on the back - */ + * Places a card on the player's board, on the give side and in the given position, + * assuming it's a valid one + * @param coords x and y position in which the card is played (where 0, 0 is the initial card) + * @param card the card to be placed + * @param side whether the card should be placed on the front or on the back + */ public void playCard(Pair coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException, WrongChoiceException { if (match.getCurrentPlayer().equals(this)) match.makeMove(coords, card, side); @@ -38,6 +45,11 @@ public void playCard(Pair coords, PlayableCard card, Side side throw new WrongTurnException("Only the current player can play cards"); } + /** + * Get an initial card from the match. + * @throws WrongTurnException if called by the player when it's not its turn + * @throws WrongStateException if called during the wrong match state + */ public InitialCard drawInitialCard() throws WrongTurnException, Exception, WrongStateException { if (match.getCurrentPlayer().equals(this)) { InitialCard card = match.drawInitialCard(); @@ -47,6 +59,12 @@ public InitialCard drawInitialCard() throws WrongTurnException, Exception, Wrong } } + /** + * Choose the initial card side. + * @param side the side of the initial + * @throws WrongTurnException if called by the player when it's not its turn + * @throws WrongStateException if called during the wrong match state + */ public void chooseInitialCardSide(Side side) throws WrongTurnException, WrongStateException { if (match.getCurrentPlayer().equals(this)) match.setInitialSide(side); @@ -71,13 +89,15 @@ public void drawCard(DrawSource source) throws WrongTurnException, WrongStateExc } /** - * Sets the player private objective (only at the start of the game). - * @param objective the chosen objective between the two proposed - * @throws WrongTurnException if called by the player when it's not its turn - */ - public void chooseSecretObjective(Objective objective) throws WrongTurnException { + * Sets the player private objective (only at the start of the game). + * @param objective the chosen objective between the two proposed + * @throws WrongTurnException if called by the player when it's not its turn + * @throws WrongStateException if called during the wrong match state + * @throws WrongChoiceException if called on an objective which is not one of the proposed ones + */ + public void chooseSecretObjective(Objective objective) throws WrongTurnException, WrongStateException, WrongChoiceException { if (match.getCurrentPlayer().equals(this)) { - match.chooseSecretObjective(objective); + match.setSecretObjective(objective); secretObjective = objective; } else { throw new WrongTurnException("Only the current player can choose an objective"); @@ -85,15 +105,15 @@ public void chooseSecretObjective(Objective objective) throws WrongTurnException } /** - * Getter for the player's board. - */ + * Getter for the player's board. + */ public Board getBoard() { return board; } /** - * Getter for the player's points. - */ + * Getter for the player's points. + */ public int getPoints() { return points; } From 8ac04bdd3217ee2ddcdb50d6a033a916b99a7286 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Sat, 23 Mar 2024 14:31:31 +0100 Subject: [PATCH 30/68] Change state behavoir - Removed setupstate - Changed how the states behave --- .../ingsw/gamemodel/AfterMoveState.java | 4 +++- .../gamemodel/ChooseInitialSideState.java | 9 ++++----- .../gamemodel/ChooseSecretObjectiveState.java | 10 +++++----- .../polimi/ingsw/gamemodel/NextTurnState.java | 10 ++-------- .../polimi/ingsw/gamemodel/ResourceCard.java | 3 +++ .../it/polimi/ingsw/gamemodel/SetupState.java | 20 ------------------- .../it/polimi/ingsw/gamemodel/WaitState.java | 17 +++++++++++----- 7 files changed, 29 insertions(+), 44 deletions(-) delete mode 100644 src/main/java/it/polimi/ingsw/gamemodel/SetupState.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java index 245790d6..dd52c888 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java @@ -1,5 +1,7 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.WrongStateException; + public class AfterMoveState extends MatchState { public AfterMoveState(Match match) { @@ -8,7 +10,7 @@ public AfterMoveState(Match match) { @Override public void drawCard() throws WrongStateException { - this.transition(); + } @Override diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java b/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java index ddab83eb..368d701a 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java @@ -8,16 +8,15 @@ public ChooseInitialSideState(Match match) { @Override public void chooseInitialSide() { - Player lastPlayer = match.getPlayers().getLast(); - - if (match.getCurrentPlayer().equals(lastPlayer)) - match.doInitialTurnFinish(); - this.transition(); } @Override public void transition() { + Player lastPlayer = match.getPlayers().getLast(); + + if (match.getCurrentPlayer().equals(lastPlayer)) + match.doInitialTurnFinish(); MatchState nextState = new NextTurnState(match); match.setState(nextState); } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java b/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java index 9ba40749..97872638 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java @@ -8,16 +8,16 @@ public ChooseSecretObjectiveState(Match match) { @Override public void chooseSecretObjective() { - Player lastPlayer = match.getPlayers().getLast(); - if (match.getCurrentPlayer().equals(lastPlayer)) - match.doStart(); - - this.transition(); } @Override public void transition() { + Player lastPlayer = match.getPlayers().getLast(); + + if (match.getCurrentPlayer().equals(lastPlayer)) + match.doStart(); + MatchState nextState = new NextTurnState(match); match.setState(nextState); } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java index 2be3df22..f2ba05d6 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java @@ -10,17 +10,13 @@ public NextTurnState(Match match) { @Override public void proposeSecretObjectives() throws WrongStateException { - if (match.isStarted()) + if (match.isStarted() || !match.isInitialTurnFinished()) throw new WrongStateException("proposeSecretObjectives called after the match was started"); - else - this.transition(); } @Override public void makeMove() throws WrongStateException { - if (match.isStarted()) - this.transition(); - else + if (!match.isStarted()) throw new WrongStateException("makeMove called when match was not started yet"); } @@ -28,8 +24,6 @@ public void makeMove() throws WrongStateException { public void drawInitialCard() throws WrongStateException { if (match.isInitialTurnFinished()) throw new WrongStateException("drawInitialCard called after the initial turn was finished"); - else - this.transition(); } @Override diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java b/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java index 1b1533ab..dbbdc911 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java @@ -13,4 +13,7 @@ public ResourceCard(CardFace front, CardFace back, int points) { this.front = front; this.back = back; this.points = points; } + public int getPoints() { + return this.points; + } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/SetupState.java b/src/main/java/it/polimi/ingsw/gamemodel/SetupState.java deleted file mode 100644 index a54ad47c..00000000 --- a/src/main/java/it/polimi/ingsw/gamemodel/SetupState.java +++ /dev/null @@ -1,20 +0,0 @@ -package it.polimi.ingsw.gamemodel; - -public class SetupState extends MatchState { - - SetupState(Match match) { - super(match); - - match.setupDecks(); - match.setupPlayers(); - match.setupBoards(); - - this.transition(); - } - - @Override - public void transition() { - MatchState nextState = new NextTurnState(match); - match.setState(nextState); - } -} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/WaitState.java b/src/main/java/it/polimi/ingsw/gamemodel/WaitState.java index 517851d1..37630091 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/WaitState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/WaitState.java @@ -1,5 +1,7 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.WrongStateException; + public class WaitState extends MatchState{ public WaitState(Match match) { @@ -8,8 +10,14 @@ public WaitState(Match match) { @Override public void transition() { - MatchState nextState = new SetupState(match); - match.setState(nextState); + if (match.isFull()) { + match.setupDecks(); + match.setupPlayers(); + match.setupBoards(); + + MatchState nextState = new NextTurnState(match); + match.setState(nextState); + } } @Override @@ -18,8 +26,7 @@ public void removePlayer() { } @Override - public void addPlayer() { - if (match.isFull()) - transition(); + public void addPlayer() throws WrongStateException { + //TBD } } From 500a399230327e43e711b4550d30ea0c8c4126e5 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Sat, 23 Mar 2024 14:31:44 +0100 Subject: [PATCH 31/68] Update Player and Match - Fix exception handling - Update methods to use the new states behavoir - Fix minor syntax errors - Add Match.getMaxPlayers method - Add Player.drawSecretObjectives method --- .../java/it/polimi/ingsw/gamemodel/Match.java | 126 +++++++++++------- .../it/polimi/ingsw/gamemodel/Player.java | 12 +- 2 files changed, 87 insertions(+), 51 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index 2ab6d710..3b03f9c1 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -10,7 +10,7 @@ * Represents the match being played by {@link Player} instances, therefore implements a slice of game logic * using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc. * Other methods serve the purpose of being called by {@link MatchState} subclasses in order to notify the change - * of the current game state or trigger some changes in the match, such as setupBoards(...), dofinish(...), + * of the current game state or trigger some changes in the match, such as setupBoards(...), * doStart(...), etc. * Few methods are called by the current player of the match, used to trigger a change in the match and so notify that * an event occurred, such as nextPlayer(...). @@ -53,13 +53,27 @@ public class Match { * @param resourcesDeck deck of resource cards * @param goldsDeck deck of gold cards * @param objectivesDeck deck of objectives + * @throws IllegalArgumentException if the decks provided do not have enough cards to start a game or maxPlayers are not 2,3,4 */ - public Match(int maxPlayers, GameDeck initialsDeck, GameDeck resourcesDeck, GameDeck goldsDeck, GameDeck objectivesDeck) { + public Match(int maxPlayers, GameDeck initialsDeck, GameDeck resourcesDeck, GameDeck goldsDeck, GameDeck objectivesDeck) throws IllegalArgumentException{ this.maxPlayers = maxPlayers; this.initialsDeck = initialsDeck; this.resourcesDeck = resourcesDeck; this.goldsDeck = goldsDeck; this.objectivesDeck = objectivesDeck; + this.currentState = new WaitState(this); + + if (goldsDeck.getSize() < 6) { + throw new IllegalArgumentException("goldsDeck does not have enough cards"); + } else if (resourcesDeck.getSize() < 10) { + throw new IllegalArgumentException("resourcesDeck does not have enough cards"); + } else if (initialsDeck.getSize() < maxPlayers) { + throw new IllegalArgumentException("initialDeck does not have enough cards"); + } else if (objectivesDeck.getSize() < 6) { + throw new IllegalArgumentException("objectivesDeck does not have enough cards"); + } else if (maxPlayers < 2 || maxPlayers > 4) { + throw new IllegalArgumentException("The players must be at least 2 or maximum 4"); + } this.players = new ArrayList(); this.visiblePlayableCards = new HashMap<>(); @@ -75,6 +89,7 @@ public void addPlayer(Player player) throws IllegalArgumentException, WrongState if(!players.contains(player)) { currentState.addPlayer(); players.add(player); + currentState.transition(); } else { throw new IllegalArgumentException("Duplicated Player in a Match"); } @@ -85,8 +100,10 @@ public void addPlayer(Player player) throws IllegalArgumentException, WrongState * Note: Called by the Controller when a player quits the match. * @param player player to be removed from the match */ - public void removePlayer(Player player) { + public void removePlayer(Player player) throws WrongStateException{ + currentState.removePlayer(); players.remove(player); + currentState.transition(); } /** @@ -119,14 +136,6 @@ protected void nextPlayer() { } } - /** - * Marks the match as finished, assuming the match hasn't finished yet. - * Note: Called by FinalState once the match is ready to finish. - */ - protected void doFinish() { - finished = true; - } - /** * Verifies if the match is finished. * Note: Called by the Controller and NextTurnState. @@ -198,9 +207,8 @@ protected void setState(MatchState state) { protected InitialCard drawInitialCard() throws Exception, WrongStateException { currentState.drawInitialCard(); - currentGivenInitialCard = initialsDeck.pop(); - + currentState.transition(); return currentGivenInitialCard; } @@ -209,12 +217,17 @@ protected InitialCard drawInitialCard() throws Exception, WrongStateException { * Note: Called by the Controller. * @return two objective cards extracted from the objectives deck */ - protected Pair proposeSecretObjectives() { - Objective obj1 = objectivesDeck.pop(); - Objective obj2 = objectivesDeck.pop(); - - currentProposedObjectives = new Pair<>(obj1, obj2); - return currentProposedObjectives; + protected Pair proposeSecretObjectives() throws WrongStateException { + currentState.proposeSecretObjectives(); + try { + Objective obj1 = objectivesDeck.pop(); + Objective obj2 = objectivesDeck.pop(); + currentProposedObjectives = new Pair<>(obj1, obj2); + currentState.transition(); + return currentProposedObjectives; + } catch (Exception e) { + throw new RuntimeException(e); + } } /** @@ -237,6 +250,7 @@ else if (objective.equals(secondProposedObjective)) else // If the objective is not one of the proposed ones, throw an exception throw new WrongChoiceException("The chosen objective is not one of the proposed ones"); + currentState.transition(); } /** @@ -263,26 +277,29 @@ protected void setupDecks() { resourcesDeck.shuffle(); goldsDeck.shuffle(); objectivesDeck.shuffle(); + try { + // Pop two resources to be placed on the common table + ResourceCard resourceCard1 = resourcesDeck.pop(); + ResourceCard resourceCard2 = resourcesDeck.pop(); - // Pop two resources to be placed on the common table - ResourceCard resourceCard1 = resourcesDeck.pop(); - ResourceCard resourceCard2 = resourcesDeck.pop(); - - // Pop two golds to be placed on the common table - GoldCard goldCard1 = goldsDeck.pop(); - GoldCard goldCard2 = goldsDeck.pop(); + // Pop two golds to be placed on the common table + GoldCard goldCard1 = goldsDeck.pop(); + GoldCard goldCard2 = goldsDeck.pop(); - // Pop two golds to be placed on the common table - Objective objective1 = objectivesDeck.pop(); - Objective objective2 = objectivesDeck.pop(); + // Pop two golds to be placed on the common table + Objective objective1 = objectivesDeck.pop(); + Objective objective2 = objectivesDeck.pop(); - // Put golds and resources in visiblePlayableCards - visiblePlayableCards.put(DrawSource.FIRST_VISIBLE, resourceCard1); - visiblePlayableCards.put(DrawSource.SECOND_VISIBLE, resourceCard2); - visiblePlayableCards.put(DrawSource.THIRD_VISIBLE, goldCard1); - visiblePlayableCards.put(DrawSource.FOURTH_VISIBLE, goldCard2); + // Put golds and resources in visiblePlayableCards + visiblePlayableCards.put(DrawSource.FIRST_VISIBLE, resourceCard1); + visiblePlayableCards.put(DrawSource.SECOND_VISIBLE, resourceCard2); + visiblePlayableCards.put(DrawSource.THIRD_VISIBLE, goldCard1); + visiblePlayableCards.put(DrawSource.FOURTH_VISIBLE, goldCard2); - visibleObjectives = new Pair<>(objective1, objective2); + visibleObjectives = new Pair<>(objective1, objective2); + } catch (Exception e) { + throw new RuntimeException(e); + } } /** @@ -293,15 +310,19 @@ protected void setupDecks() { protected void setupBoards() { // Give starting cards to players for (Player player : players) { - // Pop a card from the resources deck and one from the golds deck - GoldCard goldCard = goldsDeck.pop(); - ResourceCard resourceCard1 = resourcesDeck.pop(); - ResourceCard resourceCard2 = resourcesDeck.pop(); - - // Add each card to the player's hand - player.getBoard().addHandCard(goldCard); - player.getBoard().addHandCard(resourceCard1); - player.getBoard().addHandCard(resourceCard2); + try { + // Pop a card from the resources deck and one from the golds deck + GoldCard goldCard = goldsDeck.pop(); + ResourceCard resourceCard1 = resourcesDeck.pop(); + ResourceCard resourceCard2 = resourcesDeck.pop(); + + // Add each card to the player's hand + player.getBoard().addHandCard(goldCard); + player.getBoard().addHandCard(resourceCard1); + player.getBoard().addHandCard(resourceCard2); + } catch (Exception e) { + throw new RuntimeException(e); + } } } @@ -315,7 +336,7 @@ protected void setupBoards() { * @param side side of the card to be placed * @throws WrongStateException if called while in a state that doesn't allow making moves */ - protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException { + protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException, CardException { Board currentPlayerBoard = currentPlayer.getBoard(); // If placing the card in the current player's board is allowed by rules @@ -347,6 +368,7 @@ protected void makeMove(Pair coords, PlayableCard card, Side s // the match is now finished if (currentPlayer.equals(players.getLast()) && lastTurn) finished = true; + currentState.transition(); break; case INVALID_COORDS: throw new WrongChoiceException("Invalid coordinates!"); @@ -373,13 +395,13 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W if (goldsDeck.isEmpty()) throw new WrongChoiceException("Golds deck is empty!"); - card = goldsDeck.pop(); + card = goldsDeck.poll(); } case RESOURCES_DECK -> { if (resourcesDeck.isEmpty()) throw new WrongChoiceException("Resources deck is empty!"); - card = resourcesDeck.pop(); + card = resourcesDeck.poll(); } case FIRST_VISIBLE, SECOND_VISIBLE -> { // Get the chosen card @@ -421,13 +443,14 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W // If the golds deck is empty too, the GameDeck.poll() method returns null, // then the corresponding visible card will be null } - default: + default -> { throw new WrongChoiceException("Unexpected value: " + source); + } } if (goldsDeck.isEmpty() && resourcesDeck.isEmpty()) lastTurn = true; - + currentState.transition(); return card; } @@ -440,6 +463,7 @@ protected void setInitialSide(Side side) throws WrongStateException { currentState.chooseInitialSide(); currentPlayer.getBoard().setInitialCard(currentGivenInitialCard, side); currentGivenInitialCard = null; + currentState.transition(); } /** @@ -459,4 +483,8 @@ public Pair getVisibleObjectives() { public MatchState getCurrentState() { return currentState; } + + public int getMaxPlayers() { + return maxPlayers; + } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Player.java b/src/main/java/it/polimi/ingsw/gamemodel/Player.java index dcf0b5f9..18032fd7 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Player.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Player.java @@ -38,13 +38,21 @@ public Player(String nickname, Match match) { * @param card the card to be placed * @param side whether the card should be placed on the front or on the back */ - public void playCard(Pair coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException, WrongChoiceException { + public void playCard(Pair coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException, WrongChoiceException, CardException { if (match.getCurrentPlayer().equals(this)) match.makeMove(coords, card, side); else throw new WrongTurnException("Only the current player can play cards"); } + public Pair drawSecretObjectives() throws WrongStateException, WrongTurnException { + if (match.getCurrentPlayer().equals(this)) { + return match.proposeSecretObjectives(); + } else { + throw new WrongTurnException("Only the current player can draw secret objectives"); + } + } + /** * Get an initial card from the match. * @throws WrongTurnException if called by the player when it's not its turn @@ -79,7 +87,7 @@ public void chooseInitialCardSide(Side side) throws WrongTurnException, WrongSta * @throws WrongChoiceException if called on a drawing source which is empty (e.g. empty deck) * @throws WrongStateException if called during the wrong match state */ - public void drawCard(DrawSource source) throws WrongTurnException, WrongStateException, WrongChoiceException { + public void drawCard(DrawSource source) throws Exception { if (match.getCurrentPlayer().equals(this)) { PlayableCard card = match.drawCard(source); board.addHandCard(card); From 901a8db06196c5f07d0af67e1b383ea5dff5c36f Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Sat, 23 Mar 2024 14:32:29 +0100 Subject: [PATCH 32/68] Create a first test for Match --- .../it/polimi/ingsw/gamemodel/MatchTest.java | 46 ++++++++++++++++--- 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java index ef423fdd..4087c68a 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -1,16 +1,16 @@ package it.polimi.ingsw.gamemodel; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - import java.util.EnumSet; import java.util.Map; import java.util.Set; +import it.polimi.ingsw.utils.Pair; import org.junit.Test; import it.polimi.ingsw.exceptions.WrongStateException; +import static org.junit.Assert.*; + public class MatchTest { @Test @@ -23,19 +23,51 @@ public void shouldAnswerWithTrue() { Match match = new Match(2, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); Player p1 = new Player("Oingo", match); Player p2 = new Player("Boingo", match); + Player p3 = new Player("Foingo", match); try { match.addPlayer(p1); - match.addPlayer(p2); + match.addPlayer(p2); + } catch (WrongStateException e) { + fail("Players not added correctly"); + } + assertTrue("Match should have been full", match.isFull()); + assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof NextTurnState); + try { + match.addPlayer(p3); + fail("Should have thrown an exception"); } catch (WrongStateException e) { - e.printStackTrace(); + System.out.println(e.getMessage()); } + for (int i = 0; i < match.getMaxPlayers(); i++) { + try { + System.out.println("Player: " + match.getCurrentPlayer().getNickname() + "Chooses the initial card"); + InitialCard initial = match.getCurrentPlayer().drawInitialCard(); + assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof ChooseInitialSideState); + match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); + } catch (Exception e) { + fail(e.getMessage()); + } + } + assertFalse("Match should not have started", match.isStarted()); + + for (int i = 0; i < match.getMaxPlayers(); i++) { + try { + System.out.println("Player: " + match.getCurrentPlayer().getNickname() + "Chooses their objective"); + Pair objectives = match.getCurrentPlayer().drawSecretObjectives(); + assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof ChooseSecretObjectiveState); + match.getCurrentPlayer().chooseSecretObjective(objectives.first()); + } catch (Exception e) { + fail("Player " + i + e.getMessage()); + } + } + assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof NextTurnState); assertTrue( true ); } private GameDeck createObjectivesDeck() { - GameDeck objectivesDeck = new GameDeck(4); - for (int i = 0; i < 4; i++) { + GameDeck objectivesDeck = new GameDeck(8); + for (int i = 0; i < 8; i++) { objectivesDeck.add(generateRandomObjective()); } return objectivesDeck; From ece7201b95a1fe8f8f91498a7ebdb1fb715a9fab Mon Sep 17 00:00:00 2001 From: gixium Date: Sun, 24 Mar 2024 14:21:54 +0100 Subject: [PATCH 33/68] Add integrity check for GoldCard constructor --- .../java/it/polimi/ingsw/gamemodel/GoldCard.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java index 2ce3d677..dafc5523 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -1,6 +1,10 @@ package it.polimi.ingsw.gamemodel; import it.polimi.ingsw.exceptions.InvalidResourceException; + +import java.util.EnumSet; +import java.util.Map; + /** * The front side of these cards always gives points, but needs a certain requirement to be met in order to be played * @see CardFace @@ -22,8 +26,14 @@ public GoldCard(CardFace front, Symbol reign, Symbol multiplier, int points, Qua super(reign); this.front = front; this.points = points; + this.req = req; // integrity check already provided in the constructor of QuantityRequirement + + // integrity check for allowed multipliers + EnumSet validMultiplier = Symbol.getValidMultiplier(); + if(!validMultiplier.contains(multiplier)){ + throw new InvalidResourceException("Resource " + multiplier.toString() + " is not valid for a " + this.getClass().toString()); + } this.multiplier = multiplier; - this.req = req; } /** @@ -47,6 +57,7 @@ public QuantityRequirement getRequirement(){ * @param board The board on which we want to compute the points this card will give */ public int calculatePoints(Board board) { + return this.points; // will need to compute tot resources of board and get the tot resource } } From 4567eaa5c2c768adffd9eade0f04f756fa751107 Mon Sep 17 00:00:00 2001 From: gixium Date: Sun, 24 Mar 2024 14:22:26 +0100 Subject: [PATCH 34/68] Add integrity check for GoldCard constructor --- src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java index dafc5523..1a52a245 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -3,7 +3,6 @@ import it.polimi.ingsw.exceptions.InvalidResourceException; import java.util.EnumSet; -import java.util.Map; /** * The front side of these cards always gives points, but needs a certain requirement to be met in order to be played @@ -57,7 +56,6 @@ public QuantityRequirement getRequirement(){ * @param board The board on which we want to compute the points this card will give */ public int calculatePoints(Board board) { - return this.points; // will need to compute tot resources of board and get the tot resource } } From 68bd3b02950404ed817b54ea05d313017e43415b Mon Sep 17 00:00:00 2001 From: gixium Date: Sun, 24 Mar 2024 14:55:55 +0100 Subject: [PATCH 35/68] Add method calculatePoints in GoldCard --- .../it/polimi/ingsw/gamemodel/GoldCard.java | 28 +++++++++++++++++-- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java index 1a52a245..f9ffd9dc 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -3,6 +3,7 @@ import it.polimi.ingsw.exceptions.InvalidResourceException; import java.util.EnumSet; +import java.util.Map; /** * The front side of these cards always gives points, but needs a certain requirement to be met in order to be played @@ -52,10 +53,31 @@ public QuantityRequirement getRequirement(){ } /** - * Will compute the total points this card gives based on the board it's played on - * @param board The board on which we want to compute the points this card will give + * Will compute the total points this card gives based on the board it's played on. + * It MUST be called AFTER the placement of the gold card. + * @param board the board on which we want to compute the points this card will give + * @return the points gained from playing the gold card */ public int calculatePoints(Board board) { - return this.points; // will need to compute tot resources of board and get the tot resource + Map availableResources = board.getAvailableResources(); + + int totalElements = 0; + for(Symbol s : availableResources.keySet()){ + if(s.equals(this.multiplier)){ + totalElements = availableResources.get(s); + } + } + + return totalElements * this.points; } } + + + + + + + + + + From f2cbb612ab69556c67ea97f0716af4857df08376 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Mon, 25 Mar 2024 11:23:52 +0100 Subject: [PATCH 36/68] Refactor testing, complete board, update javadoc --- .../java/it/polimi/ingsw/gamemodel/Board.java | 168 ++++++++++-------- .../ingsw/gamemodel/PlacementOutcome.java | 4 + .../it/polimi/ingsw/gamemodel/Symbol.java | 16 ++ .../it/polimi/ingsw/gamemodel/BoardTest.java | 126 +++++++++---- 4 files changed, 198 insertions(+), 116 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index fef693e1..6b42427e 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -8,13 +8,6 @@ import it.polimi.ingsw.utils.Pair; - -/* -- implement resource elaboration on placed cards -- Update Javadoc -- More elegant way for diagonl controls (they are very similar to resource update controls) -*/ - /** * Board is the class that contains all the informations relative to a {@link Player}'s status */ @@ -23,6 +16,13 @@ public class Board { private Map, PlacedCard> placed; private Map availableResources; + private static final Map, Corner> diagonalChecks = Map.of( + new Pair<>(-1, +1), Corner.BOTTOM_RIGHT, + new Pair<>(+1, +1), Corner.BOTTOM_LEFT, + new Pair<>(-1, -1), Corner.TOP_RIGHT, + new Pair<>(+1, -1), Corner.TOP_LEFT + ); + /** * Class constructor. No inputs taken as the board starts empty */ @@ -37,6 +37,7 @@ public Board() { /** * Getter for the total resources of a player + * @return the resources of a player */ public Map getAvailableResources() { return this.availableResources; @@ -45,14 +46,28 @@ public Map getAvailableResources() { /** * Getter for the hand of the player (which must be composed of three {@link PlayableCard}), which is visible * to every player + * @return the player's hand */ public List getCurrentHand() { return this.currentHand; } + /** + * Removes a card from the hand of the player + * @param card the card that must be removed from the player's hand + * @throws HandException if the player does not have exactly 3 cards in his hand + */ + protected void removeHandCard(PlayableCard card) throws HandException { + if (currentHand.size() != 3) { + throw new HandException("Tried to remove a card from an empty hand!"); + } + currentHand.remove(card); + } + /** * Adds a card to the player's hand (which is visible to every player) * @param card the card to put in the hand + * @throws HandException if the player already has 3 cards */ protected void addHandCard(PlayableCard card) throws HandException { if (currentHand.size() > 2) { // la mano ha 3 carte max @@ -62,34 +77,30 @@ protected void addHandCard(PlayableCard card) throws HandException { } /** - * The initial card will be added by {@link Match} at the start of the game, and it will be set on the front side by default. - * During the first turn of the player, he will be asked if he wants to switch side with this method - * @param side the desired side for the initial card + * Places the initial card in the (0, 0) coordinates, on the desired side + * @param card the initial card + * @param side the desired side + * @throws CardException if the (0, 0) position is already occupied */ - protected void setInitialCard(InitialCard card) throws CardException { + protected void setInitialCard(InitialCard card, Side side) throws CardException { if (placed.get(new Pair<>(0,0)) != null) { throw new CardException("Tried to add initial card, but one already exists!"); } - placed.put(new Pair<>(0, 0), new PlacedCard(card, Side.FRONT, 0)); - } - /** - * The initial card will be added by {@link Match} at the start of the game, and it will be set on the front side by default. - * During the first turn of the player, he will be asked if he wants to switch side with this method - * @param side the desired side for the initial card - */ - protected void setInitialSide(Side side) { - placed.get(new Pair<>(0, 0)); - } + placed.put(new Pair<>(0, 0), new PlacedCard(card, side, 0)); - /** - * Removes a card from the hand of the player - * @param card the card that must be removed from the player's hand - */ - protected void removeHandCard(PlayableCard card) throws HandException { - if (currentHand.size() <= 0) { - throw new HandException("Tried to remove a card from an empty hand!"); + Symbol cornerSymbol; + for (Corner c : Corner.values()) { + cornerSymbol = card.getSide(side).getCorner(c); + if (Symbol.getBasicResources().contains(cornerSymbol)) { + availableResources.put(cornerSymbol, availableResources.get(cornerSymbol)+1); + } + } + + for (Symbol s : card.getSide(side).getCenter()) { + if (Symbol.getBasicResources().contains(s)) { + availableResources.put(s, availableResources.get(s)+1); + } } - currentHand.remove(card); } /** @@ -99,6 +110,7 @@ protected void removeHandCard(PlayableCard card) throws HandException { * @param side the side of the card to be placed * @param turn the turn of the game in which the card is played * @return the points gained from playing card + * @throws CardException if the card type is not known (neither a {@link ResourceCard} nor a {@link GoldCard}) */ protected int placeCard(Pair coord, PlayableCard card, Side side, int turn) throws CardException { PlacedCard last = new PlacedCard(card, side, turn); @@ -113,31 +125,52 @@ protected int placeCard(Pair coord, PlayableCard card, Side si throw new CardException("Unknow card type: " + card.getClass().toString() + "!"); } + Symbol cornerSymbol; + Integer x = coord.first(); + Integer y = coord.second(); + + for (Pair diagOffset : diagonalChecks.keySet()) { + cornerSymbol = this.getSymbolIfPresent(new Pair<>(x+diagOffset.first(), y+diagOffset.second()), diagonalChecks.get(diagOffset)); + if (cornerSymbol != null) { + if (Symbol.getBasicResources().contains(cornerSymbol)) { + availableResources.put(cornerSymbol, availableResources.get(cornerSymbol) - 1); + } + } + } + + for (Corner c : Corner.values()) { + cornerSymbol = card.getSide(side).getCorner(c); + if (Symbol.getBasicResources().contains(cornerSymbol)) { + availableResources.put(cornerSymbol, availableResources.get(cornerSymbol)+1); + } + } + + for (Symbol s : card.getSide(side).getCenter()) { + if (Symbol.getBasicResources().contains(s)) { + availableResources.put(s, availableResources.get(s)+1); + } + } return points; } - private boolean hasDiagonalAdjacent(Pair coord) { - Integer[] offsets = {-1, +1}; - Pair cmp; - - for (Integer xOffset : offsets) { - for (Integer yOffset : offsets) { - cmp = new Pair<>(coord.first() + xOffset, coord.second() + yOffset); - if (placed.keySet().contains(cmp)) { - return true; - } - } + private Symbol getSymbolIfPresent(Pair coords, Corner corner) { + PlacedCard placedCard = placed.get(coords); + if (placedCard == null) { + return null; } - return false; + return placedCard.getCard().getSide(placedCard.getPlayedSide()).getCorner(corner); } /** - * Checks whether the given position is valid (ie there are no adjacent cards with an empty angle and there is at least one adjacent card), - * if the card is in the player's hand and if the card requirement is met - * @param coord the x and y coordinates to check - * @return whether the given coordinates are valid or not + * Checks wheter the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card), + * the given coordinates must be valid, and if the card has a requirement it must be met + * @param coord the coordinates in which the card should be played + * @param card the card to check on + * @param side the side of the card (needed for requirement check) + * @return the outcome for the placement, which is valid only if all conditions are met + * @throws CardException if the card is not in the player's hand */ public PlacementOutcome verifyCardPlacement(Pair coord, Card card, Side side) throws CardException { if (coord.equals(new Pair<>(0, 0))) { @@ -145,7 +178,7 @@ public PlacementOutcome verifyCardPlacement(Pair coord, Card c } if (!currentHand.contains(card)) { - throw new CardException("The card " + card.getClass().toString() + " is not in the player's hand!"); + throw new CardException("The card is not in the player's hand!"); } if (placed.keySet().contains(coord)) { return PlacementOutcome.INVALID_COORDS; @@ -172,42 +205,23 @@ public PlacementOutcome verifyCardPlacement(Pair coord, Card c } } - // diagonal check: at least one exists and none has an empty corner - if (!hasDiagonalAdjacent(coord)) { - return PlacementOutcome.INVALID_COORDS; - } + boolean hasAdjacent = false; - Symbol adjacentCornerSymbol; - cmp = new Pair(coord.first()-1, coord.second()+1); - if (placed.get(cmp) != null ) { - adjacentCornerSymbol = placed.get(cmp).getPlayedCardFace().getCorner(Corner.BOTTOM_RIGHT); - if (adjacentCornerSymbol == Symbol.EMPTY_CORNER) { - return PlacementOutcome.INVALID_COORDS; - } - } + Integer x = coord.first(); + Integer y = coord.second(); - cmp = new Pair(coord.first()+1, coord.second()+1); - if (placed.get(cmp) != null ) { - adjacentCornerSymbol = placed.get(cmp).getPlayedCardFace().getCorner(Corner.BOTTOM_LEFT); - if (adjacentCornerSymbol == Symbol.EMPTY_CORNER) { - return PlacementOutcome.INVALID_COORDS; - } - } + for (Pair diagOffset : Board.diagonalChecks.keySet()) { + cmp = new Pair(x+diagOffset.first(), y+diagOffset.second()); - cmp = new Pair(coord.first()-1, coord.second()-1); - if (placed.get(cmp) != null ) { - adjacentCornerSymbol = placed.get(cmp).getPlayedCardFace().getCorner(Corner.TOP_RIGHT); - if (adjacentCornerSymbol == Symbol.EMPTY_CORNER) { - return PlacementOutcome.INVALID_COORDS; + if (placed.get(cmp) != null ) { + hasAdjacent = true; + if (placed.get(cmp).getPlayedCardFace().getCorner(diagonalChecks.get(diagOffset)) == Symbol.EMPTY_CORNER) { + return PlacementOutcome.INVALID_COORDS; + } } } - - cmp = new Pair(coord.first()-1, coord.second()+1); - if (placed.get(cmp) != null ) { - adjacentCornerSymbol = placed.get(cmp).getPlayedCardFace().getCorner(Corner.BOTTOM_RIGHT); - if (adjacentCornerSymbol == Symbol.EMPTY_CORNER) { - return PlacementOutcome.INVALID_COORDS; - } + if (!hasAdjacent) { + return PlacementOutcome.INVALID_COORDS; } return PlacementOutcome.VALID; diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java b/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java index 66f97e2b..5b39d055 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlacementOutcome.java @@ -1,5 +1,9 @@ package it.polimi.ingsw.gamemodel; +/** +* Before placing a card, its placement must be verified. This enum is used to know if it is a valid one and, if it isn't, what went wrong +* @see Board#verifyCardPlacement(it.polimi.ingsw.utils.Pair, Card, Side) +*/ public enum PlacementOutcome { VALID, INVALID_COORDS, diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java b/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java index f8c974bb..3f3c2603 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java @@ -19,6 +19,10 @@ public enum Symbol { FULL_CORNER, CORNER_OBJ; + /** + * Generates subest containing only the four basic reigns + * @return the subset containing only the four basic reigns + */ static public EnumSet getReigns() { return EnumSet.of( ANIMAL, @@ -28,6 +32,10 @@ static public EnumSet getReigns() { ); } + /** + * Generates subest containing only the basic resources (4 reigns and 3 "symbols") + * @return the subset containing only the basic resources (4 reigns and 3 "symbols") + */ static public EnumSet getBasicResources() { return EnumSet.of( ANIMAL, @@ -40,6 +48,10 @@ static public EnumSet getBasicResources() { ); } + /** + * Generates subest containing all the valid Symbols a corner can contain + * @return the subset containing all the valid Symbols a corner can contain + */ static public EnumSet getValidCorner() { return EnumSet.of( ANIMAL, @@ -54,6 +66,10 @@ static public EnumSet getValidCorner() { ); } + /** + * Generates subest containing all the symbols a {@link GoldCard} multiplier can be + * @return the subset containing all the symbols a {@link GoldCard} multiplier can be + */ static public EnumSet getValidMultiplier() { return EnumSet.of( ANIMAL, diff --git a/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java b/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java index 7ad981c6..26a723a7 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java @@ -9,66 +9,114 @@ import org.junit.Test; -import it.polimi.ingsw.gamemodel.*; import it.polimi.ingsw.utils.*; -/** - * Unit test for simple App. - */ -public class BoardTest -{ - /** - * Rigorous Test :-) - */ - @Test - public void shouldAnswerWithTrue() { - Board board = new Board(); +public class BoardTest { + InitialCard initial; + PlayableCard base; + PlayableCard validGold; + PlayableCard invalidGold; + PlayableCard brempty; + PlayableCard nores; - try { + Board board; + public BoardTest() { + board = new Board(); - InitialCard initial = new InitialCard( + try { + initial = new InitialCard( new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.PLANT)), new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.PLANT)) ); - PlayableCard init = new ResourceCard( - new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.PLANT)), + base = new ResourceCard( + new CardFace(Symbol.PLANT, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.PLANT)), Symbol.PLANT, 1 ); - PlayableCard empty = new ResourceCard( - new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.EMPTY_CORNER, Set.of(Symbol.PLANT)), + brempty = new ResourceCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.EMPTY_CORNER, Set.of()), + Symbol.PLANT, 1 + ); + + nores = new ResourceCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of()), Symbol.PLANT, 1 ); - Map m = new HashMap(); - m.put(Symbol.PLANT, 2); - PlayableCard gold = new GoldCard( + Map valid = new HashMap(); + valid.put(Symbol.PLANT, 5); + + Map invalid = new HashMap(); + invalid.put(Symbol.FUNGUS, 2); + + validGold = new GoldCard( new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.EMPTY_CORNER, Set.of(Symbol.PLANT)), - Symbol.PLANT, Symbol.INKWELL, 2, new QuantityRequirement(m) + Symbol.PLANT, Symbol.INKWELL, 2, new QuantityRequirement(valid) ); - board.addHandCard(init); - board.addHandCard(empty); - board.addHandCard(gold); - assertEquals(board.verifyCardPlacement(new Pair<>(0, 0), initial, Side.FRONT), PlacementOutcome.INVALID_COORDS); - board.setInitialCard(initial); - board.placeCard(new Pair(1, 1), init, Side.FRONT, 0); - board.placeCard(new Pair(2, 0), empty, Side.FRONT, 0); - board.placeCard(new Pair(-1, -1), init, Side.FRONT, 0); - board.placeCard(new Pair(0, -2), init, Side.FRONT, 0); - board.placeCard(new Pair(1, -3), init, Side.FRONT, 0); - board.placeCard(new Pair(2, -2), init, Side.FRONT, 0); - assertEquals(board.verifyCardPlacement(new Pair<>(3, -1), init, Side.FRONT), PlacementOutcome.INVALID_COORDS); - assertEquals(board.verifyCardPlacement(new Pair<>(-1, -1), init, Side.FRONT), PlacementOutcome.INVALID_COORDS); - assertEquals(board.verifyCardPlacement(new Pair<>(0, -1), init, Side.FRONT), PlacementOutcome.INVALID_COORDS); - assertEquals(board.verifyCardPlacement(new Pair<>(2, 2), empty, Side.FRONT), PlacementOutcome.VALID); - assertEquals(board.verifyCardPlacement(new Pair<>(2, 2), gold, Side.FRONT), PlacementOutcome.INVALID_ENOUGH_RESOURCES); + board.setInitialCard(initial, Side.FRONT); // +1 plant + board.placeCard(new Pair<>(1,1), brempty, Side.FRONT, 0); // +0 plant + board.placeCard(new Pair<>(-1,-1), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(0,-2), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(1,-3), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(2,-2), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(3,-1), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(-2,0), base, Side.FRONT, 0); // +2 plant -1 plant + + board.placeCard(new Pair<>(0,-4), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(-1,-5), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(0,-6), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(1,-7), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(2,-6), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(3,-5), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(2,-4), nores, Side.FRONT, 0); // +0 + + board.addHandCard(base); + board.addHandCard(brempty); + board.addHandCard(validGold); + verifyResources(Symbol.PLANT, 12); } catch (Exception e) { System.err.println(e); - assertTrue(false); // not expecting any exception for now, if exception was expected remove this and adapt + assertTrue(false); } } + + @Test + public void verifyBasicPlacement() { + try { + // (0, 0) is reserved to initial + assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(0, 0), base, Side.FRONT)); + // (1, 1) is already occupied + assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(0, 0), base, Side.FRONT)); + // (2, 0) is invalid since in (1, 1) there is a bottom right corner empty + assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(2, 0), base, Side.FRONT)); + // (1, -1) is a valid placement (covers 4 corners) + assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -1), base, Side.FRONT)); + // (1, -5) is a valid placement + assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -5), base, Side.FRONT)); + // no adjacent + assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(5, 5), base, Side.FRONT)); + // enough res + assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -5), validGold, Side.FRONT)); + + board.removeHandCard(base); + board.addHandCard(invalidGold); + // not enough res + assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -5), invalidGold, Side.FRONT)); + board.removeHandCard(invalidGold); + board.addHandCard(base); + + board.verifyCardPlacement(new Pair<>(1, -5), invalidGold, Side.FRONT); + } catch (Exception e) { + System.err.println(e); + assertEquals("The card is not in the player's hand!", e.getMessage()); + } + } + + public void verifyResources(Symbol s, Integer expected) { + assertEquals(expected, board.getAvailableResources().get(s)); + } } From 479cbba3fef2fba248ecf6a626c096df36481e35 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Mon, 25 Mar 2024 11:47:16 +0100 Subject: [PATCH 37/68] Add getter for placed cards --- src/main/java/it/polimi/ingsw/gamemodel/Board.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 6b42427e..43b51b4f 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -43,6 +43,14 @@ public Map getAvailableResources() { return this.availableResources; } + /** + * Getter for the board's placed cards + * @return map containing all the placed cards indexed by their coordinates + */ + public Map, PlacedCard> getPlacedCards() { + return this.placed; + } + /** * Getter for the hand of the player (which must be composed of three {@link PlayableCard}), which is visible * to every player @@ -118,7 +126,7 @@ protected int placeCard(Pair coord, PlayableCard card, Side si int points = 0; if (card instanceof GoldCard) { - points = ((GoldCard)card).calculatePoints(this); + points = ((GoldCard)card).calculatePoints(this, coord); } else if (card instanceof ResourceCard) { points = ((ResourceCard)card).getPoints(); } else { @@ -155,8 +163,8 @@ protected int placeCard(Pair coord, PlayableCard card, Side si return points; } - private Symbol getSymbolIfPresent(Pair coords, Corner corner) { - PlacedCard placedCard = placed.get(coords); + private Symbol getSymbolIfPresent(Pair coord, Corner corner) { + PlacedCard placedCard = placed.get(coord); if (placedCard == null) { return null; } From daf5dc89a5e262b7b4106c79e870ca2396512f40 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Mon, 25 Mar 2024 12:01:46 +0100 Subject: [PATCH 38/68] Fix javadoc --- src/main/java/it/polimi/ingsw/gamemodel/Match.java | 4 ++-- .../it/polimi/ingsw/gamemodel/QuantityRequirement.java | 2 +- src/main/java/it/polimi/ingsw/gamemodel/Requirement.java | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index b64ef96c..a816a5c5 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -240,9 +240,9 @@ protected void setupBoards() throws Exception { * @param card * @param side * @throws WrongStateException - * @throws WrongCardPlacementException + * @throws CardException */ - protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException { + protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, CardException { Board currentPlayerBoard = currentPlayer.getBoard(); // If placing the card in the current player's board is allowed by rules diff --git a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java index fb5c651f..48770ef9 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java @@ -12,8 +12,8 @@ public class QuantityRequirement extends Requirement{ private Map reqs; /** + * Only valid symbols are the ones returned by {@link Symbol#getBasicResources()} * @param reqs how many resources of a certain type are needed to fulfill the requirement. - * Only valid symbols are the ones returned by {@link Symbol#getValidRequirement()} * @throws InvalidResourceException if a requirement is not made up only of those symbols */ public QuantityRequirement(Map reqs) throws InvalidResourceException { diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java index b8e2989c..b5c1e496 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java @@ -6,14 +6,14 @@ */ public abstract class Requirement { + public Requirement() throws InvalidResourceException { + + } + /** * Will be implemented on the concrete classes, as they have different kind of conditions * @param board the board that will be used to check if the requirement is met * @return whether the requirement is met or not */ - public Requirement() throws InvalidResourceException { - - } - public abstract boolean isSatisfied(Board board); } From d1fe8c146da98f046c5cfc8dd1b85cf26b5a8cec Mon Sep 17 00:00:00 2001 From: gixium Date: Mon, 25 Mar 2024 12:54:05 +0100 Subject: [PATCH 39/68] Add first implementation of GoldCardTest. Improve method calculatePoints. --- .../it/polimi/ingsw/gamemodel/GoldCard.java | 53 ++++++--- .../polimi/ingsw/gamemodel/GoldCardTest.java | 105 ++++++++++++++++++ 2 files changed, 145 insertions(+), 13 deletions(-) create mode 100644 src/test/java/it/polimi/ingsw/gamemodel/GoldCardTest.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java index f9ffd9dc..7e26bef9 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -1,9 +1,13 @@ package it.polimi.ingsw.gamemodel; import it.polimi.ingsw.exceptions.InvalidResourceException; +import it.polimi.ingsw.utils.Pair; import java.util.EnumSet; +import java.util.HashSet; import java.util.Map; +import java.util.Set; + /** * The front side of these cards always gives points, but needs a certain requirement to be met in order to be played @@ -56,28 +60,51 @@ public QuantityRequirement getRequirement(){ * Will compute the total points this card gives based on the board it's played on. * It MUST be called AFTER the placement of the gold card. * @param board the board on which we want to compute the points this card will give + * @param coord the coordinates of the card just placed (needed fot corner objectives) * @return the points gained from playing the gold card */ - public int calculatePoints(Board board) { + public int calculatePoints(Board board, Pair coord) { Map availableResources = board.getAvailableResources(); int totalElements = 0; - for(Symbol s : availableResources.keySet()){ - if(s.equals(this.multiplier)){ - totalElements = availableResources.get(s); - } - } - - return totalElements * this.points; - } -} - - - + // multiplier is basic resource (subset of symbols) + if(Symbol.getBasicResources().contains(this.multiplier)){ + for(Symbol s : availableResources.keySet()){ + if(s.equals(this.multiplier)){ + totalElements = availableResources.get(s); + } + } + } else if (this.multiplier.equals(Symbol.CORNER_OBJ)) { //multiplier is a corner_objective kind + // Pair currentCoord = board.getCoordinatesPlacedCard(); + Set> edges = getEdges(coord); + Map, PlacedCard> map = board.getPlacedCards(); + for(Pair p : edges){ + // check if the board has a value (card) associated to the key (coordinates) + if(map.get(p) != null){ + totalElements++; + } + } + } + return totalElements * this.points; + } + private static Set> getEdges(Pair currentCoord) { + Pair tr = new Pair<>(currentCoord.first()+1, currentCoord.second()+1); + Pair br = new Pair<>(currentCoord.first()+1, currentCoord.second()-1); + Pair tl = new Pair<>(currentCoord.first()-1, currentCoord.second()-1); + Pair bl = new Pair<>(currentCoord.first()-1, currentCoord.second()+1); + + Set> edges = new HashSet<>(); + edges.add(tr); + edges.add(br); + edges.add(tl); + edges.add(bl); + return edges; + } +} diff --git a/src/test/java/it/polimi/ingsw/gamemodel/GoldCardTest.java b/src/test/java/it/polimi/ingsw/gamemodel/GoldCardTest.java new file mode 100644 index 00000000..58749ed6 --- /dev/null +++ b/src/test/java/it/polimi/ingsw/gamemodel/GoldCardTest.java @@ -0,0 +1,105 @@ +package it.polimi.ingsw.gamemodel; + +import it.polimi.ingsw.utils.Pair; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import static org.junit.Assert.assertEquals; + +/** + * Unit test for class GoldCard. + */ +public class GoldCardTest +{ + /** + * Unit test for method calculatePoints. + */ + @Test + public void testGoldCard(){ + Board board = new Board(); // created empty board + + try{ + // created initial card with full corners, a plant in the bottom right and a fungus in the center, for both front and back + InitialCard initial_alpha = new InitialCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.PLANT, Set.of(Symbol.FUNGUS)), + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.PLANT, Set.of(Symbol.FUNGUS)) + ); + + // created gold card with full corners, an inkwell on the bottom right and an insect in the center of the back + // 1 point, multiplier is inkwell, requires 1 plant + Map m1 = new HashMap(); m1.put(Symbol.PLANT, 1); + PlayableCard gold_beta = new GoldCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.INKWELL, Set.of(Symbol.INSECT)), + Symbol.INSECT, + Symbol.INKWELL, + 1, + new QuantityRequirement(m1) + + ); + + // created gold card with full corners and an insect in the center of the back + // 2 points, multiplier is inkwell, requires 1 inkwell + Map m2 = new HashMap(); m2.put(Symbol.INKWELL, 1); + PlayableCard gold_gamma = new GoldCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.INSECT)), + Symbol.INSECT, + Symbol.INKWELL, + 2, + new QuantityRequirement(m2) + ); + + // created gold card with full corners and an insect in the center of the back + // 2 points, multiplier is animal, requires 1 fungus + Map m3 = new HashMap(); m3.put(Symbol.FUNGUS, 1); + PlayableCard gold_delta = new GoldCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.INSECT)), + Symbol.INSECT, + Symbol.ANIMAL, + 3, + new QuantityRequirement(m3) + ); + + // created gold card with full corners and an insect in the center of the back + // 2 points, multiplier is corner_object, requires 1 fungus + PlayableCard gold_epsilon = new GoldCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.INSECT)), + Symbol.INSECT, + Symbol.CORNER_OBJ, + 2, + new QuantityRequirement(m3) + ); + + // placement phase: initial card and the four gold cards + board.setInitialCard(initial_alpha, Side.FRONT); + + // case auto-point: gold_beta contains its multiplier on one corner + Pair coord_beta = new Pair(-1, 1); + board.placeCard(coord_beta, gold_beta, Side.FRONT, 0); + assertEquals(1, ((GoldCard) gold_beta).calculatePoints(board, coord_beta)); + + // case self-sabotage: gold_gamma placement covers the only corner containing the multiplier + Pair coord_gamma = new Pair(-2, 2); + board.placeCard(coord_gamma, gold_gamma, Side.FRONT, 0); + assertEquals(0, ((GoldCard) gold_gamma).calculatePoints(board, coord_gamma)); + + // case normal-none: there are no multipliers on board + Pair coord_delta = new Pair(1, 1); + board.placeCard(coord_delta, gold_delta, Side.FRONT, 0); + assertEquals(0, ((GoldCard) gold_delta).calculatePoints(board, coord_delta)); + + // case positioning: gold_epsilon covers 2 edges + Pair coord_epsilon = new Pair(0, 2); + board.placeCard(coord_epsilon, gold_epsilon, Side.FRONT, 0); + assertEquals(4, ((GoldCard) gold_epsilon).calculatePoints(board, coord_epsilon)); + + } catch (Exception e) { + System.err.println(e); + // fail("Error: " + e.getMessage()); + } + } + + +} \ No newline at end of file From 142637e3f80e478ce2257e9a113cada43a157110 Mon Sep 17 00:00:00 2001 From: gixium Date: Mon, 25 Mar 2024 13:08:46 +0100 Subject: [PATCH 40/68] Temp refactor of FSM --- src/main/java/it/polimi/ingsw/gamemodel/Match.java | 2 +- src/main/java/it/polimi/ingsw/gamemodel/MatchState.java | 4 ++-- src/main/java/it/polimi/ingsw/gamemodel/SetupState.java | 2 +- src/main/java/it/polimi/ingsw/gamemodel/WaitState.java | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index a816a5c5..5a1a5efd 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -57,7 +57,7 @@ public Match(int maxPlayers, GameDeck initialsDeck, GameDeck Date: Mon, 25 Mar 2024 14:25:47 +0100 Subject: [PATCH 41/68] Fix error in calcuatePoints --- .../java/it/polimi/ingsw/gamemodel/Board.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 43b51b4f..de7b2baa 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -125,14 +125,6 @@ protected int placeCard(Pair coord, PlayableCard card, Side si this.placed.put(coord, last); int points = 0; - if (card instanceof GoldCard) { - points = ((GoldCard)card).calculatePoints(this, coord); - } else if (card instanceof ResourceCard) { - points = ((ResourceCard)card).getPoints(); - } else { - throw new CardException("Unknow card type: " + card.getClass().toString() + "!"); - } - Symbol cornerSymbol; Integer x = coord.first(); Integer y = coord.second(); @@ -159,6 +151,14 @@ protected int placeCard(Pair coord, PlayableCard card, Side si } } + if (card instanceof GoldCard) { + points = ((GoldCard)card).calculatePoints(this, coord); + } else if (card instanceof ResourceCard) { + points = ((ResourceCard)card).getPoints(); + } else { + throw new CardException("Unknow card type: " + card.getClass().toString() + "!"); + } + return points; } From 7d5d37c897ae4f50d74633480805004d53cefe36 Mon Sep 17 00:00:00 2001 From: gixium Date: Mon, 25 Mar 2024 14:47:08 +0100 Subject: [PATCH 42/68] GoldCard is complete, add comprehensive test --- .../polimi/ingsw/gamemodel/GoldCardTest.java | 134 ++++++++++-------- 1 file changed, 72 insertions(+), 62 deletions(-) diff --git a/src/test/java/it/polimi/ingsw/gamemodel/GoldCardTest.java b/src/test/java/it/polimi/ingsw/gamemodel/GoldCardTest.java index 58749ed6..d63847c1 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/GoldCardTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/GoldCardTest.java @@ -3,103 +3,113 @@ import it.polimi.ingsw.utils.Pair; import org.junit.Test; -import java.util.HashMap; import java.util.Map; import java.util.Set; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; /** * Unit test for class GoldCard. */ public class GoldCardTest { + Board board; + InitialCard initialCard; + PlayableCard goldAutopoint; + PlayableCard goldCovering; + PlayableCard goldNoPoints; + PlayableCard goldPositioning; + + Pair coordAutopoint; + Pair coordCovering; + Pair coordNoPoints; + Pair coordPositioning; + /** - * Unit test for method calculatePoints. - */ - @Test - public void testGoldCard(){ - Board board = new Board(); // created empty board + * Unit test for method calculatePoints. + */ + public GoldCardTest(){ + board = new Board(); // created empty board try{ - // created initial card with full corners, a plant in the bottom right and a fungus in the center, for both front and back - InitialCard initial_alpha = new InitialCard( + // map of symbols for the center of the card + Map mapGoldAutopoint = Map.of(Symbol.PLANT, 1); + Map mapGoldCovering = Map.of(Symbol.INKWELL, 1); + Map mapGoldNoPoints_Positioning = Map.of(Symbol.FUNGUS, 1); + + // coordinates for each card to be placed + coordAutopoint = new Pair(1, -1); + coordCovering = new Pair(2, -2); + coordNoPoints = new Pair(1, 1); + coordPositioning = new Pair(2, 0); + + // creation of the cards + initialCard = new InitialCard( new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.PLANT, Set.of(Symbol.FUNGUS)), new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.PLANT, Set.of(Symbol.FUNGUS)) ); - // created gold card with full corners, an inkwell on the bottom right and an insect in the center of the back - // 1 point, multiplier is inkwell, requires 1 plant - Map m1 = new HashMap(); m1.put(Symbol.PLANT, 1); - PlayableCard gold_beta = new GoldCard( + goldAutopoint = new GoldCard( new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.INKWELL, Set.of(Symbol.INSECT)), - Symbol.INSECT, - Symbol.INKWELL, - 1, - new QuantityRequirement(m1) - + Symbol.INSECT, Symbol.INKWELL, 1, new QuantityRequirement(mapGoldAutopoint) ); - // created gold card with full corners and an insect in the center of the back - // 2 points, multiplier is inkwell, requires 1 inkwell - Map m2 = new HashMap(); m2.put(Symbol.INKWELL, 1); - PlayableCard gold_gamma = new GoldCard( + goldCovering = new GoldCard( new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.INSECT)), - Symbol.INSECT, - Symbol.INKWELL, - 2, - new QuantityRequirement(m2) + Symbol.INSECT, Symbol.INKWELL, 2, new QuantityRequirement(mapGoldCovering) ); - // created gold card with full corners and an insect in the center of the back - // 2 points, multiplier is animal, requires 1 fungus - Map m3 = new HashMap(); m3.put(Symbol.FUNGUS, 1); - PlayableCard gold_delta = new GoldCard( + goldNoPoints = new GoldCard( new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.INSECT)), - Symbol.INSECT, - Symbol.ANIMAL, - 3, - new QuantityRequirement(m3) + Symbol.INSECT, Symbol.ANIMAL, 3, new QuantityRequirement(mapGoldNoPoints_Positioning) ); - // created gold card with full corners and an insect in the center of the back - // 2 points, multiplier is corner_object, requires 1 fungus - PlayableCard gold_epsilon = new GoldCard( + goldPositioning = new GoldCard( new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of(Symbol.INSECT)), - Symbol.INSECT, - Symbol.CORNER_OBJ, - 2, - new QuantityRequirement(m3) + Symbol.INSECT, Symbol.CORNER_OBJ, 2, new QuantityRequirement(mapGoldNoPoints_Positioning) ); - // placement phase: initial card and the four gold cards - board.setInitialCard(initial_alpha, Side.FRONT); - - // case auto-point: gold_beta contains its multiplier on one corner - Pair coord_beta = new Pair(-1, 1); - board.placeCard(coord_beta, gold_beta, Side.FRONT, 0); - assertEquals(1, ((GoldCard) gold_beta).calculatePoints(board, coord_beta)); + // placement phase: initial card + board.setInitialCard(initialCard, Side.FRONT); - // case self-sabotage: gold_gamma placement covers the only corner containing the multiplier - Pair coord_gamma = new Pair(-2, 2); - board.placeCard(coord_gamma, gold_gamma, Side.FRONT, 0); - assertEquals(0, ((GoldCard) gold_gamma).calculatePoints(board, coord_gamma)); - - // case normal-none: there are no multipliers on board - Pair coord_delta = new Pair(1, 1); - board.placeCard(coord_delta, gold_delta, Side.FRONT, 0); - assertEquals(0, ((GoldCard) gold_delta).calculatePoints(board, coord_delta)); - - // case positioning: gold_epsilon covers 2 edges - Pair coord_epsilon = new Pair(0, 2); - board.placeCard(coord_epsilon, gold_epsilon, Side.FRONT, 0); - assertEquals(4, ((GoldCard) gold_epsilon).calculatePoints(board, coord_epsilon)); + } catch (Exception e) { + System.err.println(e); + assertTrue(false); + } + } + @Test + public void verifyPointCalculation(){ + try{ + assertEquals(1, board.placeCard(coordAutopoint, goldAutopoint, Side.FRONT, 0)); + assertEquals(0, board.placeCard(coordCovering, goldCovering, Side.FRONT, 0)); + assertEquals(0, board.placeCard(coordNoPoints, goldNoPoints, Side.FRONT, 0)); + assertEquals(4, board.placeCard(coordPositioning, goldPositioning, Side.FRONT, 0)); } catch (Exception e) { System.err.println(e); - // fail("Error: " + e.getMessage()); + assertTrue(false); } } +/* info: + // initial card has full corners, a plant in the bottom right and a fungus in the center, for both front and back + + // goldAutopoint contains its multiplier on one corner + // created gold card with full corners, an inkwell on the bottom right and an insect in the center of the back + // 1 point, multiplier is inkwell, requires 1 plant + + // goldCovering placement covers the only corner containing the multiplier + // created gold card with full corners and an insect in the center of the back + // 2 points, multiplier is inkwell, requires 1 inkwell + + // goldNoPoints has no multipliers on board + // created gold card with full corners and an insect in the center of the back + // 2 points, multiplier is animal, requires 1 fungus + + // goldPositioning covers 2 edges + // created gold card with full corners and an insect in the center of the back + // 2 points, multiplier is corner_object, requires 1 fungus +*/ } \ No newline at end of file From 85cbed4f5a12063586d51e6bee44fbda24f9937e Mon Sep 17 00:00:00 2001 From: gixium Date: Mon, 25 Mar 2024 15:10:03 +0100 Subject: [PATCH 43/68] Update UML --- deliveries/UML/class_diagram.mmd | 17 ++++++++--------- deliveries/UML/model.pdf | Bin 110401 -> 109687 bytes 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index a1678654..c2ca3957 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -105,7 +105,7 @@ classDiagram + GoldCard(CardFace front, Symbol reign, int points, Symbol multiplier, QuantityRequirement req) + getMultiplier() Symbol + getRequirement() QuantityRequirement - + calculatePoints(Board board) int + + calculatePoints(Board board, Pair~Integer, Integer~ coord) int } class Requirement { @@ -170,15 +170,15 @@ classDiagram - availableResources: Map~Symbol, Integer~ + Board() - + getCurrentHand() List~PlayableCard~ + getAvailableResources() Map~Symbol, Integer~ - # getPlacedMap() Map<Pair<Integer,Integer>, PlacedCard> - # addHandCard(PlayableCard card) void + + getPlacedCards() Map<Pair<Integer, Integer>, PlacedCard> + + getCurrentHand() List~PlayableCard~ # removeHandCard(PlayableCard card) void - # placeCard(Pair~Integer, Integer~, PlayableCard card, Side side, int turn) int + # addHandCard(PlayableCard card) void # setInitialCard(InitialCard card, Side side) void + # placeCard(Pair~Integer, Integer~ coord, PlayableCard card, Side side, int turn) int + verifyCardPlacement(Pair~Integer, Integer~ coords, Card card, Side side) PlacementOutcome - } + } class PlacedCard { <> - card: Card @@ -308,14 +308,13 @@ classDiagram } class GameDeck { <> - - int size - cardsList: List~C~ - + GameDeck(int size) + + GameDeck() + add(C card) void + + getSize() int + pop() C + poll() C + shuffle() void + isEmpty() void - + getSize() int } diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index dde786b0cd1fd66b9ce582bd4fde561f1b0f922e..511a97e548b19badc52e96dea17135ead952cdda 100644 GIT binary patch literal 109687 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 b@Ws^)XCo))o$sv#K)rDoDh`hGs&fAeG)cm@ literal 110401 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 From 72cd059f4dc5b2f34eb1a2c7ecd430d59bb68131 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Mon, 25 Mar 2024 16:41:23 +0100 Subject: [PATCH 44/68] Refactor requirements due to misinterpretation of rules --- .../ingsw/exceptions/DeckException.java | 8 +++ .../java/it/polimi/ingsw/gamemodel/Board.java | 12 ++-- .../it/polimi/ingsw/gamemodel/GameDeck.java | 48 +++++++++++---- .../ingsw/gamemodel/PositionRequirement.java | 26 +++++++- .../ingsw/gamemodel/QuantityRequirement.java | 15 +++-- .../polimi/ingsw/gamemodel/Requirement.java | 2 +- .../it/polimi/ingsw/gamemodel/BoardTest.java | 60 ++++++++++--------- 7 files changed, 118 insertions(+), 53 deletions(-) create mode 100644 src/main/java/it/polimi/ingsw/exceptions/DeckException.java diff --git a/src/main/java/it/polimi/ingsw/exceptions/DeckException.java b/src/main/java/it/polimi/ingsw/exceptions/DeckException.java new file mode 100644 index 00000000..a388e321 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/DeckException.java @@ -0,0 +1,8 @@ +package it.polimi.ingsw.exceptions; + +public class DeckException extends Exception { + public DeckException(String s) { + super(s); + } +} + diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index de7b2baa..063ff138 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -16,7 +16,7 @@ public class Board { private Map, PlacedCard> placed; private Map availableResources; - private static final Map, Corner> diagonalChecks = Map.of( + private static final Map, Corner> diagonalOffsets = Map.of( new Pair<>(-1, +1), Corner.BOTTOM_RIGHT, new Pair<>(+1, +1), Corner.BOTTOM_LEFT, new Pair<>(-1, -1), Corner.TOP_RIGHT, @@ -129,8 +129,8 @@ protected int placeCard(Pair coord, PlayableCard card, Side si Integer x = coord.first(); Integer y = coord.second(); - for (Pair diagOffset : diagonalChecks.keySet()) { - cornerSymbol = this.getSymbolIfPresent(new Pair<>(x+diagOffset.first(), y+diagOffset.second()), diagonalChecks.get(diagOffset)); + for (Pair diagOffset : diagonalOffsets.keySet()) { + cornerSymbol = this.getSymbolIfPresent(new Pair<>(x+diagOffset.first(), y+diagOffset.second()), diagonalOffsets.get(diagOffset)); if (cornerSymbol != null) { if (Symbol.getBasicResources().contains(cornerSymbol)) { availableResources.put(cornerSymbol, availableResources.get(cornerSymbol) - 1); @@ -191,7 +191,7 @@ public PlacementOutcome verifyCardPlacement(Pair coord, Card c if (placed.keySet().contains(coord)) { return PlacementOutcome.INVALID_COORDS; } - if (card instanceof GoldCard && !((GoldCard)card).getRequirement().isSatisfied(this)) { + if (card instanceof GoldCard && ((GoldCard)card).getRequirement().isSatisfied(this) == 0) { return PlacementOutcome.INVALID_ENOUGH_RESOURCES; } @@ -218,12 +218,12 @@ public PlacementOutcome verifyCardPlacement(Pair coord, Card c Integer x = coord.first(); Integer y = coord.second(); - for (Pair diagOffset : Board.diagonalChecks.keySet()) { + for (Pair diagOffset : Board.diagonalOffsets.keySet()) { cmp = new Pair(x+diagOffset.first(), y+diagOffset.second()); if (placed.get(cmp) != null ) { hasAdjacent = true; - if (placed.get(cmp).getPlayedCardFace().getCorner(diagonalChecks.get(diagOffset)) == Symbol.EMPTY_CORNER) { + if (placed.get(cmp).getPlayedCardFace().getCorner(diagonalOffsets.get(diagOffset)) == Symbol.EMPTY_CORNER) { return PlacementOutcome.INVALID_COORDS; } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java index 13e31afe..8c4bd18b 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java @@ -1,45 +1,73 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.DeckException; + import java.util.ArrayList; +import java.util.Collections; import java.util.List; -/* +/** * Generic used to create the decks for all the types of cards */ public class GameDeck { - private int size; private List cardsList; /** - * Constructor of the class, which will initialize the deck empty - * @param size initial size of the deck + * Class constructor, takes no argument as the decks start empty */ - public GameDeck(int size) { - this.size = size; + public GameDeck() { cardsList = new ArrayList<>(); } + /** + * Adds a card to the bottom of the deck + * @param card the card to add + */ public void add(T card) { + this.cardsList.add(card); + } + /** + * Getter for the deck's size + * @return the deck's size + */ + public int getSize() { + return this.cardsList.size(); } - public T pop() throws Exception { + /** + * Removes a card from the deck's top (throws exception if the deck is empty) + * @return the removed card + * @throws DeckException if the deck is empty + */ + public T pop() throws DeckException { if (this.isEmpty()) - throw new Exception("Tried to draw from an empty deck!"); + throw new DeckException("Tried to draw from an empty deck!"); return cardsList.removeLast(); } + /** + * Removes a card from the deck's top (null if the deck is empty) + * @return the removed card (null if the deck is empty) + */ public T poll() { if (this.isEmpty()) return null; return cardsList.removeLast(); } + /** + * Shuffles the deck + */ public void shuffle() { - + Collections.shuffle(this.cardsList); } + /** + * Checks whether the deck is empty or not + * @return whether the deck is empty or not + */ public boolean isEmpty() { - return false; + return this.cardsList.isEmpty(); } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java index f6b44870..2b5b0929 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java @@ -34,9 +34,31 @@ public PositionRequirement(Map, Symbol> reqs) throws Inva * @return wheter the board actually meets the requirement or not */ @Override - public boolean isSatisfied(Board board) { + public int isSatisfied(Board board) { + Map, PlacedCard> placedCards = board.getPlacedCards(); - return true; + Card cmpPlaced; + + boolean requirementMatched; + int timesMet = 0; + + for (Pair coord : placedCards.keySet()) { + requirementMatched = true; + for (Pair offset : this.reqs.keySet()) { + cmpPlaced = placedCards.get(new Pair<>(coord.first()+offset.first(), coord.second()+offset.second())).getCard(); + if ((!(cmpPlaced instanceof PlayableCard)) || ((PlayableCard)cmpPlaced).getReign() != this.reqs.get(offset)) { + requirementMatched = false; + } + } + if (requirementMatched) { + timesMet++; + } + } + return timesMet; } } + +// (0, 0), FUNGUS +// (1, 1), INSECT +// (2, 2), FUNGUS diff --git a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java index 48770ef9..d32a0009 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java @@ -32,13 +32,18 @@ public QuantityRequirement(Map reqs) throws InvalidResourceExce * @return whether the requirement is satisfied or not */ @Override - public boolean isSatisfied(Board board) { - for (Symbol s : reqs.keySet()) { - if (board.getAvailableResources().get(s) < reqs.get(s)) { - return false; + public int isSatisfied(Board board) { + Map availableResources = board.getAvailableResources(); + Integer min = null; + Integer curr; + + for (Symbol req : reqs.keySet()) { + curr = availableResources.get(req)/reqs.get(req); + if (min == null || curr < min) { + min = curr; } } - return true; + return min; } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java index b5c1e496..8828c066 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java @@ -15,5 +15,5 @@ public Requirement() throws InvalidResourceException { * @param board the board that will be used to check if the requirement is met * @return whether the requirement is met or not */ - public abstract boolean isSatisfied(Board board); + public abstract int isSatisfied(Board board); } diff --git a/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java b/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java index 26a723a7..8829cb30 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java @@ -2,12 +2,11 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; +import org.junit.Test; -import java.util.HashMap; import java.util.Map; import java.util.Set; -import org.junit.Test; import it.polimi.ingsw.utils.*; @@ -45,33 +44,36 @@ public BoardTest() { Symbol.PLANT, 1 ); - Map valid = new HashMap(); - valid.put(Symbol.PLANT, 5); + Map valid = Map.of(Symbol.PLANT, 5); - Map invalid = new HashMap(); - invalid.put(Symbol.FUNGUS, 2); + Map invalid = Map.of(Symbol.FUNGUS, 2); validGold = new GoldCard( new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.EMPTY_CORNER, Set.of(Symbol.PLANT)), Symbol.PLANT, Symbol.INKWELL, 2, new QuantityRequirement(valid) ); + invalidGold = new GoldCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.EMPTY_CORNER, Set.of(Symbol.PLANT)), + Symbol.PLANT, Symbol.INKWELL, 2, new QuantityRequirement(invalid) + ); + board.setInitialCard(initial, Side.FRONT); // +1 plant - board.placeCard(new Pair<>(1,1), brempty, Side.FRONT, 0); // +0 plant - board.placeCard(new Pair<>(-1,-1), base, Side.FRONT, 0); // +2 plant - board.placeCard(new Pair<>(0,-2), base, Side.FRONT, 0); // +2 plant - board.placeCard(new Pair<>(1,-3), base, Side.FRONT, 0); // +2 plant - board.placeCard(new Pair<>(2,-2), base, Side.FRONT, 0); // +2 plant - board.placeCard(new Pair<>(3,-1), base, Side.FRONT, 0); // +2 plant - board.placeCard(new Pair<>(-2,0), base, Side.FRONT, 0); // +2 plant -1 plant - - board.placeCard(new Pair<>(0,-4), nores, Side.FRONT, 0); // +0 - board.placeCard(new Pair<>(-1,-5), nores, Side.FRONT, 0); // +0 - board.placeCard(new Pair<>(0,-6), nores, Side.FRONT, 0); // +0 - board.placeCard(new Pair<>(1,-7), nores, Side.FRONT, 0); // +0 - board.placeCard(new Pair<>(2,-6), nores, Side.FRONT, 0); // +0 - board.placeCard(new Pair<>(3,-5), nores, Side.FRONT, 0); // +0 - board.placeCard(new Pair<>(2,-4), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(1,1), brempty, Side.FRONT, 0); // +0 plant + board.placeCard(new Pair<>(-1,-1), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(0,-2), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(1,-3), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(2,-2), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(3,-1), base, Side.FRONT, 0); // +2 plant + board.placeCard(new Pair<>(-2,0), base, Side.FRONT, 0); // +2 plant -1 plant + + board.placeCard(new Pair<>(0,-4), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(-1,-5), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(0,-6), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(1,-7), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(2,-6), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(3,-5), nores, Side.FRONT, 0); // +0 + board.placeCard(new Pair<>(2,-4), nores, Side.FRONT, 0); // +0 board.addHandCard(base); board.addHandCard(brempty); @@ -88,24 +90,24 @@ public BoardTest() { public void verifyBasicPlacement() { try { // (0, 0) is reserved to initial - assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(0, 0), base, Side.FRONT)); + assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(0, 0), base, Side.FRONT)); // (1, 1) is already occupied - assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(0, 0), base, Side.FRONT)); + assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(0, 0), base, Side.FRONT)); // (2, 0) is invalid since in (1, 1) there is a bottom right corner empty - assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(2, 0), base, Side.FRONT)); + assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(2, 0), base, Side.FRONT)); // (1, -1) is a valid placement (covers 4 corners) - assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -1), base, Side.FRONT)); + assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -1), base, Side.FRONT)); // (1, -5) is a valid placement - assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -5), base, Side.FRONT)); + assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -5), base, Side.FRONT)); // no adjacent - assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(5, 5), base, Side.FRONT)); + assertEquals(PlacementOutcome.INVALID_COORDS, board.verifyCardPlacement(new Pair<>(5, 5), base, Side.FRONT)); // enough res - assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -5), validGold, Side.FRONT)); + assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -5), validGold, Side.FRONT)); board.removeHandCard(base); board.addHandCard(invalidGold); // not enough res - assertEquals(PlacementOutcome.VALID, board.verifyCardPlacement(new Pair<>(1, -5), invalidGold, Side.FRONT)); + assertEquals(PlacementOutcome.INVALID_ENOUGH_RESOURCES, board.verifyCardPlacement(new Pair<>(1, -5), invalidGold, Side.FRONT)); board.removeHandCard(invalidGold); board.addHandCard(base); From 021d6daae153fd1a3671cc4b3314beacb69ce79d Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Mon, 25 Mar 2024 17:34:57 +0100 Subject: [PATCH 45/68] Positionrequirement now works --- .../java/it/polimi/ingsw/gamemodel/Board.java | 1 - .../ingsw/gamemodel/PositionRequirement.java | 23 +++- .../gamemodel/PositionRequirementTest.java | 113 ++++++++++++++++++ 3 files changed, 133 insertions(+), 4 deletions(-) create mode 100644 src/test/java/it/polimi/ingsw/gamemodel/PositionRequirementTest.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 063ff138..c77a0c32 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -184,7 +184,6 @@ public PlacementOutcome verifyCardPlacement(Pair coord, Card c if (coord.equals(new Pair<>(0, 0))) { return PlacementOutcome.INVALID_COORDS; } - if (!currentHand.contains(card)) { throw new CardException("The card is not in the player's hand!"); } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java index 2b5b0929..5f8e5e55 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java @@ -1,6 +1,8 @@ package it.polimi.ingsw.gamemodel; +import java.util.ArrayList; import java.util.EnumSet; +import java.util.List; import java.util.Map; import it.polimi.ingsw.exceptions.InvalidResourceException; @@ -36,21 +38,36 @@ public PositionRequirement(Map, Symbol> reqs) throws Inva @Override public int isSatisfied(Board board) { Map, PlacedCard> placedCards = board.getPlacedCards(); + List> alreadyUsed = new ArrayList<>(); - Card cmpPlaced; + PlacedCard cmpPlaced; + Card cmp; boolean requirementMatched; int timesMet = 0; + Pair tmp; + for (Pair coord : placedCards.keySet()) { requirementMatched = true; + // for each card in the board for (Pair offset : this.reqs.keySet()) { - cmpPlaced = placedCards.get(new Pair<>(coord.first()+offset.first(), coord.second()+offset.second())).getCard(); - if ((!(cmpPlaced instanceof PlayableCard)) || ((PlayableCard)cmpPlaced).getReign() != this.reqs.get(offset)) { + tmp = new Pair<>(coord.first()+offset.first(), coord.second()+offset.second()); + cmpPlaced = placedCards.get(tmp); + if (cmpPlaced != null) { // card in required position does not exist so go next + cmp = cmpPlaced.getCard(); + // if the card is not a playable it is the initial (so go next), and if the reign is not matched go next + if ((!(cmp instanceof PlayableCard)) || ((PlayableCard)cmp).getReign() != this.reqs.get(offset) || alreadyUsed.indexOf(tmp) != -1) { + requirementMatched = false; + } + } else { requirementMatched = false; } } if (requirementMatched) { + for (Pair offset : reqs.keySet()) { + alreadyUsed.add(new Pair<>(coord.first() + offset.first(), coord.second()+offset.second())); + } timesMet++; } } diff --git a/src/test/java/it/polimi/ingsw/gamemodel/PositionRequirementTest.java b/src/test/java/it/polimi/ingsw/gamemodel/PositionRequirementTest.java new file mode 100644 index 00000000..4b4211de --- /dev/null +++ b/src/test/java/it/polimi/ingsw/gamemodel/PositionRequirementTest.java @@ -0,0 +1,113 @@ +package it.polimi.ingsw.gamemodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +import java.util.Map; +import java.util.Set; + + +import it.polimi.ingsw.utils.*; + +public class PositionRequirementTest { + Board board; + PlayableCard plant; + PlayableCard insect; + PlayableCard fungus; + + public PositionRequirementTest() { + board = new Board(); + + try { + plant = new ResourceCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of()), + Symbol.PLANT, 0 + ); + insect = new ResourceCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of()), + Symbol.INSECT, 0 + ); + + fungus = new ResourceCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of()), + Symbol.FUNGUS, 0 + ); + + board.setInitialCard(new InitialCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of()), + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of()) + ), Side.FRONT); + + board.placeCard(new Pair<>(1, -1), insect, Side.FRONT, 0); + + board.placeCard(new Pair<>(1, 1), insect, Side.FRONT, 0); + board.placeCard(new Pair<>(1, 3), insect, Side.FRONT, 0); + board.placeCard(new Pair<>(2, 4), plant, Side.FRONT, 0); + + board.placeCard(new Pair<>(2, 2), insect, Side.FRONT, 0); + + board.placeCard(new Pair<>(1, 5), insect, Side.FRONT, 0); + board.placeCard(new Pair<>(2, 6), plant, Side.FRONT, 0); + + board.placeCard(new Pair<>(3, 1), insect, Side.FRONT, 0); + + board.placeCard(new Pair<>(3, 5), plant, Side.FRONT, 0); + board.placeCard(new Pair<>(3, 7), plant, Side.FRONT, 0); + board.placeCard(new Pair<>(4, 8), plant, Side.FRONT, 0); + board.placeCard(new Pair<>(5, 9), plant, Side.FRONT, 0); + + } catch (Exception e) { + System.err.println(e); + assertTrue(false); + } + } + + @Test + public void verifyPositionReqs() { + try { + assertEquals(1, new PositionRequirement( + Map.of( + new Pair<>(0, 0), Symbol.INSECT, + new Pair<>(0, 2), Symbol.INSECT, + new Pair<>(1, 3), Symbol.PLANT + ) + ).isSatisfied(board)); + + assertEquals(0, new PositionRequirement( + Map.of( + new Pair<>(0, 0), Symbol.PLANT, + new Pair<>(0, 2), Symbol.INSECT, + new Pair<>(1, 3), Symbol.INSECT + ) + ).isSatisfied(board)); + + assertEquals(1, new PositionRequirement( + Map.of( + new Pair<>(0, 0), Symbol.PLANT, + new Pair<>(1, 1), Symbol.PLANT, + new Pair<>(2, 2), Symbol.PLANT + ) + ).isSatisfied(board)); + + assertEquals(1, new PositionRequirement( + Map.of( + new Pair<>(0, 0), Symbol.INSECT, + new Pair<>(0, 2), Symbol.INSECT, + new Pair<>(1, 3), Symbol.INSECT + ) + ).isSatisfied(board)); + + assertEquals(1, new PositionRequirement( + Map.of( + new Pair<>(0, 0), Symbol.INSECT, + new Pair<>(1, -1), Symbol.INSECT, + new Pair<>(2, -2), Symbol.INSECT + ) + ).isSatisfied(board)); + } catch (Exception e) { + System.err.println(e); + assertTrue(false); + } + } +} From 881ff24ca37954b2b514659eed6e0df0cff1e6f4 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Mon, 25 Mar 2024 17:57:45 +0100 Subject: [PATCH 46/68] Refactor: isSatisfied() -> timesMet() --- src/main/java/it/polimi/ingsw/gamemodel/Board.java | 2 +- .../it/polimi/ingsw/gamemodel/PositionRequirement.java | 2 +- .../it/polimi/ingsw/gamemodel/QuantityRequirement.java | 2 +- .../java/it/polimi/ingsw/gamemodel/Requirement.java | 2 +- .../ingsw/gamemodel/PositionRequirementTest.java | 10 +++++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index c77a0c32..288745e8 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -190,7 +190,7 @@ public PlacementOutcome verifyCardPlacement(Pair coord, Card c if (placed.keySet().contains(coord)) { return PlacementOutcome.INVALID_COORDS; } - if (card instanceof GoldCard && ((GoldCard)card).getRequirement().isSatisfied(this) == 0) { + if (card instanceof GoldCard && ((GoldCard)card).getRequirement().timesMet(this) == 0) { return PlacementOutcome.INVALID_ENOUGH_RESOURCES; } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java index 5f8e5e55..5b63eeb1 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java @@ -36,7 +36,7 @@ public PositionRequirement(Map, Symbol> reqs) throws Inva * @return wheter the board actually meets the requirement or not */ @Override - public int isSatisfied(Board board) { + public int timesMet(Board board) { Map, PlacedCard> placedCards = board.getPlacedCards(); List> alreadyUsed = new ArrayList<>(); diff --git a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java index d32a0009..1dd3daca 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java @@ -32,7 +32,7 @@ public QuantityRequirement(Map reqs) throws InvalidResourceExce * @return whether the requirement is satisfied or not */ @Override - public int isSatisfied(Board board) { + public int timesMet(Board board) { Map availableResources = board.getAvailableResources(); Integer min = null; Integer curr; diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java index 8828c066..645b0273 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java @@ -15,5 +15,5 @@ public Requirement() throws InvalidResourceException { * @param board the board that will be used to check if the requirement is met * @return whether the requirement is met or not */ - public abstract int isSatisfied(Board board); + public abstract int timesMet(Board board); } diff --git a/src/test/java/it/polimi/ingsw/gamemodel/PositionRequirementTest.java b/src/test/java/it/polimi/ingsw/gamemodel/PositionRequirementTest.java index 4b4211de..ff0df46d 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/PositionRequirementTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/PositionRequirementTest.java @@ -72,7 +72,7 @@ public void verifyPositionReqs() { new Pair<>(0, 2), Symbol.INSECT, new Pair<>(1, 3), Symbol.PLANT ) - ).isSatisfied(board)); + ).timesMet(board)); assertEquals(0, new PositionRequirement( Map.of( @@ -80,7 +80,7 @@ public void verifyPositionReqs() { new Pair<>(0, 2), Symbol.INSECT, new Pair<>(1, 3), Symbol.INSECT ) - ).isSatisfied(board)); + ).timesMet(board)); assertEquals(1, new PositionRequirement( Map.of( @@ -88,7 +88,7 @@ public void verifyPositionReqs() { new Pair<>(1, 1), Symbol.PLANT, new Pair<>(2, 2), Symbol.PLANT ) - ).isSatisfied(board)); + ).timesMet(board)); assertEquals(1, new PositionRequirement( Map.of( @@ -96,7 +96,7 @@ public void verifyPositionReqs() { new Pair<>(0, 2), Symbol.INSECT, new Pair<>(1, 3), Symbol.INSECT ) - ).isSatisfied(board)); + ).timesMet(board)); assertEquals(1, new PositionRequirement( Map.of( @@ -104,7 +104,7 @@ public void verifyPositionReqs() { new Pair<>(1, -1), Symbol.INSECT, new Pair<>(2, -2), Symbol.INSECT ) - ).isSatisfied(board)); + ).timesMet(board)); } catch (Exception e) { System.err.println(e); assertTrue(false); From c29e07827b0a691596e5186df8b8d2a8f20a88bd Mon Sep 17 00:00:00 2001 From: gixium Date: Mon, 25 Mar 2024 18:28:45 +0100 Subject: [PATCH 47/68] Update UML, method isSatisfied is now timesMet --- deliveries/UML/class_diagram.mmd | 6 +++--- deliveries/UML/model.pdf | Bin 109687 -> 109694 bytes 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index c2ca3957..6f66ad50 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -110,20 +110,20 @@ classDiagram class Requirement { <> - + isSatisfied(Board board) bool - Requirement() + + timesMet(Board board) int } class QuantityRequirement { - reqs : Map~Symbol, Integer~ + QuantityRequirement(Symbol simbol, Integer quantity) - + isSatisfied(Board board) bool + + timesMet(Board board) int } class PositionRequirement { - reqs: Map<Pair<Integer, Integer>, Color> + PositionRequirement(Map<Pair<Integer, Integer>, Color>) - + isSatisfied(Board board) bool + + timesMet(Board board) int } class Objective { - points: int diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index 511a97e548b19badc52e96dea17135ead952cdda..5a8f2588a143039ab4378f728b3b8a1c7373f641 100644 GIT binary patch delta 20996 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 delta 21012 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 Tf3AR3`3Mek?Ce>^D@y+d4PuZ2 From 97b88c014f20afb1c7e2a58dd9413f6653c81f13 Mon Sep 17 00:00:00 2001 From: gixium Date: Mon, 25 Mar 2024 19:04:49 +0100 Subject: [PATCH 48/68] Fix UML rendering --- deliveries/UML/class_diagram.mmd | 6 +++--- deliveries/UML/model.pdf | Bin 109694 -> 109126 bytes 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index 6f66ad50..48f285b8 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -20,22 +20,22 @@ classDiagram PlayableCard <|-- ResourceCard PlayableCard <|-- GoldCard + Board -- PlacementOutcome Requirement <|-- QuantityRequirement Requirement <|-- PositionRequirement Requirement <-- Objective QuantityRequirement <-- GoldCard Board --> PlacedCard - Board -- PlacementOutcome + Side <-- PlacedCard Player --> Board Player --> Objective + Player --> Color CardFace <-- Card Symbol <-- CardFace - Player --> Color Board --> Requirement %%Side <-- Player %%Side <-- Match - Side <-- PlacedCard Corner <-- CardFace %%Symbol <-- GoldCard %% DrawSource <-- Player diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index 5a8f2588a143039ab4378f728b3b8a1c7373f641..9e1ad5b1d5fc0afb45860f03d45c4f3b097b40c6 100644 GIT binary patch delta 24012 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 KVd2x-D*p#qwhcc3 delta 24342 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 Date: Mon, 25 Mar 2024 22:16:42 +0100 Subject: [PATCH 49/68] Add GameDeck tests --- .../java/it/polimi/ingsw/gamemodel/Board.java | 1 - .../polimi/ingsw/gamemodel/GameDeckTest.java | 42 +++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 src/test/java/it/polimi/ingsw/gamemodel/GameDeckTest.java diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 288745e8..613646a0 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -235,4 +235,3 @@ public PlacementOutcome verifyCardPlacement(Pair coord, Card c } } - diff --git a/src/test/java/it/polimi/ingsw/gamemodel/GameDeckTest.java b/src/test/java/it/polimi/ingsw/gamemodel/GameDeckTest.java new file mode 100644 index 00000000..379b5199 --- /dev/null +++ b/src/test/java/it/polimi/ingsw/gamemodel/GameDeckTest.java @@ -0,0 +1,42 @@ +package it.polimi.ingsw.gamemodel; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import org.junit.Test; + +import java.util.Set; + +public class GameDeckTest { + GameDeck deck; + ResourceCard base; // sadge, no PlayableCard + + public GameDeckTest() { + try { + deck = new GameDeck<>(); + base = new ResourceCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Set.of()), + Symbol.PLANT, 0 + ); + + deck.add(base); + deck.add(base); + } catch (Exception e) { + System.err.println(e); + assertTrue(false); + } + } + + @Test + public void testAll() { + try { + assertEquals(2, deck.getSize()); + assertEquals(base, deck.pop()); + assertEquals(base, deck.poll()); + assertEquals(0, deck.getSize()); + assertEquals(null, deck.poll()); + deck.pop(); + } catch (Exception e) { + assertEquals("Tried to draw from an empty deck!", e.getMessage()); + } + } +} From b042ec0ee02863a62728ad935716be08026e4c4e Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Tue, 26 Mar 2024 00:16:55 +0100 Subject: [PATCH 50/68] Update javadoc --- src/main/java/it/polimi/ingsw/gamemodel/Board.java | 4 ++-- src/main/java/it/polimi/ingsw/gamemodel/Card.java | 5 +++-- .../java/it/polimi/ingsw/gamemodel/CardFace.java | 10 +++++++--- .../java/it/polimi/ingsw/gamemodel/GoldCard.java | 12 ++++++------ .../java/it/polimi/ingsw/gamemodel/Objective.java | 1 + .../java/it/polimi/ingsw/gamemodel/PlacedCard.java | 7 +++++++ .../java/it/polimi/ingsw/gamemodel/PlayableCard.java | 5 +++++ .../polimi/ingsw/gamemodel/PositionRequirement.java | 6 +----- .../polimi/ingsw/gamemodel/QuantityRequirement.java | 4 ++-- .../java/it/polimi/ingsw/gamemodel/Requirement.java | 8 +++++++- 10 files changed, 41 insertions(+), 21 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 613646a0..8685c7a0 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -1,11 +1,12 @@ package it.polimi.ingsw.gamemodel; -import it.polimi.ingsw.exceptions.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; +import it.polimi.ingsw.exceptions.CardException; +import it.polimi.ingsw.exceptions.HandException; import it.polimi.ingsw.utils.Pair; /** @@ -159,7 +160,6 @@ protected int placeCard(Pair coord, PlayableCard card, Side si throw new CardException("Unknow card type: " + card.getClass().toString() + "!"); } - return points; } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Card.java b/src/main/java/it/polimi/ingsw/gamemodel/Card.java index c7968527..8cbddb11 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Card.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Card.java @@ -1,6 +1,6 @@ package it.polimi.ingsw.gamemodel; -/* +/** * Highest abstraction of the card object, with common aspects for every card in the game (except objectives). */ public abstract class Card { @@ -8,7 +8,8 @@ public abstract class Card { protected CardFace back; /** - * @param side the desired side + * Getter for the required side of the card + * @param side the required side * @return the structure of the specified side * @see CardFace */ diff --git a/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java b/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java index 088e4453..feae8a6e 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java @@ -2,7 +2,9 @@ import java.util.Set; -/* +import it.polimi.ingsw.exceptions.CardException; + +/** * Topological definition of a card's side */ public class CardFace { @@ -24,8 +26,9 @@ public CardFace(Symbol topLeft, Symbol topRight, Symbol bottomLeft, Symbol botto * Used to get the symbol present in one of the four corners of a card * @param corner which of the four corners we want * @return the symbol the specified corner contains + * @throws CardException if the specified corner does not exist */ - public Symbol getCorner(Corner corner) { + public Symbol getCorner(Corner corner) throws CardException { switch (corner) { case TOP_LEFT: return this.topLeft; @@ -36,11 +39,12 @@ public Symbol getCorner(Corner corner) { case BOTTOM_RIGHT: return this.bottomRight; default: - return null; + throw new CardException("The specified corner does not exist!"); } } /** + * Getter for the center of the card * @return the set containing all symbols the center of the card contains */ public Set getCenter() { diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java index 7e26bef9..8df6b464 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -1,13 +1,13 @@ package it.polimi.ingsw.gamemodel; -import it.polimi.ingsw.exceptions.InvalidResourceException; -import it.polimi.ingsw.utils.Pair; - import java.util.EnumSet; import java.util.HashSet; import java.util.Map; import java.util.Set; +import it.polimi.ingsw.exceptions.InvalidResourceException; +import it.polimi.ingsw.utils.Pair; + /** * The front side of these cards always gives points, but needs a certain requirement to be met in order to be played @@ -42,7 +42,7 @@ public GoldCard(CardFace front, Symbol reign, Symbol multiplier, int points, Qua /** * Getter for the GoldCard class - * @return the multiplier. + * @return the multiplier */ public Symbol getMultiplier(){ return this.multiplier; @@ -50,7 +50,7 @@ public Symbol getMultiplier(){ /** * Getter for the GoldCard class - * @return the quantity requirement for the gold card to be played. + * @return the quantity requirement for the gold card to be played */ public QuantityRequirement getRequirement(){ return this.req; @@ -58,7 +58,7 @@ public QuantityRequirement getRequirement(){ /** * Will compute the total points this card gives based on the board it's played on. - * It MUST be called AFTER the placement of the gold card. + * It MUST be called AFTER the placement of the gold card * @param board the board on which we want to compute the points this card will give * @param coord the coordinates of the card just placed (needed fot corner objectives) * @return the points gained from playing the gold card diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Objective.java b/src/main/java/it/polimi/ingsw/gamemodel/Objective.java index 8b024fb1..593ae50b 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Objective.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Objective.java @@ -10,6 +10,7 @@ public class Objective { private Requirement req; /** + * Class constructor. It is composed only of a requirement (using polymorphism) and the points it gives * @param points the number of points the objective will give (which is always an absolute number, it never depends on any resource) * @param req the requirement to satisfy in order to receive the points */ diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java b/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java index 0ada882a..544db1c9 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java @@ -9,7 +9,9 @@ public class PlacedCard { private Side playedSide; /** + * Class constructor, which only needs to initialize the card and the turn it is played, as well as the side it was played on * @param card the {@link Card} played + * @param playedSide the side the card was played on * @param turn the turn said card is played. Needed to know which card covers which, since a card played in a certain turn will * always cover one played before */ @@ -41,6 +43,11 @@ public Side getPlayedSide() { return this.playedSide; } + + /** + * Getter for the Card face + * @return the topological description of the side the card was played on + */ public CardFace getPlayedCardFace() { return this.card.getSide(this.playedSide); } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java b/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java index 03fe13ee..c223061c 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java @@ -29,6 +29,11 @@ public PlayableCard(Symbol reign) throws InvalidResourceException { } } + /** + * Getter for the card reign + * @return the card's reign + * @see Symbol#getReigns() + */ public Symbol getReign() { return this.reign; } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java index 5b63eeb1..cebd09e9 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java @@ -15,9 +15,9 @@ public class PositionRequirement extends Requirement{ private Map, Symbol> reqs; /** - * @param reqs The relative positioning of the cards (of which we only care about the faction). * Note that, since this requirement only cares about relative positioning, there must always be * an element whose key is (0, 0) + * @param reqs The relative positioning of the cards (of which we only care about the faction). */ public PositionRequirement(Map, Symbol> reqs) throws InvalidResourceException { EnumSet validResources = Symbol.getReigns(); @@ -75,7 +75,3 @@ public int timesMet(Board board) { } } - -// (0, 0), FUNGUS -// (1, 1), INSECT -// (2, 2), FUNGUS diff --git a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java index 1dd3daca..28a22ade 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java @@ -3,7 +3,7 @@ import java.util.EnumSet; import java.util.Map; -import it.polimi.ingsw.exceptions.*; +import it.polimi.ingsw.exceptions.InvalidResourceException; /** * This class handles requirements involving relative positioning of cards, e.g. three red cards placed in the top right corner of each other @@ -12,7 +12,7 @@ public class QuantityRequirement extends Requirement{ private Map reqs; /** - * Only valid symbols are the ones returned by {@link Symbol#getBasicResources()} + * Class constructor. Only valid symbols are the ones returned by {@link Symbol#getBasicResources()} * @param reqs how many resources of a certain type are needed to fulfill the requirement. * @throws InvalidResourceException if a requirement is not made up only of those symbols */ diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java index 645b0273..a5c02845 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java @@ -1,11 +1,17 @@ package it.polimi.ingsw.gamemodel; -import it.polimi.ingsw.exceptions.*; +import it.polimi.ingsw.exceptions.InvalidResourceException; /* * A condition must be met in order to play a golden card and to get points from the objectives. Those requirements are both represented by this class */ public abstract class Requirement { + /** + * Empty class constructor. The actual constructor resides in the subclasses, but this method is used to know + * what to expect when creating a new Requirement, allowing the use of polymorphism + * @throws InvalidResourceException if the requirement does not match any of the valid ones + * @see Symbol + */ public Requirement() throws InvalidResourceException { } From 384caec8f7a117eea1892b9e6733ff2d5549de7f Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Tue, 26 Mar 2024 14:13:23 +0100 Subject: [PATCH 51/68] Fix unhandled exception, minor spell issues --- .../java/it/polimi/ingsw/gamemodel/Board.java | 22 +++++++++++-------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java index 8685c7a0..df1a7ddd 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Board.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -10,7 +10,7 @@ import it.polimi.ingsw.utils.Pair; /** -* Board is the class that contains all the informations relative to a {@link Player}'s status +* Board is the class that contains all the information relative to a {@link Player}'s status */ public class Board { private List currentHand; @@ -131,11 +131,15 @@ protected int placeCard(Pair coord, PlayableCard card, Side si Integer y = coord.second(); for (Pair diagOffset : diagonalOffsets.keySet()) { - cornerSymbol = this.getSymbolIfPresent(new Pair<>(x+diagOffset.first(), y+diagOffset.second()), diagonalOffsets.get(diagOffset)); - if (cornerSymbol != null) { - if (Symbol.getBasicResources().contains(cornerSymbol)) { - availableResources.put(cornerSymbol, availableResources.get(cornerSymbol) - 1); + try { + cornerSymbol = this.getSymbolIfPresent(new Pair<>(x+diagOffset.first(), y+diagOffset.second()), diagonalOffsets.get(diagOffset)); + if (cornerSymbol != null) { + if (Symbol.getBasicResources().contains(cornerSymbol)) { + availableResources.put(cornerSymbol, availableResources.get(cornerSymbol) - 1); + } } + } catch (CardException e) { + System.err.println(e.getMessage()); } } @@ -157,13 +161,13 @@ protected int placeCard(Pair coord, PlayableCard card, Side si } else if (card instanceof ResourceCard) { points = ((ResourceCard)card).getPoints(); } else { - throw new CardException("Unknow card type: " + card.getClass().toString() + "!"); + throw new CardException("Unknown card type: " + card.getClass().toString() + "!"); } return points; } - private Symbol getSymbolIfPresent(Pair coord, Corner corner) { + private Symbol getSymbolIfPresent(Pair coord, Corner corner) throws CardException { PlacedCard placedCard = placed.get(coord); if (placedCard == null) { return null; @@ -172,7 +176,7 @@ private Symbol getSymbolIfPresent(Pair coord, Corner corner) { } /** - * Checks wheter the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card), + * Checks whether the positioning is valid: the card has to be in the player's hand (note that this method won't be called on the initial card), * the given coordinates must be valid, and if the card has a requirement it must be met * @param coord the coordinates in which the card should be played * @param card the card to check on @@ -199,7 +203,7 @@ public PlacementOutcome verifyCardPlacement(Pair coord, Card c Pair cmp; - // cross check: none exists + // cross-check: none exists for (Integer offset : offsets) { cmp = new Pair<>(coord.first()+offset, coord.second()); if (placed.keySet().contains(cmp)) { From 67f23aaa532081a30c7a0837655fd81c6bfe9168 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Tue, 26 Mar 2024 21:51:22 +0100 Subject: [PATCH 52/68] Update class diagram --- deliveries/UML/class_diagram.mmd | 111 ++++++++++++++++++++----------- deliveries/UML/model.pdf | Bin 109126 -> 101277 bytes 2 files changed, 72 insertions(+), 39 deletions(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index 48f285b8..ce8066e2 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -1,6 +1,7 @@ --- title: Model diagram --- + classDiagram Match --> GameDeck Match --> MatchState @@ -37,11 +38,10 @@ classDiagram %%Side <-- Player %%Side <-- Match Corner <-- CardFace - %%Symbol <-- GoldCard + %% Symbol <-- GoldCard %% DrawSource <-- Player - class Symbol { <> ANIMAL @@ -54,6 +54,11 @@ classDiagram EMPTY_CORNER FULL_CORNER CORNER_OBJ + + + getReigns() EnumSet~Symbol~ <> + + getBasicResources() EnumSet~Symbol~ <> + + getValidCorner() EnumSet~Symbol~ <> + + getValidMultiplier( EnumSet~Symbol~ <>) } class Corner { @@ -71,6 +76,7 @@ classDiagram - bottomLeft: Symbol - bottomRight: Symbol - center: Set~Symbol~ + + CardFace(Symbol topLeft, Symbol topRight, Symbol bottomLeft, Symbol bottomRight, Set~Symbol~ center) + getCorner(Corner corner) Symbol + getCenter() Set~Symbol~ @@ -78,7 +84,10 @@ classDiagram class Card { <> - # sides : Map~Side, CardFace~ + %% # sides : Map~Side, CardFace~ + # front : CardFace + # back : CardFace + + getSide(Side side) CardFace } @@ -86,6 +95,7 @@ classDiagram <> # reign: Symbol # points: int + + PlayableCard(Symbol reign) + getReign() Symbol } @@ -102,6 +112,7 @@ classDiagram class GoldCard { - multiplier: Symbol - req: QuantityRequirement + + GoldCard(CardFace front, Symbol reign, int points, Symbol multiplier, QuantityRequirement req) + getMultiplier() Symbol + getRequirement() QuantityRequirement @@ -110,60 +121,70 @@ classDiagram class Requirement { <> - - Requirement() + + Requirement() + timesMet(Board board) int } class QuantityRequirement { - reqs : Map~Symbol, Integer~ - + QuantityRequirement(Symbol simbol, Integer quantity) + + + QuantityRequirement(Map~Symbol, Integer~ reqs) + timesMet(Board board) int } class PositionRequirement { - reqs: Map<Pair<Integer, Integer>, Color> - + PositionRequirement(Map<Pair<Integer, Integer>, Color>) + + + PositionRequirement(Map<Pair<Integer, Integer>, Color> reqs) + timesMet(Board board) int } + class Objective { - points: int - req: Requirement + + Objective(int points, Requirement req) + getPoints() int + getRequirement() Requirement } + class Player { - nickname: String - match: Match - points: Int - board: Board - - color: Color - - objective: Objective + - pawnColor: Color + - secretObjective: Objective + Player(String nickname, Match match) + playCard(Pair coord, PlayableCard card, Side side) void - + drawCard(DrawSource draw) void - + chooseObjective(Objective objective) void - + chooseInitialSide(Side side) void + + drawSecretObjective Pair~Objective, Objective~ + + drawInitialCard() InitialCard + + drawCard(DrawSource source) void + + chooseSecretObjective(Objective objective) void + + chooseInitialCardSide(Side side) void + getBoard() Board + getPoints() int + getSecretObjective() Objective - + getColor() Color + + getPawnColor() Color + getNickname() String # setColor() void } - class Color{ + + class Color { <> RED BLUE GREEN YELLOW } - class Side{ + + class Side { <> FRONT BACK } + class Board { - currentHand: List~PlayableCard~ - placed: Map<Pair<Integer,Integer>, PlacedCard> @@ -173,61 +194,67 @@ classDiagram + getAvailableResources() Map~Symbol, Integer~ + getPlacedCards() Map<Pair<Integer, Integer>, PlacedCard> + getCurrentHand() List~PlayableCard~ + + verifyCardPlacement(Pair~Integer, Integer~ coords, Card card, Side side) PlacementOutcome # removeHandCard(PlayableCard card) void # addHandCard(PlayableCard card) void # setInitialCard(InitialCard card, Side side) void # placeCard(Pair~Integer, Integer~ coord, PlayableCard card, Side side, int turn) int - + verifyCardPlacement(Pair~Integer, Integer~ coords, Card card, Side side) PlacementOutcome } + class PlacedCard { - <> - card: Card - turn: int - + PlacedCard(Card card, int turn) + - playedSide: Side + + + PlacedCard(Card card, Side playedSide, int turn) + getCard() Card + + getPlayedSide() Side + getTurn() int + + getPlayedCardFace() CardFace } class Match { - - players: list~Players~ + - players: List~Players~ - maxPlayers: int - - currState: MatchState - currentPlayer: Player + - currState: MatchState - initialsDeck: GameDeck~InitialCard~ - resourcesDeck: GameDeck~ResourceCard~ - goldsDeck: GameDeck~GoldCard~ - objectivesDeck: GameDeck~Objective~ - - visibleGolds: Pair~GoldCard, GoldCard~ - - visibleResources: Pair~ResourceCard, ResourceCard~ + - visiblePlayableCards: Map~DrawSource, PlayableCard~ - visibleObjectives: Pair~Objective, Objective~ - currentProposedObjectives: Pair~Objective, Objective~ - currentGivenInitialCard: InitialCard - - started: bool - - initialTurn: bool - - lastTurn: bool - - finished: bool - - + Match(int maxPlayers, resourceDeck ) void - + isFull() bool - + isStarted() bool - + isInitialTurnFinished() bool - + isFinished() bool + - started: boolean + - initialTurn: boolean + - lastTurn: boolean + - finished: boolean + + + Match(int maxPlayers, GameDeck~InitialCard~ initialsDeck, GameDeck~ResourceCard~ resourcesDeck, GameDeck~GoldCard~ goldsDeck, GameDeck~Objective~ objectivesDeck) void + + isFull() boolean + + isStarted() boolean + + isInitialTurnFinished() boolean + + isFinished() boolean + addPlayer(Player player) void + removePlayer(Player player) void + getCurrentPlayer() Player + getPlayers() List~Player~ + getMaxPlayers() int + + getVisiblePlayableCards Map~DrawSource, PlayableCard~ + + getVisibleObjectives() Pair~Objective, Objective~ # getPoints() int # addPoints() void # setSecretObjective(Objective obj) void # proposeSecretObjectives() Pair~Objective, Objective~ # makeMove(Pair~Integer, Integer~ coords, PlayableCard card, Side side) void # drawCard(DrawSource draw) PlayableCard + # drawInitialCard() InitialCard # setInitialSide(Side side) void # doStart() void # doInitialTurnFinish() void # doFinish() void - # setState() void + # setState(MatchState state) void # setupDecks() void # setupPlayers() void # setupBoards() void @@ -246,7 +273,8 @@ classDiagram class MatchState { <> - + match: Match + - match: Match + + MatchState(Match match) + transition() void + addPlayer() void @@ -257,7 +285,7 @@ classDiagram + drawInitialCard() void + makeMove() void + drawCard() void - + # getMatch() Match } class WaitState{ @@ -266,6 +294,7 @@ classDiagram + addPlayer() void + removePlayer() void } + class NextTurnState { + NextTurnState(Match match) void + proposeSecretObjectives() void @@ -273,11 +302,13 @@ classDiagram + drawInitialCard() void + transition() void } + class ChooseInitialSideState{ + ChooseInitialSideState(Match match) void + chooseInitialSide(Side side) void + transition() void } + class ChooseSecretObjectiveState { + ChooseSecretObjectiveState() void + chooseSecretObjective() void @@ -305,15 +336,17 @@ classDiagram VALID, INVALID_COORDS, INVALID_ENOUGH_RESOURCES, - } + } + class GameDeck { <> - - cardsList: List~C~ + - cardsList: List~T~ + + GameDeck() - + add(C card) void + + add(T card) void + getSize() int - + pop() C - + poll() C + + pop() T + + poll() T + shuffle() void + isEmpty() void } diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index 9e1ad5b1d5fc0afb45860f03d45c4f3b097b40c6..ba0654690a3b5a337f86ecdc07511efbb523902f 100644 GIT binary patch literal 101277 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 z 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 From b0975d5f7a50977f071a36a20e8fb2ba40b04353 Mon Sep 17 00:00:00 2001 From: FrancescoCaracciolo Date: Tue, 9 Apr 2024 19:58:55 +0200 Subject: [PATCH 53/68] Add some tests for Match --- .../it/polimi/ingsw/gamemodel/MatchTest.java | 260 ++++++++++++++---- 1 file changed, 209 insertions(+), 51 deletions(-) diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java index 4087c68a..8eac09c4 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -1,49 +1,62 @@ package it.polimi.ingsw.gamemodel; +import java.util.Collections; import java.util.EnumSet; +import java.util.HashMap; import java.util.Map; import java.util.Set; +import javax.management.RuntimeErrorException; + import it.polimi.ingsw.utils.Pair; import org.junit.Test; +import it.polimi.ingsw.exceptions.InvalidResourceException; import it.polimi.ingsw.exceptions.WrongStateException; import static org.junit.Assert.*; public class MatchTest { + private Match match; + private Player player1, player2, player3, player4; + GameDeck initialsDeck; + GameDeck resourcesDeck; + GameDeck goldsDeck; + GameDeck objectivesDeck; + @Test public void shouldAnswerWithTrue() { - GameDeck initialsDeck = createInitialsDeck(); - GameDeck resourcesDeck = createResourcesDeck(); - GameDeck goldsDeck = createGoldsDeck(); - GameDeck objectivesDeck = createObjectivesDeck(); - - Match match = new Match(2, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); - Player p1 = new Player("Oingo", match); - Player p2 = new Player("Boingo", match); - Player p3 = new Player("Foingo", match); - + GameDeck initialsDeck = createInitialsDeck(4); + GameDeck resourcesDeck = createResourcesDeck(40); + GameDeck goldsDeck = createGoldsDeck(40); + GameDeck objectivesDeck = createObjectivesDeck(16); + + match = new Match(2, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + player1 = new Player("Oingo", match); + player2 = new Player("Boingo", match); + player3 = new Player("Foingo", match); + try { - match.addPlayer(p1); - match.addPlayer(p2); - } catch (WrongStateException e) { + match.addPlayer(player1); + match.addPlayer(player2); + } catch (Exception e) { fail("Players not added correctly"); } assertTrue("Match should have been full", match.isFull()); assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof NextTurnState); try { - match.addPlayer(p3); + match.addPlayer(player3); fail("Should have thrown an exception"); - } catch (WrongStateException e) { + } catch (Exception e) { System.out.println(e.getMessage()); } for (int i = 0; i < match.getMaxPlayers(); i++) { try { System.out.println("Player: " + match.getCurrentPlayer().getNickname() + "Chooses the initial card"); InitialCard initial = match.getCurrentPlayer().drawInitialCard(); - assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof ChooseInitialSideState); + assertTrue("Wrong state: " + match.getCurrentState(), + match.getCurrentState() instanceof ChooseInitialSideState); match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); } catch (Exception e) { fail(e.getMessage()); @@ -55,43 +68,174 @@ public void shouldAnswerWithTrue() { try { System.out.println("Player: " + match.getCurrentPlayer().getNickname() + "Chooses their objective"); Pair objectives = match.getCurrentPlayer().drawSecretObjectives(); - assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof ChooseSecretObjectiveState); + assertTrue("Wrong state: " + match.getCurrentState(), + match.getCurrentState() instanceof ChooseSecretObjectiveState); match.getCurrentPlayer().chooseSecretObjective(objectives.first()); } catch (Exception e) { fail("Player " + i + e.getMessage()); } } assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof NextTurnState); - assertTrue( true ); + assertTrue(true); + } + + @Test + public void constructor() { + player1 = new Player("Oingo", match); + player2 = new Player("Boingo", match); + player3 = new Player("Foingo", match); + player4 = new Player("Francesco Edoardo Caracciolo", match); + + // Verify that match throws an exception when the initial cards deck has less + // cards than the number of players (3 cards in this case, minus than 4 + // players in the match) + initializeAllEqualDecks(); + initialsDeck = createInitialsDeck(3); + + try { + match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + fail("Match constructor: Exception about initials deck not thrown"); + } catch (IllegalArgumentException e) { + assertTrue("", true); + } + + // Verify that match throws an exception when the gold cards deck has less + // than 9 cards + initializeAllEqualDecks(); + goldsDeck = createGoldsDeck(5); + + try { + match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + fail("Match constructor: Exception about golds deck not thrown"); + } catch (IllegalArgumentException e) { + assertTrue("", true); + } + + // Verify that match throws an exception when the initial cards deck has less + // than 10 cards + initializeAllEqualDecks(); + resourcesDeck = createResourcesDeck(9); + + try { + match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + fail("Match constructor: Exception about resources deck not thrown"); + } catch (IllegalArgumentException e) { + assertTrue("", true); + } + + // Verify that match throws an exception when the number of players is more or + // less than expected + initializeAllEqualDecks(); + objectivesDeck = createObjectivesDeck(5); + + try { + match = new Match(1, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + fail("Match constructor: Exception about players not thrown"); + } catch (IllegalArgumentException e) { + assertTrue("", true); + } + } + + private void initializeAllEqualDecks() { + // Initialize Initial Cards deck + initialsDeck = createDeterministicInitialsDeck(4); + objectivesDeck = createDeterministicObjectivesDeck(10); + goldsDeck = createGoldsDeck(40); + resourcesDeck = createResourcesDeck(40); + } + + private static GameDeck createDeterministicObjectivesDeck(int size) { + // Initialize Objective deck + GameDeck objectivesDeck = new GameDeck<>(); + Objective objective = null; + HashMap resources = new HashMap<>(); + resources.put(Symbol.FUNGUS, 2); + try { + Requirement req = new QuantityRequirement(resources); + } catch (Exception e) { + throw new RuntimeException(); + } + for (int i = 0; i < size; i++) { + objective = new Objective(2, null); + objectivesDeck.add(objective); + } + return objectivesDeck; + } + + private static GameDeck createDeterministicInitialsDeck(int size) { + // Initialize Initial Cards deck + GameDeck initialsDeck = new GameDeck(); + InitialCard card = null; + for (int i = 0; i < size; i++) { + card = new InitialCard( + new CardFace(Symbol.FUNGUS, Symbol.ANIMAL, Symbol.PLANT, Symbol.INSECT, Collections.emptySet()), + new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, + Collections.emptySet())); + initialsDeck.add(card); + } + return initialsDeck; + } + + private static GameDeck createDeterministicResourcesDeck(int size) { + GameDeck resourcesDeck = new GameDeck<>(); + ResourceCard card = null; + for (int i = 0; i < size; i++) { + try { + card = new ResourceCard( + new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, + Collections.emptySet()), + Symbol.FUNGUS, 0); + } catch (Exception e) { + throw new RuntimeException(); + } + } + return resourcesDeck; + } + + private static GameDeck createDeterministicGoldsDeck(int size) { + GameDeck goldsDeck = new GameDeck<>(); + GoldCard card = null; + HashMap resources = new HashMap<>(); + resources.put(Symbol.FUNGUS, 1); + for (int i = 0; i < size; i++) { + try { + card = new GoldCard(new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, + Symbol.EMPTY_CORNER, Collections.emptySet()), Symbol.FUNGUS, Symbol.FEATHER, 3, + new QuantityRequirement(resources)); + } catch (Exception e) { + throw new RuntimeErrorException(null); + } + } + return goldsDeck; } - - private GameDeck createObjectivesDeck() { - GameDeck objectivesDeck = new GameDeck(8); - for (int i = 0; i < 8; i++) { + + private GameDeck createObjectivesDeck(int size) { + GameDeck objectivesDeck = new GameDeck(); + for (int i = 0; i < size; i++) { objectivesDeck.add(generateRandomObjective()); } return objectivesDeck; } - private GameDeck createResourcesDeck() { - GameDeck resourcesDeck = new GameDeck(40); - for (int i = 0; i < 40; i++) { + private GameDeck createResourcesDeck(int size) { + GameDeck resourcesDeck = new GameDeck(); + for (int i = 0; i < size; i++) { resourcesDeck.add(generateRandomResourceCard()); } return resourcesDeck; } - private GameDeck createGoldsDeck() { - GameDeck goldsDeck = new GameDeck(40); - for (int i = 0; i < 40; i++) { + private GameDeck createGoldsDeck(int size) { + GameDeck goldsDeck = new GameDeck(); + for (int i = 0; i < size; i++) { goldsDeck.add(generateRandomGoldCard()); } return goldsDeck; } - private GameDeck createInitialsDeck() { - GameDeck initialsDeck = new GameDeck(4); - for (int i = 0; i < 4; i++) { + private GameDeck createInitialsDeck(int size) { + GameDeck initialsDeck = new GameDeck(); + for (int i = 0; i < size; i++) { initialsDeck.add(generateRandomInitialCard()); } return initialsDeck; @@ -100,11 +244,15 @@ private GameDeck createInitialsDeck() { private ResourceCard generateRandomResourceCard() { EnumSet reigns = Symbol.getReigns(); EnumSet corners = Symbol.getValidCorner(); - return new ResourceCard( - new CardFace(randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), Set.of(randomSymbol(reigns))), - new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Set.of(randomSymbol(reigns))), - (int) Math.random() * 2 - ); + try { + return new ResourceCard( + new CardFace(randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), + randomSymbol(corners), Collections.emptySet()), + randomSymbol(reigns), + (int) Math.random() * 2); + } catch (InvalidResourceException e) { + throw new RuntimeException(); + } } private GoldCard generateRandomGoldCard() { @@ -112,29 +260,40 @@ private GoldCard generateRandomGoldCard() { EnumSet corners = Symbol.getValidCorner(); EnumSet multipliers = Symbol.getValidMultiplier(); EnumSet resources = Symbol.getBasicResources(); - return new GoldCard( - new CardFace(randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), Set.of(randomSymbol(reigns))), - new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Set.of(randomSymbol(reigns))), - randomSymbol(multipliers), - (int) Math.random() * 2, - new QuantityRequirement(Map.of(randomSymbol(resources), 1)) - ); - } - + try { + return new GoldCard( + new CardFace(randomSymbol(corners), randomSymbol(corners), randomSymbol(corners), + randomSymbol(corners), + Collections.emptySet()), + randomSymbol(reigns), + randomSymbol(multipliers), + (int) Math.random() * 2, + new QuantityRequirement(Map.of(randomSymbol(resources), 1))); + } catch (InvalidResourceException e) { + throw new RuntimeException(); + } + } + private InitialCard generateRandomInitialCard() { - EnumSet reigns = Symbol.getReigns(); + EnumSet reigns = Symbol.getReigns(); // Generate a random number between 0 and 3 int index = (int) (Math.random() * 3); return new InitialCard( - new CardFace(randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), Set.of(randomSymbol(reigns))), - new CardFace(randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), Set.of(randomSymbol(reigns))) - ); + new CardFace(randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), + Set.of(randomSymbol(reigns))), + new CardFace(randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), randomSymbol(reigns), + Set.of(randomSymbol(reigns)))); } private Objective generateRandomObjective() { EnumSet resources = Symbol.getBasicResources(); - return new Objective((int) (Math.random() * 2), new QuantityRequirement(Map.of(randomSymbol(resources), 3))); + try { + return new Objective((int) (Math.random() * 2), + new QuantityRequirement(Map.of(randomSymbol(resources), 3))); + } catch (InvalidResourceException e) { + throw new RuntimeErrorException(null); + } } private Symbol randomSymbol(EnumSet validSymbols) { @@ -151,4 +310,3 @@ private Symbol randomSymbol(EnumSet validSymbols) { return null; } } - From 26e67c38dc22029e48eee9a3182c2769bbbd4265 Mon Sep 17 00:00:00 2001 From: Davide Greco Date: Tue, 9 Apr 2024 22:14:35 +0200 Subject: [PATCH 54/68] Add option for gold card without multiplier --- src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java | 3 +++ src/main/java/it/polimi/ingsw/gamemodel/Symbol.java | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java index 8df6b464..f1a2c4a7 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -64,6 +64,9 @@ public QuantityRequirement getRequirement(){ * @return the points gained from playing the gold card */ public int calculatePoints(Board board, Pair coord) { + if (this.multiplier == Symbol.NO_MULT) { + return this.points; + } Map availableResources = board.getAvailableResources(); int totalElements = 0; diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java b/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java index 9fd936bb..495d6e5a 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java @@ -17,8 +17,9 @@ public enum Symbol { PARCHMENT, EMPTY_CORNER, FULL_CORNER, + NO_MULT, CORNER_OBJ; - + /** * Generates subset containing only the four basic reigns * @return the subset containing only the four basic reigns @@ -79,6 +80,7 @@ static public EnumSet getValidMultiplier() { FEATHER, INKWELL, PARCHMENT, + NO_MULT, CORNER_OBJ ); } From 1bee3c2c224ca519afd24fa15b49b513547bac95 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Wed, 10 Apr 2024 20:56:38 +0200 Subject: [PATCH 55/68] Add unit tests to MatchTest Methods added to test: - nextPlayer - removePlayer - setInitialSide - drawInitialCard - proposeSecretObjective - setSecretObjective --- .../it/polimi/ingsw/gamemodel/MatchTest.java | 449 +++++++++++++++--- 1 file changed, 376 insertions(+), 73 deletions(-) diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java index 8eac09c4..98261668 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -1,5 +1,6 @@ package it.polimi.ingsw.gamemodel; +import java.security.spec.ECField; import java.util.Collections; import java.util.EnumSet; import java.util.HashMap; @@ -8,6 +9,8 @@ import javax.management.RuntimeErrorException; +import it.polimi.ingsw.exceptions.CardException; +import it.polimi.ingsw.exceptions.WrongChoiceException; import it.polimi.ingsw.utils.Pair; import org.junit.Test; @@ -20,64 +23,10 @@ public class MatchTest { private Match match; private Player player1, player2, player3, player4; - GameDeck initialsDeck; - GameDeck resourcesDeck; - GameDeck goldsDeck; - GameDeck objectivesDeck; - - @Test - public void shouldAnswerWithTrue() { - GameDeck initialsDeck = createInitialsDeck(4); - GameDeck resourcesDeck = createResourcesDeck(40); - GameDeck goldsDeck = createGoldsDeck(40); - GameDeck objectivesDeck = createObjectivesDeck(16); - - match = new Match(2, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); - player1 = new Player("Oingo", match); - player2 = new Player("Boingo", match); - player3 = new Player("Foingo", match); - - try { - match.addPlayer(player1); - match.addPlayer(player2); - } catch (Exception e) { - fail("Players not added correctly"); - } - assertTrue("Match should have been full", match.isFull()); - assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof NextTurnState); - try { - match.addPlayer(player3); - fail("Should have thrown an exception"); - } catch (Exception e) { - System.out.println(e.getMessage()); - } - for (int i = 0; i < match.getMaxPlayers(); i++) { - try { - System.out.println("Player: " + match.getCurrentPlayer().getNickname() + "Chooses the initial card"); - InitialCard initial = match.getCurrentPlayer().drawInitialCard(); - assertTrue("Wrong state: " + match.getCurrentState(), - match.getCurrentState() instanceof ChooseInitialSideState); - match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); - } catch (Exception e) { - fail(e.getMessage()); - } - } - assertFalse("Match should not have started", match.isStarted()); - - for (int i = 0; i < match.getMaxPlayers(); i++) { - try { - System.out.println("Player: " + match.getCurrentPlayer().getNickname() + "Chooses their objective"); - Pair objectives = match.getCurrentPlayer().drawSecretObjectives(); - assertTrue("Wrong state: " + match.getCurrentState(), - match.getCurrentState() instanceof ChooseSecretObjectiveState); - match.getCurrentPlayer().chooseSecretObjective(objectives.first()); - } catch (Exception e) { - fail("Player " + i + e.getMessage()); - } - } - assertTrue("Wrong state: " + match.getCurrentState(), match.getCurrentState() instanceof NextTurnState); - assertTrue(true); - } + private GameDeck initialsDeck; + private GameDeck resourcesDeck; + private GameDeck goldsDeck; + private GameDeck objectivesDeck; @Test public void constructor() { @@ -86,7 +35,7 @@ public void constructor() { player3 = new Player("Foingo", match); player4 = new Player("Francesco Edoardo Caracciolo", match); - // Verify that match throws an exception when the initial cards deck has less + // Verify that Match throws an exception when the initial cards deck has less // cards than the number of players (3 cards in this case, minus than 4 // players in the match) initializeAllEqualDecks(); @@ -94,48 +43,363 @@ public void constructor() { try { match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + // An exception is supposed to be thrown here fail("Match constructor: Exception about initials deck not thrown"); - } catch (IllegalArgumentException e) { - assertTrue("", true); - } + } catch (IllegalArgumentException e) {} - // Verify that match throws an exception when the gold cards deck has less + // Verify that Match throws an exception when the gold cards deck has less // than 9 cards initializeAllEqualDecks(); goldsDeck = createGoldsDeck(5); try { match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + // An exception is supposed to be thrown here fail("Match constructor: Exception about golds deck not thrown"); - } catch (IllegalArgumentException e) { - assertTrue("", true); - } + } catch (IllegalArgumentException e) {} - // Verify that match throws an exception when the initial cards deck has less + // Verify that Match throws an exception when the initial cards deck has less // than 10 cards initializeAllEqualDecks(); resourcesDeck = createResourcesDeck(9); try { match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + // An exception is supposed to be thrown here fail("Match constructor: Exception about resources deck not thrown"); - } catch (IllegalArgumentException e) { - assertTrue("", true); - } + } catch (IllegalArgumentException e) {} - // Verify that match throws an exception when the number of players is more or + // Verify that Match throws an exception when the number of players is more or // less than expected initializeAllEqualDecks(); objectivesDeck = createObjectivesDeck(5); try { match = new Match(1, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + // An exception is supposed to be thrown here fail("Match constructor: Exception about players not thrown"); - } catch (IllegalArgumentException e) { - assertTrue("", true); + } catch (IllegalArgumentException e) {} + } + + @Test + public void addPlayer() { + initializeAllEqualDecks(); + + player1 = new Player("Oingo", match); + player2 = new Player("Boingo", match); + player3 = new Player("Miozio", match); + + match = new Match(2, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + + // Verify that Match throws an exception when a plyer is added while the match + // is in a state which is not valid (i.e. not equal to WaitState) + try { + match.setState(new FinalState(match)); + match.addPlayer(player1); + // An exception is supposed to be thrown here + fail("Exception about wrong state not thrown "); + } + catch (WrongStateException e) {} + catch (Exception e) { + throw new RuntimeException(e); + } + + match = new Match(2, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + + // Verify that all players are correctly added + try { + match.addPlayer(player1); + match.addPlayer(player2); + } catch (Exception e) { + throw new RuntimeException(e); + } + + assertTrue("1st player not added", match.getPlayers().contains(player1)); + assertTrue("2nd player not added", match.getPlayers().contains(player2)); + + // Verify that Match throws an exception when a player already in the match + // is added again + try { + match.addPlayer(player1); + // An exception is supposed to be thrown here + fail("Exception about duplicated player not thrown"); + } + catch (IllegalArgumentException e) {} + catch (Exception e) { + throw new RuntimeException(e); + } + + // Verify that Match throws an exception when a player is added even if the + // math is full + try { + match.addPlayer(player3); + // An exception is supposed to be thrown here + fail("Exception about too many players not thrown"); + } + catch (WrongStateException e) {} + catch (Exception e) { + throw new RuntimeException(e); } } + @Test + public void nextPlayer() { + initializeAllEqualDecks(); + + player1 = new Player("Oingo", match); + player2 = new Player("Boingo", match); + player3 = new Player("Miozio", match); + + match = new Match(3, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + + try { + match.addPlayer(player1); + match.addPlayer(player2); + // a third player is not added, otherwise Match would be full and go to NextTurnState, then call nextTurn + // automatically + } catch (WrongStateException e) { + throw new RuntimeException(e); + } + + // Verify that match sets the current player as the first in the players List + match.nextPlayer(); + assertEquals("Current player not equal to 1st player", match.getCurrentPlayer(), match.getPlayers().get(0)); + + // Verify that match switches to the next player in the List, the 2nd + match.nextPlayer(); + assertEquals("Current player not equal to 2st player", match.getCurrentPlayer(), match.getPlayers().get(1)); + + // Verify that match switches to the next player in the list, back to the 1st + match.nextPlayer(); + assertEquals("Current player not equal to 1st player again", match.getCurrentPlayer(), match.getPlayers().get(0)); + } + + @Test + public void removePlayer() { + initializeAllEqualDecks(); + match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + player1 = new Player("Oingo", match); + player2 = new Player("Boingo", match); + player3 = new Player("Jotaro", match); + player4 = new Player("Polnareff", match); + + // Test in waitstate + try { + match.addPlayer(player1); + match.addPlayer(player2); + match.removePlayer(player1); + assertFalse("Player not removed", match.getPlayers().contains(player1)); + match.addPlayer(player1); + match.addPlayer(player3); + match.addPlayer(player4); + } catch (Exception e) { + fail("No exception must be thrown, exception thrown:" + e.getMessage()); + } + assertTrue("Match not in correct state", match.getCurrentState() instanceof NextTurnState); + // Test outside of waitstate + try { + match.removePlayer(player1); + } catch (Exception e) { + fail("No exception must be thrown, exception thrown:" + e.getMessage()); + } + assertFalse("Player was not removed", match.getPlayers().contains(player1)); + // TODO end match + //assertTrue("Match has not ended after player quit in game", match.isFinished()); + } + + @Test + public void drawInitialCard() { + initializeBlankMatch(2); + + InitialCard card = null; + + try { + card = match.drawInitialCard(); + } catch (WrongStateException e) { + throw new RuntimeException(e); + } + + // Verify that Match draws the inital card + assertNotNull(card); + + // Verify that Match has gone to the next state + assertTrue("State not changed after drawInitialCard", match.getCurrentState() instanceof ChooseInitialSideState); + + // Verify that Match throws an exception when the method is called while being in the wrong state + match.setState(new FinalState(match)); + + try { + match.drawInitialCard(); + // An exception is supposed to be thrown here + fail("drawInitialCard called in wrong state and has not given an exception"); + } catch (WrongStateException e) {} + } + + @Test + public void setInitialSide() { + // Setup a basic game + initializeBlankMatch(2); + + // For each player in the match + for (int i = 0; i < 2; i++) { + Player current = match.getCurrentPlayer(); + Side side = Side.values()[i]; + InitialCard card = null; + + try { + card = current.drawInitialCard(); + } catch (Exception e) { + throw new RuntimeException(e); + } + + // Verify that Match doesn't throw a WrongStateException if the current state is in the right state + // i.e. ChooseInitialSideState + try { + match.setInitialSide(side); + } catch (WrongStateException e) { + fail("Exception thrown even if in ChooseInitialSideState" + e.getMessage()); + } + + PlacedCard playedCard = current.getBoard().getPlacedCards().get(new Pair<>(0,0)); + Side playedSide = playedCard.getPlayedSide(); + + assertEquals("Side not applied correctly", playedSide, side); + assertEquals("Wrong card played", card, playedCard.getCard()); + assertTrue("Wrong state after setInitialSide", match.getCurrentState() instanceof NextTurnState); + } + + } + + @Test + public void proposeSecretObjectives() { + // Initialize a match + initializeBlankMatch(2); + + // Setup Match so that, in the end, should be in NextTurnState and ready to go to ChooseSecretObjectiveState + try { + for (int i = 0; i < 2; i++) { + match.getCurrentPlayer().drawInitialCard(); + match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + + // Start actual testing + // For each player in the match + for (int i = 0; i < 2; i++) { + try { + // Should trigger match state transition to ChooseSecretObjectiveState + Pair obj = match.proposeSecretObjectives(); + + assertNotNull(obj); + assertNotNull(obj.first()); + assertNotNull(obj.second()); + assertTrue("State change has not happened", match.getCurrentState() instanceof ChooseSecretObjectiveState); + + // Trigger match state transition to NextTurnState + match.setSecretObjective(obj.first()); + } catch (Exception e) { + fail("Exception should not have been thrown " + e.getMessage()); + } + } + + // Match should now be in AfterMoveState + try { + match.proposeSecretObjectives(); + // An exception is supposed to be thrown here + fail("Exception not thrown when called in the wrong state"); + } catch (WrongStateException e) {} + } + + @Test + public void setSecretObjective() { + initializeBlankMatch(2); + try { + for (int i = 0; i < 2; i++) { + match.getCurrentPlayer().drawInitialCard(); + match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + // Start actual testing + // Call in wrong state + try { + match.setSecretObjective(generateRandomObjective()); + fail("setSecretObjective has not thrown a wrong state exception"); + } catch (WrongStateException e) { + assertTrue("Exception thrown correctly", true); + } catch (Exception e) { + throw new RuntimeException(e); + } + + for (int i = 0; i < 2; i++) { + Pair obj = null; + try { + obj = match.proposeSecretObjectives(); + } catch (Exception e) { + throw new RuntimeException(e); + } + // Test setting the objective to a non given one + try { + match.setSecretObjective(generateRandomObjective()); + fail("Should have thrown a WrongChoiceException"); + } catch (WrongChoiceException e) { + // Good + } catch (Exception e) { + throw new RuntimeException(e); + } + // Test the correct behavoir + try { + int size = objectivesDeck.getSize(); + match.setSecretObjective(obj.first()); + assertEquals("The card has not been put back in the deck",objectivesDeck.getSize(), size+1); + } catch (Exception e) { + fail("An exception should not have been thrown" + e.getMessage()); + } + } + } + + @Test + public void makeMove() { + initializeBlankStartedMatch(2); + + // Try placing a card in the wrong coordinates + try { + match.makeMove(new Pair<>(1, 4), match.getCurrentPlayer().getBoard().getCurrentHand().get(0), Side.FRONT); + fail("Should have thrown a WrongChoiceException"); + } catch (WrongChoiceException e) { + // Good + } catch (Exception e) { + throw new RuntimeException(e); + } + + // Try placing that is not in the player hand + try { + match.makeMove(new Pair<>(1, 1), generateRandomResourceCard(), Side.FRONT); + fail("Should have thrown CardException"); + } catch (CardException e) { + // Good + } catch (Exception e) { + throw new RuntimeException(e); + } + + // Try placing the card in the right position + PlayableCard card = match.getCurrentPlayer().getBoard().getCurrentHand().get(0); + try { + match.makeMove(new Pair<>(1, 1), card, Side.BACK); + } catch (Exception e) { + fail("Exception thrown when no exception expected"); + } + + assertEquals("Card not placed", card, match.getCurrentPlayer().getBoard().getPlacedCards().get(new Pair<>(1,1)).getCard()); + assertEquals("Card not removed from hand", 2, match.getCurrentPlayer().getBoard().getCurrentHand().size()); + + assertTrue("State not changed after makeMove", match.getCurrentState() instanceof AfterMoveState); + + } + private void initializeAllEqualDecks() { // Initialize Initial Cards deck initialsDeck = createDeterministicInitialsDeck(4); @@ -309,4 +573,43 @@ private Symbol randomSymbol(EnumSet validSymbols) { } return null; } + + private void initializeBlankMatch(int maxPlayers) { + // Setup a basic game + String names[] = {"Oingo", "Boingo", "Jotaro", "Polnareff"}; + Player players[] = new Player[4]; + + initializeAllEqualDecks(); + + match = new Match(maxPlayers, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + + for (int i = 0; i < maxPlayers; i++) { + players[i] = new Player(names[i], match); + + try { + match.addPlayer(players[i]); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + private void initializeBlankStartedMatch(int maxPlayers) { + // Setup a basic game + initializeBlankMatch(maxPlayers); + + try { + for (int i = 0; i < maxPlayers; i++) { + match.getCurrentPlayer().drawInitialCard(); + match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); + } + + for (int i = 0; i < maxPlayers; i++) { + Pair obj = match.getCurrentPlayer().drawSecretObjectives(); + match.getCurrentPlayer().chooseSecretObjective(obj.first()); + } + } catch (Exception e) { + throw new RuntimeException(e); + } + } } From 6151883fdb4917f7305197db887caeb18789de32 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Wed, 10 Apr 2024 22:05:27 +0200 Subject: [PATCH 56/68] Change MatchState methods - Remove Exception throwing from transition() prototype - removePlayer() now trigger match transition to FinalState --- .../java/it/polimi/ingsw/gamemodel/MatchState.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java index 15da12de..71b142d8 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java @@ -1,7 +1,6 @@ package it.polimi.ingsw.gamemodel; import it.polimi.ingsw.exceptions.WrongStateException; -// TODO: Choose if to remove all methods except transition public abstract class MatchState { Match match; @@ -9,14 +8,16 @@ public MatchState (Match match) { this.match = match; } - public abstract void transition() throws Exception; + public abstract void transition(); - public void addPlayer() throws Exception { + public void addPlayer() throws WrongStateException { throw new WrongStateException("addPlayer not allowed from the current match state!"); } - public void removePlayer() throws WrongStateException{ - throw new WrongStateException("removePlayer not allowed from the current match state!"); + // TODO: Add PlayerAbortedState + public void removePlayer() { + // throw new WrongStateException("removePlayer not allowed from the current match state!"); + match.setState(new FinalState(match)); } public void drawInitialCard() throws WrongStateException{ From c08a53cfd5e766a4b3bc4a5a520292447982c1e3 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Wed, 10 Apr 2024 22:11:54 +0200 Subject: [PATCH 57/68] Minor changes on Match class - Remove Exception throwing from all methods - Change few javadocs --- .../java/it/polimi/ingsw/gamemodel/Match.java | 85 +++++++++++++------ 1 file changed, 61 insertions(+), 24 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index d07a7d40..5c3e08cc 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -83,15 +83,16 @@ else if (maxPlayers < 2 || maxPlayers > 4) * Adds a new player to the match, assuming it's not null. * Note: Called by the Controller when a player joins the match. * @param player player to be added to the match - * @throws IllegalArgumentException if the player is already in the match + * @throws IllegalArgumentException if the player is already in the match or too many players would be in the match + * @throws WrongStateException if called while in a state that doesn't allow adding players */ - public void addPlayer(Player player) throws Exception { + public void addPlayer(Player player) throws IllegalArgumentException, WrongStateException { if(!players.contains(player)) { currentState.addPlayer(); players.add(player); currentState.transition(); } else { - throw new IllegalArgumentException("Duplicated Player in a Match"); + throw new IllegalArgumentException("Duplicated player in a match"); } } @@ -100,7 +101,8 @@ public void addPlayer(Player player) throws Exception { * Note: Called by the Controller when a player quits the match. * @param player player to be removed from the match */ - public void removePlayer(Player player) throws WrongStateException, Exception { + // TODO: Add PlayerAbortedState + public void removePlayer(Player player) throws WrongStateException { currentState.removePlayer(); players.remove(player); currentState.transition(); @@ -116,23 +118,26 @@ public boolean isFull() { } /** - * Modifies the current player according to the next turn: - * If it's the first turn, the current player gets initialized as the - * first one in the players List; the turn order then follows the players List order, in a circular way. - * Ex. 1 -> 2 -> 3 -> 1 -> etc. + * Modifies the current player according to the next turn: if it's the first turn, the current player is the first + * one in the players List, the turn order then follows the players List order, in a circular way. + * Ex. 1st -> 2nd -> 3rd ---> 1st -> 2nd etc. * Note: Called by NextTurnState every time a new turn starts. */ protected void nextPlayer() { - // If player has never been initialized OR the current player is the last one - if (currentPlayer == null || currentPlayer.equals(players.getLast())) { - // Set currentPlayer as the first one - currentPlayer = players.getFirst(); - - turn++; + if(!players.isEmpty()) { + // If player has never been initialized OR the current player is the last one + if (currentPlayer == null || currentPlayer.equals(players.getLast())) { + // Set currentPlayer as the first one + currentPlayer = players.getFirst(); + + turn++; + } else { + // Get the index of the current player and choose the next one + int currentPlayerIndex = players.indexOf(currentPlayer); + currentPlayer = players.get(currentPlayerIndex + 1); + } } else { - // Get the index of the current player and choose the next one - int currentPlayerIndex = players.indexOf(currentPlayer); - currentPlayer = players.get(currentPlayerIndex + 1); + throw new RuntimeException("No players in the match, the next player cannot be set"); } } @@ -205,10 +210,23 @@ protected void setState(MatchState state) { this.currentState = state; } - protected InitialCard drawInitialCard() throws Exception, WrongStateException { + /** + * + * @return + * @throws WrongStateException + * @throws DeckException + */ + protected InitialCard drawInitialCard() throws WrongStateException { currentState.drawInitialCard(); - currentGivenInitialCard = initialsDeck.pop(); + + try { + currentGivenInitialCard = initialsDeck.pop(); + } catch (DeckException e) { + throw new RuntimeException(e); + } + currentState.transition(); + return currentGivenInitialCard; } @@ -222,8 +240,11 @@ protected Pair proposeSecretObjectives() throws WrongState try { Objective obj1 = objectivesDeck.pop(); Objective obj2 = objectivesDeck.pop(); + currentProposedObjectives = new Pair<>(obj1, obj2); + currentState.transition(); + return currentProposedObjectives; } catch (Exception e) { throw new RuntimeException(e); @@ -238,6 +259,7 @@ protected Pair proposeSecretObjectives() throws WrongState */ protected void setSecretObjective(Objective objective) throws WrongChoiceException, WrongStateException, Exception { currentState.chooseSecretObjective(); + // Get proposed objectives Objective firstProposedObjective = currentProposedObjectives.first(); Objective secondProposedObjective = currentProposedObjectives.second(); @@ -250,6 +272,7 @@ else if (objective.equals(secondProposedObjective)) else // If the objective is not one of the proposed ones, throw an exception throw new WrongChoiceException("The chosen objective is not one of the proposed ones"); + currentState.transition(); } @@ -336,7 +359,8 @@ protected void setupBoards() { * @param side side of the card to be placed * @throws WrongStateException if called while in a state that doesn't allow making moves */ - protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException, CardException, Exception { + protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException, CardException { + currentState.makeMove(); Board currentPlayerBoard = currentPlayer.getBoard(); // If placing the card in the current player's board is allowed by rules @@ -352,7 +376,11 @@ protected void makeMove(Pair coords, PlayableCard card, Side s // Remove the card from the player's hand // since it has been placed on the board - currentPlayerBoard.removeHandCard(card); + try { + currentPlayerBoard.removeHandCard(card); + } catch (HandException e) { + throw new RuntimeException(e); + } // Update the current player's points currentPlayer.addPoints(gainedPoints); @@ -368,6 +396,7 @@ protected void makeMove(Pair coords, PlayableCard card, Side s // the match is now finished if (currentPlayer.equals(players.getLast()) && lastTurn) finished = true; + currentState.transition(); break; case INVALID_COORDS: @@ -375,6 +404,7 @@ protected void makeMove(Pair coords, PlayableCard card, Side s case INVALID_ENOUGH_RESOURCES: throw new WrongChoiceException("Not enough resources!"); } + currentState.transition(); } /** @@ -386,7 +416,7 @@ protected void makeMove(Pair coords, PlayableCard card, Side s * @throws WrongChoiceException if the source does not have cards * @return the card drawn */ - protected PlayableCard drawCard(DrawSource source) throws WrongStateException, WrongChoiceException, Exception { + protected PlayableCard drawCard(DrawSource source) throws WrongStateException, WrongChoiceException { currentState.drawCard(); PlayableCard card; @@ -459,10 +489,17 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W * @param side the side to put the initial card on * @throws WrongStateException if called while in a state that doesn't allow choosing the initial card side */ - protected void setInitialSide(Side side) throws WrongStateException, CardException, Exception { + protected void setInitialSide(Side side) throws WrongStateException { currentState.chooseInitialSide(); - currentPlayer.getBoard().setInitialCard(currentGivenInitialCard, side); + + try { + currentPlayer.getBoard().setInitialCard(currentGivenInitialCard, side); + } catch (CardException e) { + throw new RuntimeException(e); + } + currentGivenInitialCard = null; + currentState.transition(); } From cd48c0acf3a64e54d9dced39122f44602c7c0484 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Thu, 11 Apr 2024 20:00:52 +0200 Subject: [PATCH 58/68] Add javadoc to MatchState and defualt behaviour of removePlayer, add PlayerQuitException --- .../ingsw/exceptions/PlayerQuitException.java | 4 + .../it/polimi/ingsw/gamemodel/MatchState.java | 89 ++++++++++++++++--- 2 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 src/main/java/it/polimi/ingsw/exceptions/PlayerQuitException.java diff --git a/src/main/java/it/polimi/ingsw/exceptions/PlayerQuitException.java b/src/main/java/it/polimi/ingsw/exceptions/PlayerQuitException.java new file mode 100644 index 00000000..2e65c66f --- /dev/null +++ b/src/main/java/it/polimi/ingsw/exceptions/PlayerQuitException.java @@ -0,0 +1,4 @@ +package it.polimi.ingsw.exceptions; + +public class PlayerQuitException { +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java index 71b142d8..afccf4ae 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java @@ -1,6 +1,16 @@ package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.exceptions.PlayerQuitException; import it.polimi.ingsw.exceptions.WrongStateException; +/** + * Represents an appendix of {@link Match}. + * Match fully delegates to this class, through composition, the role of keeping track of the current game state: it + * throws exceptions when a Match method is called while being in the wrong state and triggers some Match behavior + * (calling match.someMethod(...)) when a certain transition has occurred. + * Each method is supposed to be overridden by a subclass of MatchState if and only if it is allowed to be called from + * that specific subclass; if not overridden, the MatchState version of the method will be called and thus an exception + * will be thrown. + */ public abstract class MatchState { Match match; @@ -8,39 +18,96 @@ public MatchState (Match match) { this.match = match; } + /** + * Triggers the transition in Match from the current state to the next one, this means changing the current Match's + * MatchState instance to a new one, having the same static type (MatchState) but a different dynamic type (a subclass + * of MatchState). + */ public abstract void transition(); + /** + * Checks dynamically if a player can be added in the current state, otherwise throws an exception. + * The check is performed implicitly, since this version of the method is called if and only if it hasn't been + * overridden by the MatchState object from which it's called. + * @throws WrongStateException if the method cannot be called in the current state + */ public void addPlayer() throws WrongStateException { throw new WrongStateException("addPlayer not allowed from the current match state!"); } - // TODO: Add PlayerAbortedState - public void removePlayer() { - // throw new WrongStateException("removePlayer not allowed from the current match state!"); + /** + * Checks dynamically if a player can be removed in the current state, otherwise it forces the Match to go to the + * FinalState and throws an exception. + * The check is performed implicitly, since this version of the method is called if and only if it hasn't been + * overridden by the MatchState object from which it's called. + * @throws PlayerQuitException if the method cannot be called in the current state + */ + public void removePlayer() throws PlayerQuitException { + // Exceptionally force the match to go to FinalState + // since a player has quit in a state that wasn't WaitState match.setState(new FinalState(match)); + + throw new PlayerQuitException("Player quit not allowed in the current match state!"); } - + + /** + * Checks dynamically if an initial card can be drawn in the current state, otherwise throws an exception. + * The check is performed implicitly, since this version of the method is called if and only if it hasn't been + * overridden by the MatchState object from which it's called. + * @throws WrongStateException if the method cannot be called in the current state + */ public void drawInitialCard() throws WrongStateException{ - throw new WrongStateException("deawInitialCard not allowed from the current match state!"); + throw new WrongStateException("drawInitialCard not allowed in the current match state!"); } - + + /** + * Checks dynamically if a player can choose the initial card side in the current state, otherwise throws an exception. + * The check is performed implicitly, since this version of the method is called if and only if it hasn't been + * overridden by the MatchState object from which it's called. + * @throws WrongStateException if the method cannot be called in the current state + */ public void chooseInitialSide() throws WrongStateException{ - throw new WrongStateException("chooseInitialSide not allowed from the current match state!"); + throw new WrongStateException("chooseInitialSide not allowed in the current match state!"); } + /** + * Checks dynamically if a player can make a move in the current state, otherwise throws an exception. + * The check is performed implicitly, since this version of the method is called if and only if it hasn't been + * overridden by the MatchState object from which it's called. + * @throws WrongStateException if the method cannot be called in the current state + */ public void makeMove() throws WrongStateException{ - throw new WrongStateException("makeMove not allowed from the current match state!"); + throw new WrongStateException("makeMove not allowed in the current match state!"); } + /** + * Checks dynamically if a player can draw a playable card in the current state, otherwise throws an exception. + * The check is performed implicitly, since this version of the method is called if and only if it hasn't been + * overridden by the MatchState object from which it's called. + * @throws WrongStateException if the method cannot be called in the current state + */ public void drawCard() throws WrongStateException{ - throw new WrongStateException("drawCard not allowed from the current match state!"); + throw new WrongStateException("drawCard not allowed in the current match state!"); } + /** + * Checks dynamically if a secret objective can be proposed to the current player in the current state, otherwise + * throws an exception. + * The check is performed implicitly, since this version of the method is called if and only if it hasn't been + * overridden by the MatchState object from which it's called. + * @throws WrongStateException if the method cannot be called in the current state + */ public void proposeSecretObjectives() throws WrongStateException{ - throw new WrongStateException("proposeSecretObjective not allowed from the current match state!"); + throw new WrongStateException("proposeSecretObjective not allowed in the current match state!"); } + /** + * Checks dynamically if a player can choose their secret objective in the current state, otherwise throws an exception. + * The check is performed implicitly, since this version of the method is called if and only if it hasn't been + * overridden by the MatchState object from which it's called. + * @throws WrongStateException if the method cannot be called in the current state + */ public void chooseSecretObjective() throws WrongStateException { - throw new WrongStateException("chooseSecretObjective not allowed from the current match state!"); + throw new WrongStateException("chooseSecretObjective not allowed in the current match state!"); } } From 81731829de3f0c6603dab9dfae272a31d4ddbd35 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Thu, 11 Apr 2024 20:03:54 +0200 Subject: [PATCH 59/68] Add tests to MatchTest Methods test added/finished: - drawCard - makeMove --- .../it/polimi/ingsw/gamemodel/MatchTest.java | 201 +++++++++++++++--- 1 file changed, 167 insertions(+), 34 deletions(-) diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java index 98261668..c3f3a088 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -9,14 +9,10 @@ import javax.management.RuntimeErrorException; -import it.polimi.ingsw.exceptions.CardException; -import it.polimi.ingsw.exceptions.WrongChoiceException; +import it.polimi.ingsw.exceptions.*; import it.polimi.ingsw.utils.Pair; import org.junit.Test; -import it.polimi.ingsw.exceptions.InvalidResourceException; -import it.polimi.ingsw.exceptions.WrongStateException; - import static org.junit.Assert.*; public class MatchTest { @@ -177,34 +173,45 @@ public void nextPlayer() { @Test public void removePlayer() { initializeAllEqualDecks(); + match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + player1 = new Player("Oingo", match); player2 = new Player("Boingo", match); player3 = new Player("Jotaro", match); player4 = new Player("Polnareff", match); - // Test in waitstate + // Tests while Match is in WaitState: removePlayer can be called try { match.addPlayer(player1); match.addPlayer(player2); + + // Verify that Match correctly removes a player match.removePlayer(player1); assertFalse("Player not removed", match.getPlayers().contains(player1)); + + // Match should now be full and switch to NextTurnState match.addPlayer(player1); match.addPlayer(player3); match.addPlayer(player4); } catch (Exception e) { fail("No exception must be thrown, exception thrown:" + e.getMessage()); } + + // Verify that Match is in the right state, that is NextTurnState assertTrue("Match not in correct state", match.getCurrentState() instanceof NextTurnState); - // Test outside of waitstate + + // Match is not in NextTurnState + // Tests while Match is NOT in WaitState: removePlayer cannot be called try { + // Verify that Match throws an exception when the method is called while being in the wrong state match.removePlayer(player1); - } catch (Exception e) { - fail("No exception must be thrown, exception thrown:" + e.getMessage()); - } - assertFalse("Player was not removed", match.getPlayers().contains(player1)); - // TODO end match - //assertTrue("Match has not ended after player quit in game", match.isFinished()); + // An exception is supposed to be thrown here + fail("match.removePlayer called in wrong state and an exception hasn't been thrown"); + } catch (PlayerQuitException e) {} + + // Verify that Match, after a player quit in the wrong state, is in FinalState + assertTrue("Match is not in FinalState even if a player quit in the wrong state", match.getCurrentState() instanceof FinalState); } @Test @@ -367,7 +374,7 @@ public void makeMove() { // Try placing a card in the wrong coordinates try { - match.makeMove(new Pair<>(1, 4), match.getCurrentPlayer().getBoard().getCurrentHand().get(0), Side.FRONT); + match.makeMove(new Pair<>(1, 4), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.FRONT); fail("Should have thrown a WrongChoiceException"); } catch (WrongChoiceException e) { // Good @@ -378,34 +385,151 @@ public void makeMove() { // Try placing that is not in the player hand try { match.makeMove(new Pair<>(1, 1), generateRandomResourceCard(), Side.FRONT); - fail("Should have thrown CardException"); - } catch (CardException e) { + fail("Should have thrown WrongChoiceException"); + } catch (WrongChoiceException e) { // Good } catch (Exception e) { throw new RuntimeException(e); } + // Try placing a card that the user does not enough resources to + PlayableCard card = match.getCurrentPlayer().getBoard().getCurrentHand().get(0); + try { + match.makeMove(new Pair<>(1, 1), card, Side.FRONT); + fail("Card placed even though there were not enough resources available"); + } catch (Exception e) { + // Good + } // Try placing the card in the right position - PlayableCard card = match.getCurrentPlayer().getBoard().getCurrentHand().get(0); + card = match.getCurrentPlayer().getBoard().getCurrentHand().get(1); + // Note: now user has 2 Fungus + try { match.makeMove(new Pair<>(1, 1), card, Side.BACK); } catch (Exception e) { - fail("Exception thrown when no exception expected"); + fail("Exception thrown when no exception expected" + e.getMessage()); } assertEquals("Card not placed", card, match.getCurrentPlayer().getBoard().getPlacedCards().get(new Pair<>(1,1)).getCard()); assertEquals("Card not removed from hand", 2, match.getCurrentPlayer().getBoard().getCurrentHand().size()); - assertTrue("State not changed after makeMove", match.getCurrentState() instanceof AfterMoveState); + // Make the game go on + try { + match.getCurrentPlayer().drawCard(DrawSource.RESOURCES_DECK); + match.makeMove(new Pair<>(1,1), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + match.getCurrentPlayer().drawCard(DrawSource.RESOURCES_DECK); + } catch (Exception e) { + fail(match.getCurrentState().getClass().getSimpleName()); + throw new RuntimeException(e); + } + // Test placing a gold card with enough resources, should not fail + PlayableCard card2 = match.getCurrentPlayer().getBoard().getCurrentHand().get(0); + try { + Player curr = match.getCurrentPlayer(); + int oldplayerpoints = curr.getPoints(); + match.makeMove(new Pair<>(2,2), card2, Side.FRONT); + assertEquals(oldplayerpoints + card2.points, match.getCurrentPlayer().getPoints()); + } catch (Exception e) { + fail("No exception should be thrown: " + e.getMessage()); + } + } + + @Test + public void drawCard() { + initializeBlankStartedMatch(2, 2, 10, 9, 7); + + // Try drawing card in the wrong state + try { + match.drawCard(DrawSource.FIRST_VISIBLE); + // An exception is supposed to be thrown here + fail("Should have thrown WrongStateException"); + } + catch (WrongStateException e) {} + catch (Exception e) { + fail("Wrong exception launched: " + e.getMessage()); + } + assertEquals(3, goldsDeck.getSize()); + assertEquals(3, resourcesDeck.getSize()); + // NOTE: at this point of the game + // resourcesDeck: 3 card goldsDeck: 3 cards + // Try drawing a card from FIRST_VISIBLE + // Draw from every source + DrawSource[] sources = DrawSource.values(); + int j = 1; + for (int i = 0; i < 6; i++) { + try { + Player p = match.getCurrentPlayer(); + + p.playCard(new Pair<>(j, j), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + // Increment indexes for the next player + if (match.getPlayers().getLast().equals(match.getCurrentPlayer())) j++; + PlayableCard card = match.drawCard(sources[i]); + p.getBoard().addHandCard(card); + assertNotNull(card); + // Check right replacement + if (i == 0) { + assertTrue("Wrong state reached after draw", match.getCurrentState() instanceof NextTurnState); + assertEquals(2, goldsDeck.getSize()); + } else if (i == 1) { + assertTrue("Wrong state reached after draw", match.getCurrentState() instanceof NextTurnState); + assertEquals(2, resourcesDeck.getSize()); + } else if (i <= 3) { + assertTrue("Wrong state reached after draw", match.getCurrentState() instanceof NextTurnState); + assertTrue("Wrong card placed", match.getVisiblePlayableCards().get(sources[i]) instanceof GoldCard); + } else { + if (i == 4) { + assertTrue("Wrong state reached after draw", match.getCurrentState() instanceof NextTurnState); + } else { + assertTrue("Wrong state reached after draw", match.getCurrentState() instanceof FinalState); + } + assertTrue("Wrong card placed", match.getVisiblePlayableCards().get(sources[i]) instanceof ResourceCard); + } + } catch (Exception e) { + fail("Should not have launched an exception: " + e.getMessage()); + } + } + // Check the state + // Now all the decks should be empty and the match should be finished + assertEquals(0, goldsDeck.getSize()); + assertEquals(0, resourcesDeck.getSize()); + assertTrue(match.isFinished()); + + // Try drawing from an empty deck + initializeBlankStartedMatch(2, 2, 10, 6, 4); + j=1; + assertFalse(match.isFinished()); + // Draw from the two empty decks + for (int i = 0; i < 2; i++) { + try { + match.getCurrentPlayer().playCard(new Pair<>(j, j), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + match.drawCard(sources[i]); + } catch (WrongChoiceException e) { + // Go on with the state, correct exception launched + try { + match.drawCard(sources[i + 2]); + } catch (Exception e2) { + throw new RuntimeException(e2); + } + } catch (Exception e) { + fail("Wrong exception launched: " + e.getMessage() + " " + match.getCurrentState().getClass().getSimpleName()); + } + } + assertTrue(match.isFinished()); } + + // Private helper Methods private void initializeAllEqualDecks() { // Initialize Initial Cards deck - initialsDeck = createDeterministicInitialsDeck(4); - objectivesDeck = createDeterministicObjectivesDeck(10); - goldsDeck = createGoldsDeck(40); - resourcesDeck = createResourcesDeck(40); + initializeAllEqualDecks(4, 10, 40, 40); + } + + private void initializeAllEqualDecks(int initialsNum, int objectsNum, int resourcesNum, int goldsNum) { + initialsDeck = createDeterministicInitialsDeck(initialsNum); + objectivesDeck = createDeterministicObjectivesDeck(objectsNum); + goldsDeck = createDeterministicGoldsDeck(goldsNum); + resourcesDeck = createDeterministicResourcesDeck(resourcesNum); } private static GameDeck createDeterministicObjectivesDeck(int size) { @@ -413,7 +537,7 @@ private static GameDeck createDeterministicObjectivesDeck(int size) { GameDeck objectivesDeck = new GameDeck<>(); Objective objective = null; HashMap resources = new HashMap<>(); - resources.put(Symbol.FUNGUS, 2); + resources.put(Symbol.ANIMAL, 90); try { Requirement req = new QuantityRequirement(resources); } catch (Exception e) { @@ -433,7 +557,7 @@ private static GameDeck createDeterministicInitialsDeck(int size) { for (int i = 0; i < size; i++) { card = new InitialCard( new CardFace(Symbol.FUNGUS, Symbol.ANIMAL, Symbol.PLANT, Symbol.INSECT, Collections.emptySet()), - new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Collections.emptySet())); initialsDeck.add(card); } @@ -446,9 +570,10 @@ private static GameDeck createDeterministicResourcesDeck(int size) for (int i = 0; i < size; i++) { try { card = new ResourceCard( - new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Collections.emptySet()), Symbol.FUNGUS, 0); + resourcesDeck.add(card); } catch (Exception e) { throw new RuntimeException(); } @@ -460,12 +585,13 @@ private static GameDeck createDeterministicGoldsDeck(int size) { GameDeck goldsDeck = new GameDeck<>(); GoldCard card = null; HashMap resources = new HashMap<>(); - resources.put(Symbol.FUNGUS, 1); + resources.put(Symbol.FUNGUS, 2); for (int i = 0; i < size; i++) { try { - card = new GoldCard(new CardFace(Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, Symbol.EMPTY_CORNER, - Symbol.EMPTY_CORNER, Collections.emptySet()), Symbol.FUNGUS, Symbol.FEATHER, 3, + card = new GoldCard(new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.EMPTY_CORNER, + Symbol.FULL_CORNER, Collections.emptySet()), Symbol.FUNGUS, Symbol.NO_MULT, 3, new QuantityRequirement(resources)); + goldsDeck.add(card); } catch (Exception e) { throw new RuntimeErrorException(null); } @@ -574,18 +700,17 @@ private Symbol randomSymbol(EnumSet validSymbols) { return null; } - private void initializeBlankMatch(int maxPlayers) { + private void initializeBlankMatch(int maxPlayers, int initialsNum, int objectivesNum, int resourcesNum, int goldsNum) { // Setup a basic game String names[] = {"Oingo", "Boingo", "Jotaro", "Polnareff"}; Player players[] = new Player[4]; - initializeAllEqualDecks(); + initializeAllEqualDecks(initialsNum, objectivesNum, resourcesNum, goldsNum); match = new Match(maxPlayers, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); for (int i = 0; i < maxPlayers; i++) { players[i] = new Player(names[i], match); - try { match.addPlayer(players[i]); } catch (Exception e) { @@ -594,9 +719,13 @@ private void initializeBlankMatch(int maxPlayers) { } } - private void initializeBlankStartedMatch(int maxPlayers) { + protected void initializeBlankMatch(int maxPlayers) { + initializeBlankMatch(maxPlayers, 4, 10, 40, 40); + } + + private void initializeBlankStartedMatch(int maxPlayers, int initialsNum, int objectivesNum, int resourcesNum, int goldsNum) { // Setup a basic game - initializeBlankMatch(maxPlayers); + initializeBlankMatch(maxPlayers, initialsNum, objectivesNum, resourcesNum, goldsNum); try { for (int i = 0; i < maxPlayers; i++) { @@ -612,4 +741,8 @@ private void initializeBlankStartedMatch(int maxPlayers) { throw new RuntimeException(e); } } + + private void initializeBlankStartedMatch(int maxPlayers) { + initializeBlankStartedMatch(maxPlayers, 4, 10, 40, 40); + } } From 25dcf6c088c2e2a60483d1da53c5a265505cfb1e Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Thu, 11 Apr 2024 20:05:41 +0200 Subject: [PATCH 60/68] Fix Match according to MatchTest result and add functionality to decide the winner --- .../ingsw/exceptions/PlayerQuitException.java | 5 +- .../ingsw/gamemodel/AfterMoveState.java | 2 + .../it/polimi/ingsw/gamemodel/FinalState.java | 2 + .../java/it/polimi/ingsw/gamemodel/Match.java | 211 +++++++++++++----- 4 files changed, 168 insertions(+), 52 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/exceptions/PlayerQuitException.java b/src/main/java/it/polimi/ingsw/exceptions/PlayerQuitException.java index 2e65c66f..807d226d 100644 --- a/src/main/java/it/polimi/ingsw/exceptions/PlayerQuitException.java +++ b/src/main/java/it/polimi/ingsw/exceptions/PlayerQuitException.java @@ -1,4 +1,7 @@ package it.polimi.ingsw.exceptions; -public class PlayerQuitException { +public class PlayerQuitException extends Exception{ + public PlayerQuitException(String message) { + super(message); + } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java index dd52c888..b7f503e4 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java @@ -17,5 +17,7 @@ public void drawCard() throws WrongStateException { public void transition() { MatchState nextState = new AfterDrawState(match); match.setState(nextState); + + nextState.transition(); } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/FinalState.java b/src/main/java/it/polimi/ingsw/gamemodel/FinalState.java index 1eb8bc1b..e042a124 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/FinalState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/FinalState.java @@ -4,6 +4,8 @@ public class FinalState extends MatchState { public FinalState(Match match) { super(match); + + match.decideWinner(); } @Override diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index 5c3e08cc..055c3593 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -2,7 +2,9 @@ import it.polimi.ingsw.utils.Pair; +import java.sql.Array; import java.util.*; +import java.util.stream.Collectors; import it.polimi.ingsw.exceptions.*; @@ -16,8 +18,6 @@ * an event occurred, such as nextPlayer(...). */ public class Match { - // TODO: Fix exception throwing/handling - private final List players; private final int maxPlayers; private Player currentPlayer; @@ -46,6 +46,11 @@ public class Match { // Current match turn as an integer (incremental) private int turn; + // Players ranking of the match at the end of it. + // The List order represents the ranking order, the Boolean represent if the related player is a winner. + // This is needed since the match can end in a tie, in such case the first two/three players of the List will have a + // True flag. + private List> playersFinalRanking; /** * Initializes main Match attributes and allocate the attribute players List, assuming no parameter is null. @@ -64,9 +69,9 @@ public Match(int maxPlayers, GameDeck initialsDeck, GameDeck(objective1, objective2); - } catch (Exception e) { + } catch (DeckException e) { throw new RuntimeException(e); } } @@ -351,28 +356,39 @@ protected void setupBoards() { /** * Makes the chosen move on the board of the current player (known because of the internal Match state); - * in particular checks if the placement is valid, then places the card on the player's board and add points + * in particular, checks if the placement is valid, then places the card on the player's board and add points * to the player. * Note: Called by the current player. * @param coords coordinates in which to place the card * @param card card to place * @param side side of the card to be placed * @throws WrongStateException if called while in a state that doesn't allow making moves + * @throws WrongChoiceException if the move is not allowed (placement not allowed, or not enough resources, or card + * not in player's hand) */ - protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException, CardException { + protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongChoiceException { currentState.makeMove(); + Board currentPlayerBoard = currentPlayer.getBoard(); + PlacementOutcome outcome; + try { + outcome = currentPlayerBoard.verifyCardPlacement(coords, card, side); + } catch (CardException e) { + throw new WrongChoiceException(e.getMessage()); + } + // If placing the card in the current player's board is allowed by rules - PlacementOutcome outcome = currentPlayerBoard.verifyCardPlacement(coords, card, side); switch (outcome) { case VALID: - // Trigger current state behavior - currentState.makeMove(); - // Place the card in the current player's board // and save the points possibly gained because of the move - int gainedPoints = currentPlayerBoard.placeCard(coords, card, side, turn); + int gainedPoints; + try { + gainedPoints = currentPlayerBoard.placeCard(coords, card, side, turn); + } catch (CardException e) { + throw new RuntimeException(e); + } // Remove the card from the player's hand // since it has been placed on the board @@ -390,13 +406,6 @@ protected void makeMove(Pair coords, PlayableCard card, Side s if (currentPlayer.getPoints() >= 20) lastTurn = true; - // If the current player is the last one in the match turns rotation - // i.e. the last one in the players List - // AND the current turn is the last one - // the match is now finished - if (currentPlayer.equals(players.getLast()) && lastTurn) - finished = true; - currentState.transition(); break; case INVALID_COORDS: @@ -404,43 +413,49 @@ protected void makeMove(Pair coords, PlayableCard card, Side s case INVALID_ENOUGH_RESOURCES: throw new WrongChoiceException("Not enough resources!"); } - currentState.transition(); } /** - * Draws a card from the requested source; if a visible card is chosen, this one gets substituted by a new one - * drawn by a deck. + * Draws a card from the passed source. If the caller wants to draw one of the four visible cards, a rule is applied: + * the first and second visible cards (FIRST/SECOND_VISIBLE) will be substituted by a gold card if possible, if not, + * by a resource card, if not again, they will be null (then not substituted); the first and second visible cards + * (THIRD/FOURTH_VISIBLE) will be substituted by a resource card if possible, if not, by a gold card, if not again, + * they will be null (then not substituted). * Note: Called by the current player. - * @param source represents the source of the draw + * @param source the source to draw a card from * @throws WrongStateException if called while in a state that doesn't allow making moves * @throws WrongChoiceException if the source does not have cards * @return the card drawn */ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, WrongChoiceException { + PlayableCard card; + currentState.drawCard(); - PlayableCard card; switch (source) { case GOLDS_DECK -> { - if (goldsDeck.isEmpty()) - throw new WrongChoiceException("Golds deck is empty!"); - - card = goldsDeck.poll(); + try { + card = goldsDeck.pop(); + } catch (DeckException e) { + throw new WrongChoiceException("The gold cards deck is empty!"); + } } - case RESOURCES_DECK -> { - if (resourcesDeck.isEmpty()) - throw new WrongChoiceException("Resources deck is empty!"); - card = resourcesDeck.poll(); + case RESOURCES_DECK -> { + try { + card = resourcesDeck.pop(); + } catch (DeckException e) { + throw new WrongChoiceException("The resource cards deck is empty!"); + } } + case FIRST_VISIBLE, SECOND_VISIBLE -> { - // Get the chosen card card = visiblePlayableCards.get(source); // If not present (e.g. on the last turn both decks are empty, so remaining turns will be played // drawing the four visible cards, but they won't be substituted by others) throw an exception if (card == null) - throw new WrongChoiceException("There is no visible gold in position one!"); + throw new WrongChoiceException("There is no visible card in the chosen position!"); // If the golds deck is NOT empty, substitute the first/second visible // card with a new gold @@ -453,18 +468,18 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W // If the resources deck is empty too, the GameDeck.poll() method returns null, // then the corresponding visible card will be null } + case THIRD_VISIBLE, FOURTH_VISIBLE -> { - // Get the chosen card card = visiblePlayableCards.get(source); // If not present (e.g. on the last turn both decks are empty, so remaining turns will be played // drawing the four visible cards, but they won't be substituted by others) throw an exception if (card == null) - throw new WrongChoiceException("There is no visible gold in position one!"); + throw new WrongChoiceException("There is no visible card in the chosen position!"); // If the resources deck is NOT empty, substitute the third/fourth visible // card with a new resource - if(!goldsDeck.isEmpty()) + if(!resourcesDeck.isEmpty()) visiblePlayableCards.put(source, resourcesDeck.poll()); // If the resources deck is empty, substitute the third/fourth visible // card with a gold @@ -473,19 +488,28 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W // If the golds deck is empty too, the GameDeck.poll() method returns null, // then the corresponding visible card will be null } + default -> { - throw new WrongChoiceException("Unexpected value: " + source); + throw new RuntimeException("Unexpected value of source"); } + } if (goldsDeck.isEmpty() && resourcesDeck.isEmpty()) lastTurn = true; + + // If the current player is the last one in the match turns rotation, i.e. the last one in the players List + // AND the current turn is the last one the match is now finished + if (currentPlayer.equals(players.getLast()) && lastTurn) + finished = true; + currentState.transition(); + return card; } /** - * Sets the current player's initial card side + * Sets the current player's initial card side. * @param side the side to put the initial card on * @throws WrongStateException if called while in a state that doesn't allow choosing the initial card side */ @@ -502,17 +526,102 @@ protected void setInitialSide(Side side) throws WrongStateException { currentState.transition(); } - - /** - * Returns the visible objectives - * - * @return a pair containing two objectives that are visible - * + + // Returns a Map that links each player to the number of DIFFERENT objectives achieved + private Map checkObjectivesAchievement() { + // Map that links each player to the number of DIFFERENT objectives achieved + Map playersAchievedObjectives = new HashMap<>(); + + Objective firstObjective = visibleObjectives.first(); + Objective secondObjective = visibleObjectives.second(); + + for (Player p : players) { + Board board = p.getBoard(); + Objective secretObjective = p.getSecretObjective(); + int numAchievedObjectives = 0; + + // Add to the player the points of the specific objective MULTIPLIED BY how many times they met the + // objective requirement + p.addPoints(secretObjective.getPoints() * secretObjective.getReq().timesMet(board)); + p.addPoints(firstObjective.getPoints() * firstObjective.getReq().timesMet(board)); + p.addPoints(secondObjective.getPoints() * secondObjective.getReq().timesMet(board)); + + // Count the number of achieved ojectives by the player + if (secretObjective.getReq().timesMet(board) >= 1) + numAchievedObjectives++; + if (firstObjective.getReq().timesMet(board) >= 1) + numAchievedObjectives++; + if (secondObjective.getReq().timesMet(board) >= 1) + numAchievedObjectives++; + + playersAchievedObjectives.put(p, numAchievedObjectives); + } + + return playersAchievedObjectives; + } + + /** + * Calculates the winner (or winners) + */ + protected void decideWinner() { + Map achievedObjectives = checkObjectivesAchievement(); + + List sortedPlayers = players.stream() + .sorted( + // Create a comparator that firstly sorts for player points + // and in case of same points, for the number of achieved objectives + Comparator.comparingInt(Player::getPoints) + .thenComparing(achievedObjectives::get) + .reversed() + ) + .toList(); + + Player bestPlayer = sortedPlayers.get(0); + int bestAchievedObjectives = achievedObjectives.get(bestPlayer); + int bestPoints = bestPlayer.getPoints(); + boolean winner; + + for (Player p : sortedPlayers) { + winner = false; + + // If the current player has as many points and as many achieved objectives as the winner, then they're + // winner too + if (p.getPoints() == bestPoints && achievedObjectives.get(p) == bestAchievedObjectives) + winner = true; + + playersFinalRanking.add(new Pair<>(p, winner)); + } + } + + /** + * Getter for the final ranking of players. Return a valid result if and only if called when the match is in the + * FinalState, so it's finished. + * @return Players ranking of the match at the end of it; the List order represents the ranking order, the Boolean + * represent if the related player is a winner; this is needed since the match can end in a tie, in such case the + * first two/three players of the List will have a True flag + */ + public List> getPlayersFinalRanking() { + return playersFinalRanking; + } + + /** + * Returns the visible objectives. + * @return a Pair containing the two visible objectives */ public Pair getVisibleObjectives() { return visibleObjectives; } + /** + * Getter for the four visible playable cards (i.e. resource cards and gold cards, not objectives) on the common + * table. + * @return a Map that links each visible playable card to a DrawSource (restricted to FIRST_VISIBLE, SECOND_VISIBLE, + * THIRD_VISIBLE, FOURTH_VISIBLE) + */ + public Map getVisiblePlayableCards() { + return visiblePlayableCards; + } + /** * Getter for the current match state. * @return the current state of the match From b6735fde2f3a46cb3b8d404e0b224901b2c02d40 Mon Sep 17 00:00:00 2001 From: FrancescoCaracciolo Date: Thu, 11 Apr 2024 23:28:39 +0200 Subject: [PATCH 61/68] Fix test not passing --- .../java/it/polimi/ingsw/gamemodel/Match.java | 6 ++++-- .../it/polimi/ingsw/gamemodel/MatchTest.java | 17 +++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index 055c3593..674028ea 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -542,12 +542,13 @@ private Map checkObjectivesAchievement() { // Add to the player the points of the specific objective MULTIPLIED BY how many times they met the // objective requirement - p.addPoints(secretObjective.getPoints() * secretObjective.getReq().timesMet(board)); + if (secretObjective != null) + p.addPoints(secretObjective.getPoints() * secretObjective.getReq().timesMet(board)); p.addPoints(firstObjective.getPoints() * firstObjective.getReq().timesMet(board)); p.addPoints(secondObjective.getPoints() * secondObjective.getReq().timesMet(board)); // Count the number of achieved ojectives by the player - if (secretObjective.getReq().timesMet(board) >= 1) + if (secretObjective != null && secretObjective.getReq().timesMet(board) >= 1) numAchievedObjectives++; if (firstObjective.getReq().timesMet(board) >= 1) numAchievedObjectives++; @@ -564,6 +565,7 @@ private Map checkObjectivesAchievement() { * Calculates the winner (or winners) */ protected void decideWinner() { + playersFinalRanking = new ArrayList<>(); Map achievedObjectives = checkObjectivesAchievement(); List sortedPlayers = players.stream() diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java index c3f3a088..a3cc45fa 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -90,7 +90,7 @@ public void addPlayer() { // Verify that Match throws an exception when a plyer is added while the match // is in a state which is not valid (i.e. not equal to WaitState) try { - match.setState(new FinalState(match)); + match.setState(new AfterMoveState(match)); match.addPlayer(player1); // An exception is supposed to be thrown here fail("Exception about wrong state not thrown "); @@ -202,13 +202,17 @@ public void removePlayer() { assertTrue("Match not in correct state", match.getCurrentState() instanceof NextTurnState); // Match is not in NextTurnState - // Tests while Match is NOT in WaitState: removePlayer cannot be called + // Tests while Match is NOT in WaitState: removePlayer stops the match try { // Verify that Match throws an exception when the method is called while being in the wrong state match.removePlayer(player1); // An exception is supposed to be thrown here fail("match.removePlayer called in wrong state and an exception hasn't been thrown"); - } catch (PlayerQuitException e) {} + } catch (PlayerQuitException e) { + + } catch (Exception e) { + fail("Wrong exception: " + e.getMessage()); + } // Verify that Match, after a player quit in the wrong state, is in FinalState assertTrue("Match is not in FinalState even if a player quit in the wrong state", match.getCurrentState() instanceof FinalState); @@ -233,7 +237,7 @@ public void drawInitialCard() { assertTrue("State not changed after drawInitialCard", match.getCurrentState() instanceof ChooseInitialSideState); // Verify that Match throws an exception when the method is called while being in the wrong state - match.setState(new FinalState(match)); + match.setState(new AfterMoveState(match)); try { match.drawInitialCard(); @@ -538,13 +542,14 @@ private static GameDeck createDeterministicObjectivesDeck(int size) { Objective objective = null; HashMap resources = new HashMap<>(); resources.put(Symbol.ANIMAL, 90); + Requirement req; try { - Requirement req = new QuantityRequirement(resources); + req = new QuantityRequirement(resources); } catch (Exception e) { throw new RuntimeException(); } for (int i = 0; i < size; i++) { - objective = new Objective(2, null); + objective = new Objective(2, req); objectivesDeck.add(objective); } return objectivesDeck; From ddb9b82c533db1cfcc9db1ec2b5b4df83d6b25a1 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:26:50 +0200 Subject: [PATCH 62/68] Add peek method, fix add method - Add peek method - Fix add method adding a card on top instead of bottom --- .../java/it/polimi/ingsw/gamemodel/GameDeck.java | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java index 8c4bd18b..212c2eed 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java @@ -24,7 +24,7 @@ public GameDeck() { * @param card the card to add */ public void add(T card) { - this.cardsList.add(card); + this.cardsList.addFirst(card); } /** @@ -46,6 +46,16 @@ public T pop() throws DeckException { return cardsList.removeLast(); } + /** + * Retruns a card from the deck's top without removing it (returns null if empty) + * @return the card + */ + public T peek() { + if (this.isEmpty()) + return null; + return cardsList.getLast(); + } + /** * Removes a card from the deck's top (null if the deck is empty) * @return the removed card (null if the deck is empty) From fa2715e3cd0597d3981f6cd298975fe9ec26b108 Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:27:19 +0200 Subject: [PATCH 63/68] Add getVisibleCardsBack --- .../java/it/polimi/ingsw/gamemodel/Match.java | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index 674028ea..7220bfd7 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -632,6 +632,25 @@ public MatchState getCurrentState() { return currentState; } + /** + * Getter of the visible cards back on the top of the decks + * @return Pair of two CardFace, the first one is the CardFace at the + * top of golds deck, the second one is the CardFace at the top of resources deck + */ + public Pair getVisibleCardsBack() { + Card goldCard = goldsDeck.peek(); + Card resourceCard = resourcesDeck.peek(); + CardFace gold = null; + CardFace resource = null; + + if (goldCard != null) + gold = goldCard.getSide(Side.BACK); + if (resourceCard != null) + resource = resourceCard.getSide(Side.BACK); + + return new Pair<>(gold, resource); + } + public int getMaxPlayers() { return maxPlayers; } From f9214f9884f9a37fa912b0c363f5b31d425a22cd Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Fri, 12 Apr 2024 13:27:44 +0200 Subject: [PATCH 64/68] Add decideWinners testing --- .../it/polimi/ingsw/gamemodel/MatchTest.java | 188 +++++++++++++++++- 1 file changed, 182 insertions(+), 6 deletions(-) diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java index a3cc45fa..7c1fccbb 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -1,11 +1,7 @@ package it.polimi.ingsw.gamemodel; import java.security.spec.ECField; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; import javax.management.RuntimeErrorException; @@ -522,8 +518,188 @@ public void drawCard() { assertTrue(match.isFinished()); } + @Test + public void decideWinners() { + // Note: match and because of deck end has already been tested + // in drawCard testing + // Create a match where both players win + initializeBlankStartedMatch(2, 2, 10, 9, 7); + // The objectives used as a test give + // two points for every two fungus + Integer i = 1; + // For every move, a fungus is placed, the index is the number of fungus a player has + while (!match.isFinished()) { + try { + match.getCurrentPlayer().playCard(new Pair<>(i, i), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + match.getCurrentPlayer().drawCard(decideDrawSource()); + match.getCurrentPlayer().playCard(new Pair<>(i, i), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + match.getCurrentPlayer().drawCard(decideDrawSource()); + } catch (Exception e) { + throw new RuntimeException(e); + } + i++; + } + int pair_i = i%2==0 ? i : i-1; + // Since there are three objectives, + // the expected number of points from each player is + // pair_i * 3 + for (Player p : match.getPlayers()) { + assertEquals(i, p.getBoard().getAvailableResources().get(Symbol.FUNGUS)); + assertEquals("Wrong points given to players",pair_i * 3, p.getPoints()); + } + // Check if the ranking is right + List> ranking = match.getPlayersFinalRanking(); + assertTrue("Player is not marked as winner", ranking.get(0).second()); + assertTrue("Player is not marked as winner", ranking.get(1).second()); + // Create a match where only one player wins + initializeBlankMatch(2, 2, 6, 9, 7); + try { + // Finish initials card turn + match.getCurrentPlayer().drawInitialCard(); + match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); + match.getCurrentPlayer().drawInitialCard(); + match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); + + // Change objectives deck + Objective[] normalObjectives = {objectivesDeck.poll(), objectivesDeck.poll()}; + while (!objectivesDeck.isEmpty()) objectivesDeck.poll(); + HashMap impossibleRequirement = new HashMap<>(); + impossibleRequirement.put(Symbol.ANIMAL, 900000); + Objective impossible = new Objective(0, new QuantityRequirement(impossibleRequirement)); + objectivesDeck.add(impossible); + objectivesDeck.add(normalObjectives[0]); + objectivesDeck.add(normalObjectives[1]); + + // Let players "choose" their objectives + match.getCurrentPlayer().drawSecretObjectives(); + match.getCurrentPlayer().chooseSecretObjective(impossible); + match.getCurrentPlayer().drawSecretObjectives(); + match.getCurrentPlayer().chooseSecretObjective(normalObjectives[1]); + } catch (Exception e) { + throw new RuntimeException(e); + } + + i = 1; + // For every move, a fungus is placed, the index is the number of fungus a player has + while (!match.isFinished()) { + try { + match.getCurrentPlayer().playCard(new Pair<>(i, i), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + match.getCurrentPlayer().drawCard(decideDrawSource()); + match.getCurrentPlayer().playCard(new Pair<>(i, i), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + match.getCurrentPlayer().drawCard(decideDrawSource()); + } catch (Exception e) { + throw new RuntimeException(e); + } + i++; + } + player1 = match.getPlayers().get(0); + player2 = match.getPlayers().get(1); + assertEquals("Wrong points given to players",pair_i * 3, player2.getPoints()); + // player 1 has only two objectives + assertEquals("Wrong points given to players",pair_i * 2, player1.getPoints()); + // Check if the ranking is right + ranking = match.getPlayersFinalRanking(); + // Player 2 wins, player 1 lost + assertEquals(player2, ranking.get(0).first()); + assertEquals(player1, ranking.get(1).first()); + assertTrue("Player is not marked as winner", ranking.get(0).second()); + assertFalse("Player is marked as winner", ranking.get(1).second()); + + // This variable is useful for next test + int pointDifference = player2.getPoints() - player1.getPoints(); + // Test that the winner is choosen correctly when + // they have the same amount of points but different amount of objectives + + + // Same as before... + initializeBlankMatch(2, 2, 6, 9, 7); + player1 = match.getPlayers().get(0); + player2 = match.getPlayers().get(1); + try { + // Finish initials card turn + match.getCurrentPlayer().drawInitialCard(); + match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); + match.getCurrentPlayer().drawInitialCard(); + match.getCurrentPlayer().chooseInitialCardSide(Side.FRONT); + + // Change objectives deck + Objective[] normalObjectives = {objectivesDeck.poll(), objectivesDeck.poll()}; + while (!objectivesDeck.isEmpty()) objectivesDeck.poll(); + HashMap impossibleRequirement = new HashMap<>(); + impossibleRequirement.put(Symbol.ANIMAL, 900000); + Objective impossible = new Objective(0, new QuantityRequirement(impossibleRequirement)); + objectivesDeck.add(impossible); + objectivesDeck.add(normalObjectives[0]); + objectivesDeck.add(normalObjectives[1]); + + // Let players "choose" their objectives + match.getCurrentPlayer().drawSecretObjectives(); + match.getCurrentPlayer().chooseSecretObjective(impossible); + match.getCurrentPlayer().drawSecretObjectives(); + match.getCurrentPlayer().chooseSecretObjective(normalObjectives[1]); + } catch (Exception e) { + throw new RuntimeException(e); + } + i = 1; + // For every move, a fungus is placed, the index is the number of fungus a player has + // Let's add a cheat gold card to player1 hand + GoldCard cheatCard = null; + HashMap noreq = new HashMap<>(); + noreq.put(Symbol.FUNGUS, 1); + try { + player1.getBoard().removeHandCard(player1.getBoard().getCurrentHand().get(1)); + cheatCard = new GoldCard( + new CardFace(Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Symbol.FULL_CORNER, Collections.emptySet()), + Symbol.FUNGUS, + Symbol.NO_MULT, + pointDifference, + new QuantityRequirement(noreq) + ); + player1.getBoard().addHandCard(cheatCard); + } catch (Exception e) { + throw new RuntimeException(e); + } + // Simulate the match + while (!match.isFinished()) { + try { + match.getCurrentPlayer().playCard(new Pair<>(i, i), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + match.getCurrentPlayer().drawCard(decideDrawSource()); + match.getCurrentPlayer().playCard(new Pair<>(i, i), match.getCurrentPlayer().getBoard().getCurrentHand().get(1), Side.BACK); + match.getCurrentPlayer().drawCard(decideDrawSource()); + } catch (Exception e) { + throw new RuntimeException(e); + } + i++; + } + // Check that the players have the same amount of points + assertEquals(player1.getPoints(), player2.getPoints()); + // Check if the ranking is right + ranking = match.getPlayersFinalRanking(); + // Player 2 wins, player 1 lost + assertEquals(player2, ranking.get(0).first()); + assertEquals(player1, ranking.get(1).first()); + assertTrue("Player is not marked as winner", ranking.get(0).second()); + assertFalse("Player is marked as winner", ranking.get(1).second()); + } // Private helper Methods + private DrawSource decideDrawSource() { + Map visible = match.getVisiblePlayableCards(); + if (!resourcesDeck.isEmpty()) { + return DrawSource.RESOURCES_DECK; + } else if (!goldsDeck.isEmpty()) { + return DrawSource.GOLDS_DECK; + } else { + DrawSource[] values = DrawSource.values(); + for (int i=2; i < 6; i++) { + if (visible.get(values[i]) != null) { + return values[i]; + } + } + } + return null; + } + private void initializeAllEqualDecks() { // Initialize Initial Cards deck initializeAllEqualDecks(4, 10, 40, 40); @@ -541,7 +717,7 @@ private static GameDeck createDeterministicObjectivesDeck(int size) { GameDeck objectivesDeck = new GameDeck<>(); Objective objective = null; HashMap resources = new HashMap<>(); - resources.put(Symbol.ANIMAL, 90); + resources.put(Symbol.FUNGUS, 2); Requirement req; try { req = new QuantityRequirement(resources); From 4e3e754b402b634d1704db87729b1e5da083dbbe Mon Sep 17 00:00:00 2001 From: Francesco Caracciolo <67018178+FrancescoCaracciolo@users.noreply.github.com> Date: Sat, 13 Apr 2024 17:44:14 +0200 Subject: [PATCH 65/68] Update model UML diagram --- deliveries/UML/class_diagram.mmd | 9 ++++++++- deliveries/UML/model.pdf | Bin 101277 -> 103792 bytes 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index ce8066e2..8d97e15a 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -54,6 +54,7 @@ classDiagram EMPTY_CORNER FULL_CORNER CORNER_OBJ + NO_MULT + getReigns() EnumSet~Symbol~ <> + getBasicResources() EnumSet~Symbol~ <> @@ -239,10 +240,13 @@ classDiagram + addPlayer(Player player) void + removePlayer(Player player) void + getCurrentPlayer() Player + + getCurrentState() MatchState + getPlayers() List~Player~ + getMaxPlayers() int + getVisiblePlayableCards Map~DrawSource, PlayableCard~ + getVisibleObjectives() Pair~Objective, Objective~ + + getVisibleCardsBack() Pair~CardFace, CardFace~ + + getPlayersFinalRanking() List~Pair~Player, Boolean~~ # getPoints() int # addPoints() void # setSecretObjective(Objective obj) void @@ -259,7 +263,9 @@ classDiagram # setupPlayers() void # setupBoards() void # nextPlayer() void - + # decideWinner() void + - checkObjectivesAchievement() Map~Player, Integer~ + } class DrawSource { <> @@ -347,6 +353,7 @@ classDiagram + getSize() int + pop() T + poll() T + + peek() T + shuffle() void + isEmpty() void } diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index ba0654690a3b5a337f86ecdc07511efbb523902f..000dbd4cea6b8e7471dc51f3396138d235504e51 100644 GIT binary patch delta 38649 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 So`Z_hW^-1r-gM#SIsXsqIr>uo delta 37304 zcmZ6y1yoht_XSFcg4~Nis~{jC-CdHB(%s!4b>UJM6eJ{sjdXWQNjK6R(jiEQG$=~x zJJ;VEZ+!nZh74Kf?6c0=E9RX03_U8rO;5yqXT-wI#mB|L!_Upb%)!9`{b9ssS9YL?&d+hGvW@YAt@13^kzgY2vKVjL|Uq^T0YAg0KD0V*XMc=?gnY%4H~VC26Tk#5JkVgGTO#hQ>yhrL@SA zlQHGB)iI6ZnWt3r#O_VkU+Y!=UVR$-2$NrqBiuR5{JT5Vex11Zx927THQ75fyRCDI z`W@YNy!!W;z5cuh^{20}{pxM&b>H9n>Z7>++;>GKc2>o&y{<0DR8Dn{nd$M0H$G-| zEfxOeO#~B*?4yT~neljxbE(l+)l(^B9LbAURFoFJtSaljpZvZaF!<2T>LWV8=h1R~ z#`rB*+re2rdBEz!$o@5$MNveZYRnI8XRVKH2q5p5H(MPtJj*{i)yp1P^qU$iWo#%5 z>@5sFB-?dtvfDq(tYOB8M5%Jg3JNa;ELN)O|1-~?40Nqh>+pKz2NXDjNqh8v3VNH#0g2wwjz z?Z&tKOL1}K@4sV47jjfswysY9oP1r0=r1z-b0F1r2mX0SR<`4zex!+Ti|@8@oBYWZ z>a`syz5UMBnXhX@-3+!tXx}8h-`N8LEP68AO1c;N-fQXmU#vgSY#9l*R$}X);uw3q zDMzk747@#4Cl<$)*C~F4a`L0YDU{<4*z}JObB|vWR$h!YC69~4I71Nc%;>%{JLJ;K#_dTaLk?T^0CTQ zxxM{9>W(|}SK<-bP155>x6J)!8dFEEq^rifzBIoJtmB#|ZNIcx7k<=1$42M-Rjk58 zonlPLftzi^p#Pl{TqC9rM_z&d(1r7vkc1otZ`~S|Kbpo zRmu?(dGnRJX;51C>(z$t#f_{4+nAqOG@h(}uheAjosltH-fOrsW;!RKr~K$y`IrIE zg{Q>(DA#4aXq=5$<0^zBSDKVawmU9&9!Ku*m9`nUxXHx>wHTetZFB2S%4b9Ca}45PJ6iaR*5eXUzvhY_ zC77vp;=K(KFng-Yj@hj3#(3+k#M|0I^*mW~E4#b86|`h>OJ8V-oG=Sz>%MSc3&fZG zmN$E@^iH&XY;(6KX_3gZ}}Fm&$f*@nzLef>%d zBOLbzV%!aiYcB%$-Ep1e=^IqS$MZPVk(EwAWWd2 zw%_~aF$^#1>#)0*CW=sGyAkhR9iv3M&V1Zf+Xy4`G|3$ zyfld|K;toCx$2Xv0mpg0{qd&bg=|loy6W?grL8P{pKtU0iKZWrEVTx~u$wRTe7^hX*&XkCvM?o$)s~)db4IvP(c>pEramShe*7o$#gggmH%taz&=2%?ibRY0k3-%pHQGGigxonVvr~yl+kz-rebrDfwuY zXRAA^ng3I!0GYj|BK+;VHw8SUfEy+`Pg6hZ8%mK)mDE=`AoZ5G$3#W2gNB$=s+p#L z{E~VzD^S{uvoh}nzuNwG$3ooU9$!r@Zg@X6Mxshhf&h@K{-wv^Xw>=$;*+pfwpqzXwaVUvOHNwlxc_8NT{3 zhh>PB3Il1zJyk0+scjkR=J!rd$g;jXT*wi)c(F=Sb#zZ~i<9TR6YeetTkcnuzQ_#o zU^nt_?_4#1tY&9dxyU-?J-|hL{AM1%{_VRR(fB^u48!Zh%9!Lj z7PqfA|F}jC#|&D_Jmayd*vz3W)w|z!KcReKEX8}?+Q#c>q(5bBl7x)5lN$rk7f7*O zB={P;#_Jw+IGcSXOW7?Y;`hQbg|#`EBjd-(8YwsQwzT2z6CW_LydyFu7VqAW4Kujn zD}##*q7r*-?$Qkhz7M5N9uN*EGQj&O{jzDe>8-1CzLXHx=!0T`C&x`Q-zdpPdgA+S zRhhD^$+#9S-2OJmZjBKgaO5pDQugVk7^Kx2-rFJKe!|2TkuZ#>X@0@F=b^@w={Po*<P@)VRbuW!N&-j#^JRT+<(w@8TFzv3xHeKT`_ zksBT9f|$GJl#{3K=RUitv_Eq3MY~Phl;$`zC9zspP0PNFf6smyL{R_S)kSI}mzOg) z6q6T4+z^Xp`Yj;#sw)^9S2CFin{NDck%%xs4XDuL-e6rgS+q2mH^h8QCQxmoIkY9z z7t4+0ZE|uC33$=1?bF7DNG&3?91AD-VJh#4fqR!E>Gh9rMss(adpG2wAJGVixVn6# zcfEN0sQV#@(!Ixu<}TX(34B<~E4LKPw?BQ!`tFe%p03%|7yv-**joc&SlLv;eoJZyM?K^D!`&bZ*ik3BTZU}c<=VE zD2kDb)7IaC2uJrEao5ypil&1j(bP&tn`A0xQSh~FnmGpzu>uP|9otPs?jBzZ>`MeC z4N8pDBUDm%YOeEF#}Ii_Vu2BM2-v}l?u}f-*c4WMGSc-|#z1aJh;hL@OHnwZEq&?j zOz<0=FMsSGdML_a5*ju#%RYP6*j-U%nM$~H!mW+tai2qqqjHnwj5_?Gy(8C~wbLwR zYoD;+N)GWc65PYNeYvJ=*|yedLcbuc(LyPR?@hi;QOu|*>#1OTFliprLZx4msh4ky z{cA*Yh)+1R-D+yk%4!k)GiqhEJJdkHEnoVXOIa#s*cnd`N7s3k%`#gn@9(tv*9Fw? zP^*W866;kK8VXiyM6tyg&fv*Rh2>eqyI19WG| zgvjn-fGm<};$f(~!{aFaqhWXAgJ`vKX{?_ow+jq1w;IRTXQ>jBa@#Tv8SxhJV7~^T?P3X38xw+LB z8-I!Jyv|e+9vUGSlVQbt_09d*2lcl_a;x$4Iw3;N?9K0*fs@+p!$O?{Uoe%HASIRY zx#^b_^N&}jxN`Xps~E~>wWzDKS|OJ}=fM|B>B{+*52#u-8M%iu%NouTnCJ{de*vA6 z_^XMh3l{y6)lvpxihQ!)jalcpmUSm4f7M(cWWJtyoJinE0E+b|m1=p6Sy0nT-($4b z|I%QI!X%`e?(P~c@sUs|(?_T`{`O)y#q}@n$sphSac?T{_)%8Ir~;kkP3)(9k~i^; z<7eXwpD~2>{B!DQ=UaI7NgUhoKO$D3}1HJxpF^5m^^ z(uROI(k)q&YO(9=&bLETBjv-W3?*H*pcRSqlgaapqs}*G>(pOE?xe?7IpO6weRvD! zRY1}AMZGSrnWOcHyF%C=rRr2UM!nUkvK>;?7G1Qx$Ell8mFqokYQ`=jlaU&d<)<;C z=5mILc-`p+g3zYj-bu@{R9`Uvmy?r zGpaxC;SIrd5%Fhi7B;TNuBZoYZVFP|w`ZngQlGdFa_(>+CxxE9!DCYD>Y;>*zEIa{ z$-Cm9Rxj+p`OD)DC*r5Yx-Cr5F|+Y-o8%1>MK1o^7UqR68pdA*mY->u-qwlO+)i-v zE=e{#ciQ`Uv2>qr+FUl#NijldLV7mrX}*<#KGAwk6mF|ZPEkVnmk+0?n56TTnIM|| zE140|+BT-Ma#og9`R8)4iyHg0l2M5tt&vAu(mGcMnRdm*Hu5cr-S=dq=7165$a^W~ zHiT`DjgD~by;^o(PQ@ZWU$H0mjSHU#x(BJhqUaot?&&@APg1&v>&JyYD%d4NJe9b^#YVdQ%sYOs)-vf*s=nFW zzRGNiobb1_psM7Q`70}0y=SAHT?4YOwv?z|EB~sVqjh&B7bb{zWn#WGnk)ve4m9-1Tn_&hvYG(z;62KRPykoE;$Hmlik!(sdfD(tZp zj;}8-=f!WLh!3Q|JacCWt&kA+n_Sc-QJz9oh2hL%8CQ= z&ddjxYKYb_X>t4(^@?;nZZS|JXB$r4Z_n3Du~$w@|C%zIx-7P6+fAHx=1$^MFux@g zQ5pI=Bc|hZ$asQt23xtGPGJ%Lz9_Y1DGkxs`QP_nUfRxOU!UwkUo`3$P5xdaqOLDY zP`wPO%f-aZ_5jqy5W7f>=jG2;@e%P~$*9Ls0oVIeCe+8B8KaDww0|Ty5hi~Smp}jd zU0k*YT>toGqGMf^GM|Y@PvV|LEo`=Lx=epe!}@hdxw=Gh=i=5+zOEzF;1uRN-N6Y3 zOYB3o?dvXGCNY1_rZ2V|W7f7!w)tpsj#tEfXH3M5n^#kyELJ&8bF)5jO1(5P-4CvK zZFSM1KfgX?fua2chevsw603_=fXT-Gy;+z)LSgo9l~LGB*WaES0TL2pjwg2|nT39i zQBmKhF;jQj2p}1rja^_gdOirFwo2@&4Num5?9LEie@#VAOms>`-mFODUd(DNWnbv= zh;cP^y4rrj1(o*KZF+sa=?(5Gybcjb!jm8=lGL`9PV!)JKcVnwtnKb|It%UADJk+9LUYBWWA z>{)DOTH{EM_mFZNg&R}F$qR>xCPWUGy@-9p66qj!5MP0!O}_9>$lZF1T2a$WtQ=!s zUwpIb{fZ-gGcQMzDfjLczEwTxdepRMm^;-cq|Og%u8WWfb*p+r-`H5B?stjB0BchE zXhre5>)JghV$SS<^Jf~~CyOzKPivCi#?;{P&^LOiV_!7+EI;IBi+zo|l`!Uid2epZ z%DYj{^>Jw>3fsKZTk-ipo3Uf2sOmUHQk2-~PVyh-ed{g1^X8X)jX$2Jl}mplN9YsT zyz_2lAN{OgXIgR*S^r#v&Q}Vr`9OoXUbNs`n9yuSB!y+iJAu>Pf{i^qeG+5TpMLE- zoo-(f1wY*jXCsnQdXVrZW?tptvlVV%sinB32kcli07ZdOFoosU@epBsDYH}v`&(n!*xyY#XG^B3&MT#tVob$ zTFaSnF+S?=@wBPvWrw5rMnq?uCEM7S7xt#Oi^CG5rX%77ZAD$H3)>%zFaug|7RMmk z7xPZ%Gld9+GVA4Yae_W@k7#WjaU-KYsAy%+U)v@(CRh!%m*MZn<9gSQWYEV{biGzH z8_hNSGc;UiBf>WLW!}Zstw}K4^=t0X7sotYb4DlB8%Mlo|19RVJ4N&RHeIX~eU^`K zF)G$-)hGgsN4i9g#@$Car78Oe8MSz+Po>Gdsb^{|#*}qZqaNHF9U;#qVH>0>N^XBpYkgL>V{qqOUc|<7c9&B>cKH?zW6@dwKa)hEt?^D# z1?$g0Ss#;)jN%@pB@mR9ek+g1{o+tVGj=38ox#lUGcQtvPh)tmOZ{x?$qg=Ng%|vO zP%OU}MuxHd%HPO2x!WnwaITN9uOUZv)zswL^Y-`j;hzQ5T8%qbMeSV zHN>HGyqdD9Ml7`tZ?$=xo{h~Z*qZiID5t(;C!0~Wd|&uFjb)T8@n?Ew^@D_{$!9{E zxoY(kc>|HT68Eink8+Q`_CLG|+GKp2{6Jf3TllxX80f1@?(A(e6c#7Oe59>`UAciTOTuB}{>44`j~Bv?_&_*f1$8RgL~cRL+d&qUV5ZB#j)miu6tHuP zX3p0~B`P)4{o>AP5zQItjB?^v1-HNriVY~8{STaaDK;J*{3)US5f_PZe93~r`nwc?AG=2l z%&zH+bXStTBav2l=1!Uo-z(=BVGp-#>rY2}htG*Z``NT={k>mVB-(lH&zgNM;~KmD zBrOZMY*ME|5%@DD>Z!xz%FXmXW!s?$QB<76`qoDQj4=f}j10JY1(wPO<|=IZmFRnV zDN>_Bgx*G83fLT;bLzZo`ek35)6zJ@pw9@|1}D|HU)Qa~KfkcU$U&4!eiFz&&uQwW zQduJxs2UR<@xIHK5k9-cx)Ue=+Jxw4(l7&mWLhbrwabm(60_t z?{p7XOSdZi6Mu*d>-k`z&{8vMFcYJ}f}<4r!t(6JY6o*3E^gZcv6vSz;xE#hs5m^- zNp&L*alQL5g0$9`s5={-T6h((yZ2Ed-?im^Ti#-eMGywOtJWomAboF5PiZiaB)7#R z^dKA4V?oMr%x%Y7{?dDcbBt?3{*K^eo(eoUMI1{UA4$WX)n(gOBEM4g}pM( z<6~#PU|4>mGL)15B|l7&A9OnQDB0D@(a9C@JJYK>hoL~5fX2@F&0d~4-TUZos1IFL z5Y*&~Vmse9oLe}u(H#G=Z_fxm62Fb%mbVef zA@b&IBaYeIOfc8J+RUz4zB?1u2Q^U48BgtJ81?+e8cH+nN3Nm6K`qT6K0K|x{Pv2= zTXdE+wpGw*ITfm@z{l-Gr{pM-y@6S`#+q-u znVuWGs?%ZVL_AoR##$#(-O_z_SS~L87cU!`*`vjO#upfU_QG!}w&s2EAU}@NW%T1P zVuAOXFSlCOE^t7I=$cNmmdV3`z$$8ehRC0R|7u{LR~|q3BtdE*xM~^I-B!rMvB$^T zKGNqU?wFc}Mb(v5DJVTUF%oezn;%Dwnup_?nANHd?qApQl;@8HWCn_mxkNxe@*q-Z z8iG&`1b%TJ)z#fdUd1f3CqwTPC)zikDHVq6=JH_ZG}Ean+?deS*++~VbGTo=XDud+ z&$jBLaCNTeK|IQUa0>+;S$o3dHW`W8m(#(U?|3}ex|79U_1BX zky}+W^&$kBGXaQvx_mbrlB#kd&!8t_pMoa-%DB z5^De4^To#-2X1r^BGWKc$AyFOC=^C8GOnc`NX*THyUY!PaoJpwW@~FaU%Cis#32r?yc~kL*O7J)Y-@(tg z59IEdaJ^h~m3Kpe#J>ky7oXAKMb!R`|oVJ@9y^-^=Rp>|5WXJd- z%e@&c&bK8>w;c5sx>f=(@r563UgX?wgIYX-Pw~|`n4TB#3!)vBs&}|lUk^embTRG- z5|{_3`WLoFb}86mgz(XJyz$Qoq5g627L`=j2w$Dp_6&ly>Mq5z9Oc49=}>lIl@^Q} zD0b^AIO(aZeLSwI?bK8w3u1tS>gs2Ue?eL2NAsU#nTaLFUocDQN!S{Te2!n$I(8Cd zMs0Wq0*2lvMlER}XTckb*`X;KL!+#Rea>e~yJ;wEB6h*L;q0Szrmz+|q9wZeJ3osa z@~UI+5ifReN4=-0-MYaLo$tQq7q%^RF*i@}`E@f48k zd?~M%*~YosDC>aHiC{qOzdKef+wdx5R?TS$c?F2{HJjLv8x-tKB^RV_5;}xw{S4$Y zGROAf_^=i@OrbgR#yR?qXJ%-jf&&U;GupNrZ!^Wjq^sOu$-tq?y6nI`LDLSF zwdk6({ve;$kg@oOS$k0OeAjn*h3j+7I+R4PnAZ~*BROXQ_NYTGtX%1xhbxDu;jp4O z3XGOqE;qe}8uLl55cf=EedyNqcfYz*L+SU^7y^&-BikkX+uV2zUW(~^QKyzF*kdF` zdsLjRm5>&^jH2+b5f6jY$zR1_E4tG1V%R@7pKVmyyI(KqX@_Vc`{a`6){-=!MaPRV z^D*wzS#p%t*Xn@P^84Fyp;whVsBbO%HyxnIW_s4uqSAiA2-%@;YNi?!hOBBG*pl|F z_#KOrQOfFykIlX$T1br*w>=VWJpX>yT=2{Nc|yK`XMBOETs(DIWc}P(t7iR15F>YH zLzMrZ0R$F_yuD#t`MJv*iNm@3OJD3ublzTRj^eE+=YEch?{YI`5xnyRWgu1B<`_kh zkNH?=v}V{esag0VrA0Q|!nVqAacKP1|H-Qzgh z&7NKJOfqD$zo(%6?@pq*bhu0xK@8(qp@|SEvX7UoJhym+A}?Od4YR>2HC!ep-{4Hy zA;`R6Bc+UUDzx?F+a3192$TwXN36vqUdo5t(TeMzj5K0}V$1R^@LJZTE%49SOg^fe zm#9*#2~E|pW-v<=*Rv{A$dx0^huH8QB)JU?D!V)2B|O=6ZLi3`n@mxB*LGo~pYThN z=1Xqz+RoE`{!wgMhxtT@!iADN*)InTRNI7zit=D{W_XRjxS>rRs!rA(=QBd_he6sq z#?YG2;mOJ#x2lKwTe3C?jFeE*U$sNCD~PQ~uK9lC*J#T}WzA43TqaN)1R}8X$|s1Y zP+G!xls>(F2)atgn;KuX?z1l#vJ0HVe_yNuMiF6YsVifTKDO@`T}udahVPWWxhyY) z=XwWge$qLlDOaOdL)A^^r6-Z5J#kXgCU7Bu(WjdC4c$x1VaU5(h@XEqZDD`Z<@M+K zSws9!b^e^Ajr{2mico3KE*5cbv>QCSdi37OOVp@x5q8L2XSZxd@|Tl8t@YFA&#*p; zisy~>Rx1>btqRZ0Phw%)jGyZ*3O&08`CBuED;|tC+O581aYWHL!pmkFo$hb@vgoVa zTGgOftK)P3O-n6g?#DwM5h18NaZzn+Q?0{A3zhVxL;d@9Ld2{#GH9@Ez>ry0J)7s% zp|@Vtp>ek5^DiygKeRE6R4F>EyPy2AICx5_&=(xq_z*FS7_8ZLC%L9j*fXSaHZS zQq`ufSNTsWDDv+)~wiL0L6CXp%S=J6GQC#+=r?fMJ zwxCViq*YZpnTve=*kftD2lS8hzNJib@0wq7PSi_L<)XBaezr()Pxo!(z6;hnEcRRj z82IcTh(hX!Ru704Ao^3XrYKF+#L?n?dmTQ0Mk$-<-b=i5)7b5)ZwMl4@)msG`WG)t z1#6pIyn|R5@0w;V&nunhTD{^0FYzfabVjjf_XPxV-t$SUKfd07XwuG>@sE7Mgl zuct>)>AsIPHL~cxAV)nCnYJ$$o&2&t9KfinTht;&_80U;`v3D<9TdmEDvgTF7mf*PVhrCxqLXzG`C_sT7$y|9d( zWg(N{pG9{l$dS$2^Qi@A-pZoyQ0MKnWdnqk7Wp{+9r``Sgz&mSW2n`5;DYPD%H84H zDz`<{!&d+deuhsXe`1NQ}A)*EZ&)A%Q}9k8bxOXt(dO;%12j}M^=lO zXVl)LVTinyzoM-704j9Fr^#L&(I)y#{@9=;QFCWEddr4q6>dbrKK8Fep$1(ZR+Inu zc&2`>G&qqoCgd|Tpt{bBgu^U`PAE+56kr7~NB-CBY%8i+AGe%1MP zJcJ$p8T0eo+2xn+qn|D2lf<*EKWh~ZNt)FLf zNK;67e8^wYWzUhc3vQ;+2tqCBWW61ep{x_lvge)&G?(G3fB)@p#IHz=g51SM&yZJ9 zn6bTDHX5a^RW9JWQmRdqJ9)3PL$=Nkd$G7`=4sTIDUzV2BRwx%9n&u)!%J*P?QMdp zsvfT^nx3JWKPSJi?KC41i`Y?`+bTok6(pUSxm>UGd^Jywg)SrzqY0~a`uwLmK5}a( zN55tNSUGx(m3$m&w~X*y6thk^tDa%z`y2hWxo`-zr2lCq%t|wIn(M;Sy}^o+rKy)) z_;{fx^2=v6?0R!vzGHP*%^~ecTF3|pe1~wm5v0c?2f%waM|UNfwY z%q!R4HPqK?o&2IIbD=4+OjIj_TS34vL|yb-k>;=7J^w2iZB-LWYu>2AxS7@GBQs2v zJ&PkKw(VLy$E>%c#y2T`6V$vtTuB+qcvC378tuQg=g{EbBkt+3vo!YTHCBhz^T@a! z3<7t@vNf1kk7AK#Leh<2dJlE9L9=u8j~)DQ|xbQC)6uftc)2AkjdB zLJZZAm+L)PPLn?~d>>w1g9w1oF7U&9Mn31dh9B}1$Q$>?T9_{uxi=F0%gggVB#kO+ z+ULpAe>1n+kS(4g@NU=TkKJ}-d)oPdc+a5D`yS?$?|r$b$$k%G?r!B!dkt2?Kl`84 z$_ol7MBWiQW(J@q+5P!)dCO^Le|cGn7wXRE-&NL=CVEU966!BTTZheAmK|0va&k#b z_n7u9j$0wCmI;%u3Gu4r3HXGe~zsNsXe64L>9A8D)i^oO4Zu+IO8{q%&Zte*l;b$h>y677i)Q` ztexfZ$40X!=RR5pg-sjz4zWz4aul7pO$A%yEf;a0?I#vR_06q!`uFx}V; z8sNE96BX?q-}U(rOM#g;Vr9N9KMGQJIm)?!-i7tuI@yhx?#@Go%O|kLRC108VILbo}3CuRJOS|5uPC|VnyP{3# z`=nTs`8KgU+ie21QD2lqjD=jIxI~qLz2%Sn8yy!@+LW}hdwy&4tcJ~Bs%7ml!sjxw z+ji`Ci@MhdR4%_YpUAc8yy4+Vl`h=<{5nlf#ftaZvTy?bnvk*W{i7M~w7xgLqzYf| zxy83J3a@BCjHn3>s0bb}X4kqEb!4I1Hw z$R;sTUdc>EHlfCsN8ZBQoqHNOX&Qq+Tk~Dpjz)g{nD3qp&99kfodEyz;Xl z=ZHhRtnN7frCl8tYCD85;HPrb9&gJFkHO79@96ZZg@Zkw+u$Vfy}ioy-+GU9=9E)A zm_8I|r)k_4PAfG#C+=`+PKiA7uy12l>3`gIT4xO3wG^Lk~4-w0?f4^oW}7 zDAF)_btPvOe=RfnkQTU@hT!=Z%g z@Um}rLpmFh7`7*WRu2!ToIYC1{J3>+`;kkE;>`SR+nkN`D%^iE2RBWBMP+%ZakfhH zhU3dZI+rK9jS8e`w(IXFVUQqUnRjLqS9$h={(8s_vE+le-*TA+eJN_y36V$lB{0ZIg#EpCvan*F)F5z z?s$p6CK%<|78j^P$IHQdu0Ow*@V3<_cupI0>8--}{`c}&3W-f0x)1cm7g5%Erf3#-;XFRwO^4YCM^uJt3W0jSqiDVM>!v&XKUtolG5Z2`*!RLX zT?px^K1JSMJ&=DQJFd7htEEn>G;b~3M)Q~Aq1kjEbrr|@d)nHj_?L$HyjW7XttU#- zq&4@cIdw7K2{P%_o=_CW-sLM8*_y9RB+zLM{}%I{akfe*W7GH^r!Pabq=RLWQ}0b| zYE&G?@Wyg`yY5jH$qUWI!)X@FC>%-!JWtV#l8f z_6LZg=Z}?kTc4gokRp&jqUsy9p`rRLP4Jcz>(5y>mL&Z0yJQrl50`tiOH*d~q=NJ} z!va4{G>IvRxGu)V(G9Fl6A$gjBGv+2Nl*^8vTtUsE5hb3tppcU`8m(}7M}RFs9)AW zfE4URkE`P72%Z)SQpRu%{G8)kyO}gB#5D+&{pq43&ZQ^$8vBr+Tm7G%{6*caEc$J~ zCc`4OMMml34`s@h$6KFX-xRs|sZEFZvnVuU--xaagAS%o0`}^j9u>Z!IVaBvK)ssl zV==h6g?#yGICg&2R~QoRzw?d!SDg)XV{WkVnL2eVn0_7LCharO#?l-y@LeB=r6as2$zvLwKvrg%mj@h1e)QJ^YqB5te*`371A_4!{P|wI$V-c}FjH`|*Q5FaB zZ_~w=p~I2iLKorK5t5V8sVNqGG5W9y9WpU!mrV$3mG0}pa$hU@9$SbBb*m0lvdShr zp&4N{#!)@-pM3-gNz-Xc3ftoeF_w`(&ocPmBTzJ#`nI)$;dk}NGRmV+p_HeVKkp@{ zb91qm9ku+Gj;1Ke`fxHQYZu&CaYIundr=-Q%X80!(ob7ES`ej;r}lH89%Cp;JGS+P zyg4j!_TYsb&a49`+38&sf1=*zr>C;c92O^7L)8L`t)MXMA}%K@`Fx`Wnu7N`i!GrVp#b!^PM zHmL9l6V8w2tkcsr7)Z{iiHu)Fio9U0Ua&j(P5M0v0?^2s5y?0*GyS=pX()xS} zN+n(Rv4_c|0rb&BD)l?Fg+@&WJ&Jq!WznN8(hTp>(`d}Q+5^pqfIP^>l~dC!)6&fN zU{96rNczVd1R};imxt@DH^=R>>nK^JQCFzA_(Mql(UEWI3mLK_%$6SH$GV!d#5uv< zv#{Y!bc+jcd(2GvE)!p1Yg9^0);b$YmpvIM;E!pB&~Mt|S6%XFZ$0;-F1W{`74a(O z53+`zPgej#QfM0n3ZjxNQWxE^T{;>j1!SA2&fJQ*|a?qnuBpzh|kwVjj|8Fep2+IP{~bh93TFfSHG>f z3QteSt9OTv!;5i05cW?M^D&<>%8kC{-zcwXdG4Z6HG2tt1L^3m8#y@dzn(U<)hiar z7XR8)^RxWWpLpb>#i3jj#<(_X1sjan?jDNqwW#`we18U+VBga)X!v{QZBIWnjnE2Z z8F{RlkTkQ;?x(L0@H>ozO%>>DL!Zn#vpCQK9*-#^4`^nVvOct8e(=H!f%!;5So$chm zfA6dYJ;b+iw)}tpvlaB4i#Z78ueim*%g4sSFDL*+1s1Wm1OSDgCN{4iH*gj7#NrYJ z-~O3AKwQWZi<<+~38C-u0+_HToSTzTObqSQ#oQjB6LhL@fi7VZ=r$i%MBnE6&uuQC z=E?=$iJ))ugJKcL88>(=igqRdyhI_B2ORt}1%ZqhWb%R*F>UZtkN}e#nE9~+C2BJ4PrJ0y1 z;sHScT;Qb)DOeSUfp{6{Jv;zYRtuCrg8>U!$m9j%vf993f&g$mhfF?@`CJ<)NfH1| zImqM(&T`t|ffNB4N1IR)8huqwj1{CQh+^?_ zfJFr@5Gn)1R9;%?rpB1kNuIU=K2nUnpX708teLXcB{g4i!aAPC%fF0Ab=VO#Xl6>ry4dG090+*@|8tk07sRS^0W)r6dT??cu zLm_EECMW37&;pJxppY~nlMBRXY5`#tC?vGW4UDz40KO^|la>SEkbwaKZ3Qr|3d0om zmyT9#QScYKdQ(Rc6sr+n3jWJSq>d;Um4$&B=&H9m`f6-AD=^R%1?kUWAYWGz+R3pl z0yxUSfQOy}x*a?85I|TS2Bh>MlLy4;BLKbv4BRt-OkQAY;05L(bKO7@-H7al-k=ED zZ>^ycAvgbj_RGluCgfOwh>;lZg7zC~qy!%8!XVyu!s6!vD#kkC5Hf3woe24P|9_83 zLTNG~2km+U;Ef5ioDcjo(FM`^1VGOeGWkKjsV*=wfB{xBM=X8;kZPs_SPkKE<_TsZBp@eR~6bmnK0GU*lN}#|PT4m_~S~cL{1hSDP&@w9r5UB|V zJyuFU$rKKy1=E}jys#Dl23l~CWUU0K%%IiQ4uDA;4jgQhp(bgx@dD@Ca4b$PKyAql z+-=FAUWv5z0{uF0aMw;5BwN6MgPj9N)P;i$J0;*?35Vtw?byj)1X%0ALA|{a5VL|- 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 YE`Q+OX74-j^`fx(l1qN-y7zDXe+`E^9{>OV From f129ec1b23fb629ce659e526bc6c4e0cbaef288b Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Sun, 14 Apr 2024 00:23:24 +0200 Subject: [PATCH 66/68] Add tests for Player class and change MatchTest - Add PlayerTest that tests the Player class - Change few utility MatchTest methods to make them protected static, to make them available to PlayerTest --- src/test/java/it/polimi/ingsw/AppTest.java | 20 ---- .../it/polimi/ingsw/gamemodel/MatchTest.java | 24 ++-- .../it/polimi/ingsw/gamemodel/PlayerTest.java | 110 ++++++++++++++++++ 3 files changed, 122 insertions(+), 32 deletions(-) delete mode 100644 src/test/java/it/polimi/ingsw/AppTest.java create mode 100644 src/test/java/it/polimi/ingsw/gamemodel/PlayerTest.java diff --git a/src/test/java/it/polimi/ingsw/AppTest.java b/src/test/java/it/polimi/ingsw/AppTest.java deleted file mode 100644 index dcf3b800..00000000 --- a/src/test/java/it/polimi/ingsw/AppTest.java +++ /dev/null @@ -1,20 +0,0 @@ -package it.polimi.ingsw; - -import static org.junit.Assert.assertTrue; - -import org.junit.Test; - -/** - * Unit test for simple App. - */ -public class AppTest -{ - /** - * Rigorous Test :-) - */ - @Test - public void shouldAnswerWithTrue() - { - assertTrue( true ); - } -} diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java index 7c1fccbb..6b23bed4 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -1,6 +1,5 @@ package it.polimi.ingsw.gamemodel; -import java.security.spec.ECField; import java.util.*; import javax.management.RuntimeErrorException; @@ -681,7 +680,8 @@ public void decideWinners() { assertEquals(player1, ranking.get(1).first()); assertTrue("Player is not marked as winner", ranking.get(0).second()); assertFalse("Player is marked as winner", ranking.get(1).second()); - } + } + // Private helper Methods private DrawSource decideDrawSource() { Map visible = match.getVisiblePlayableCards(); @@ -712,7 +712,7 @@ private void initializeAllEqualDecks(int initialsNum, int objectsNum, int resour resourcesDeck = createDeterministicResourcesDeck(resourcesNum); } - private static GameDeck createDeterministicObjectivesDeck(int size) { + protected static GameDeck createDeterministicObjectivesDeck(int size) { // Initialize Objective deck GameDeck objectivesDeck = new GameDeck<>(); Objective objective = null; @@ -731,7 +731,7 @@ private static GameDeck createDeterministicObjectivesDeck(int size) { return objectivesDeck; } - private static GameDeck createDeterministicInitialsDeck(int size) { + protected static GameDeck createDeterministicInitialsDeck(int size) { // Initialize Initial Cards deck GameDeck initialsDeck = new GameDeck(); InitialCard card = null; @@ -745,7 +745,7 @@ private static GameDeck createDeterministicInitialsDeck(int size) { return initialsDeck; } - private static GameDeck createDeterministicResourcesDeck(int size) { + protected static GameDeck createDeterministicResourcesDeck(int size) { GameDeck resourcesDeck = new GameDeck<>(); ResourceCard card = null; for (int i = 0; i < size; i++) { @@ -762,7 +762,7 @@ private static GameDeck createDeterministicResourcesDeck(int size) return resourcesDeck; } - private static GameDeck createDeterministicGoldsDeck(int size) { + protected static GameDeck createDeterministicGoldsDeck(int size) { GameDeck goldsDeck = new GameDeck<>(); GoldCard card = null; HashMap resources = new HashMap<>(); @@ -812,7 +812,7 @@ private GameDeck createInitialsDeck(int size) { return initialsDeck; } - private ResourceCard generateRandomResourceCard() { + protected static ResourceCard generateRandomResourceCard() { EnumSet reigns = Symbol.getReigns(); EnumSet corners = Symbol.getValidCorner(); try { @@ -826,7 +826,7 @@ private ResourceCard generateRandomResourceCard() { } } - private GoldCard generateRandomGoldCard() { + protected static GoldCard generateRandomGoldCard() { EnumSet reigns = Symbol.getReigns(); EnumSet corners = Symbol.getValidCorner(); EnumSet multipliers = Symbol.getValidMultiplier(); @@ -845,7 +845,7 @@ private GoldCard generateRandomGoldCard() { } } - private InitialCard generateRandomInitialCard() { + protected static InitialCard generateRandomInitialCard() { EnumSet reigns = Symbol.getReigns(); // Generate a random number between 0 and 3 int index = (int) (Math.random() * 3); @@ -857,7 +857,7 @@ private InitialCard generateRandomInitialCard() { Set.of(randomSymbol(reigns)))); } - private Objective generateRandomObjective() { + protected static Objective generateRandomObjective() { EnumSet resources = Symbol.getBasicResources(); try { return new Objective((int) (Math.random() * 2), @@ -867,7 +867,7 @@ private Objective generateRandomObjective() { } } - private Symbol randomSymbol(EnumSet validSymbols) { + private static Symbol randomSymbol(EnumSet validSymbols) { int size = validSymbols.size(); // Generate a random number between 0 and 3 int index = (int) (Math.random() * size); @@ -900,7 +900,7 @@ private void initializeBlankMatch(int maxPlayers, int initialsNum, int objective } } - protected void initializeBlankMatch(int maxPlayers) { + private void initializeBlankMatch(int maxPlayers) { initializeBlankMatch(maxPlayers, 4, 10, 40, 40); } diff --git a/src/test/java/it/polimi/ingsw/gamemodel/PlayerTest.java b/src/test/java/it/polimi/ingsw/gamemodel/PlayerTest.java new file mode 100644 index 00000000..c614bb8e --- /dev/null +++ b/src/test/java/it/polimi/ingsw/gamemodel/PlayerTest.java @@ -0,0 +1,110 @@ +package it.polimi.ingsw.gamemodel; + +import it.polimi.ingsw.exceptions.WrongStateException; +import it.polimi.ingsw.exceptions.WrongTurnException; +import it.polimi.ingsw.utils.Pair; +import org.junit.Test; +import static org.junit.Assert.fail; + +public class PlayerTest { + // No Player method is specifically tested since the only actual action carried out by all of them is to check + // if the calling player instance corresponds to the match current player (this == match.currentPlayer()) and, + // if so, call a corresponding method of Match and/or of Board (e.g. Player.chooseInitialCardSide() just calls + // Match.setInitialSide()); if the player doesn't correspond to the match current player, a WrongTurnException + // is thrown: this occurrence is tested + + @Test + public void notCurrentPlayer() { + GameDeck goldsDeck = MatchTest.createDeterministicGoldsDeck(40); + GameDeck resourcesDeck = MatchTest.createDeterministicResourcesDeck(40); + GameDeck objectivesDeck = MatchTest.createDeterministicObjectivesDeck(10); + GameDeck initialsDeck = MatchTest.createDeterministicInitialsDeck(10); + + Match match = new Match(4, initialsDeck, resourcesDeck, goldsDeck, objectivesDeck); + + Player player1 = new Player("A", match); + Player player2 = new Player("B", match); + Player player3 = new Player("C", match); + Player player4 = new Player("D", match); + + try { + match.addPlayer(player1); + match.addPlayer(player2); + match.addPlayer(player3); + match.addPlayer(player4); + } catch (WrongStateException e) { + throw new RuntimeException(e); + } + + PlayableCard card = MatchTest.generateRandomResourceCard(); + Objective objective = MatchTest.generateRandomObjective(); + Pair pos = new Pair<>(1, 1); + Player NotCurrPlayer = match.getPlayers().get(1); // the current player is the one at 0th position + + // Verify that drawInitialCard() throws a WrongTurnException when the caller is not the match current player + try { + NotCurrPlayer.drawInitialCard(); + // An exception should be thrown here + fail("WrongTurnException wasn't thrown but should have been"); + } + catch (WrongTurnException e) {} + catch (Exception e) { + throw new RuntimeException(e); + } + + // Verify that chooseInitialCardSide() throws a WrongTurnException when the caller is not the match current player + try { + NotCurrPlayer.chooseInitialCardSide(Side.FRONT); + // An exception should be thrown here + fail("WrongTurnException wasn't thrown but should have been"); + } + catch (WrongTurnException e) {} + catch (Exception e) { + throw new RuntimeException(e); + } + + // Verify that drawSecretObjectives() throws a WrongTurnException when the caller is not the match current player + try { + NotCurrPlayer.drawSecretObjectives(); + // An exception should be thrown here + fail("WrongTurnException wasn't thrown but should have been"); + } + catch (WrongTurnException e) {} + catch (Exception e) { + throw new RuntimeException(e); + } + + // Verify that chooseSecretObjective() throws a WrongTurnException when the caller is not the match current player + try { + NotCurrPlayer.chooseSecretObjective(objective); + // An exception should be thrown here + fail("WrongTurnException wasn't thrown but should have been"); + } + catch (WrongTurnException e) {} + catch (Exception e) { + throw new RuntimeException(e); + } + + // Verify that drawCard() throws a WrongTurnException when the caller is not the match current player + try { + NotCurrPlayer.drawCard(DrawSource.FIRST_VISIBLE); + // An exception should be thrown here + fail("WrongTurnException wasn't thrown but should have been"); + } + catch (WrongTurnException e) {} + catch (Exception e) { + throw new RuntimeException(e); + } + + // Verify that playCard() throws a WrongTurnException when the caller is not the match current player + try { + NotCurrPlayer.playCard(pos, card, Side.FRONT); + // An exception should be thrown here + fail("WrongTurnException wasn't thrown but should have been"); + } + catch (WrongTurnException e) {} + catch (Exception e) { + throw new RuntimeException(e); + } + } +} From acda5004833125cbb262d52b90c415c989820fd5 Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Sun, 14 Apr 2024 00:28:50 +0200 Subject: [PATCH 67/68] Add/change Player javadoc, remove useles Exception throwing from Player and Match, change MatchState constructor to protected --- .../java/it/polimi/ingsw/gamemodel/Match.java | 38 ++++++++----------- .../it/polimi/ingsw/gamemodel/MatchState.java | 2 +- .../it/polimi/ingsw/gamemodel/Player.java | 30 ++++++++------- 3 files changed, 33 insertions(+), 37 deletions(-) diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index 7220bfd7..51ef5290 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -1,15 +1,11 @@ package it.polimi.ingsw.gamemodel; import it.polimi.ingsw.utils.Pair; - -import java.sql.Array; -import java.util.*; -import java.util.stream.Collectors; - import it.polimi.ingsw.exceptions.*; +import java.util.*; /** - * Represents the match being played by {@link Player} instances, therefore implements a slice of game logic + * Represents the match played by {@link Player} instances, therefore implements a slice of game logic * using drawCard(...), setInitialSide(...), setSecretObjective(...), proposeSecretObjective(...), etc. * Other methods serve the purpose of being called by {@link MatchState} subclasses in order to notify the change * of the current game state or trigger some changes in the match, such as setupBoards(...), @@ -250,7 +246,7 @@ protected Pair proposeSecretObjectives() throws WrongState currentState.transition(); return currentProposedObjectives; - } catch (Exception e) { + } catch (DeckException e) { throw new RuntimeException(e); } } @@ -261,7 +257,7 @@ protected Pair proposeSecretObjectives() throws WrongState * Note: Called by the current player * @param objective the accepted objective by the player (NOT the discarded one) */ - protected void setSecretObjective(Objective objective) throws WrongChoiceException, WrongStateException, Exception { + protected void setSecretObjective(Objective objective) throws WrongChoiceException, WrongStateException { currentState.chooseSecretObjective(); // Get proposed objectives @@ -489,10 +485,7 @@ protected PlayableCard drawCard(DrawSource source) throws WrongStateException, W // then the corresponding visible card will be null } - default -> { - throw new RuntimeException("Unexpected value of source"); - } - + default -> throw new RuntimeException("Unexpected value of source"); } if (goldsDeck.isEmpty() && resourcesDeck.isEmpty()) @@ -547,7 +540,7 @@ private Map checkObjectivesAchievement() { p.addPoints(firstObjective.getPoints() * firstObjective.getReq().timesMet(board)); p.addPoints(secondObjective.getPoints() * secondObjective.getReq().timesMet(board)); - // Count the number of achieved ojectives by the player + // Count the number of achieved objectives by the player if (secretObjective != null && secretObjective.getReq().timesMet(board) >= 1) numAchievedObjectives++; if (firstObjective.getReq().timesMet(board) >= 1) @@ -570,8 +563,10 @@ protected void decideWinner() { List sortedPlayers = players.stream() .sorted( - // Create a comparator that firstly sorts for player points - // and in case of same points, for the number of achieved objectives + // Create a comparator that firstly sorts based on player points + // and secondly, in case of same points, on the number of achieved objectives + // Please note: reversed() since the default sort is ascending (min first), but the expected + // results requires a a descending sort (max points/objectives first) Comparator.comparingInt(Player::getPoints) .thenComparing(achievedObjectives::get) .reversed() @@ -581,17 +576,14 @@ protected void decideWinner() { Player bestPlayer = sortedPlayers.get(0); int bestAchievedObjectives = achievedObjectives.get(bestPlayer); int bestPoints = bestPlayer.getPoints(); - boolean winner; + boolean isWinner; for (Player p : sortedPlayers) { - winner = false; - - // If the current player has as many points and as many achieved objectives as the winner, then they're - // winner too - if (p.getPoints() == bestPoints && achievedObjectives.get(p) == bestAchievedObjectives) - winner = true; + // If the current player has as many points and as many achieved objectives as the winner, + // then they're winner too + isWinner = p.getPoints() == bestPoints && achievedObjectives.get(p) == bestAchievedObjectives; - playersFinalRanking.add(new Pair<>(p, winner)); + playersFinalRanking.add(new Pair<>(p, isWinner)); } } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java index afccf4ae..2eee9fe0 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java @@ -14,7 +14,7 @@ public abstract class MatchState { Match match; - public MatchState (Match match) { + protected MatchState (Match match) { this.match = match; } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Player.java b/src/main/java/it/polimi/ingsw/gamemodel/Player.java index 7bc1a6fb..dd1599ee 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Player.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Player.java @@ -4,12 +4,10 @@ import it.polimi.ingsw.exceptions.*; /** - * Represents each in-game user, so acts also as a gateway receiving input - * by the Controller. + * Represents each in-game user, so acts also as a gateway receiving input by the Controller. * It's also responsible for the board's logic, which is a slice of the game logic. */ public class Player { - // TODO: Fix exceptions private final String nickname; private final Match match; private int points; @@ -33,19 +31,24 @@ public Player(String nickname, Match match) { } /** - * Places a card on the player's board, on the give side and in the given position, - * assuming it's a valid one + * Places a card on the player's board, on the give side and in the given position, assuming it's valid. * @param coords x and y position in which the card is played (where 0, 0 is the initial card) * @param card the card to be placed * @param side whether the card should be placed on the front or on the back */ - public void playCard(Pair coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException, WrongChoiceException, CardException, Exception { + public void playCard(Pair coords, PlayableCard card, Side side) throws WrongTurnException, WrongStateException, WrongChoiceException { if (match.getCurrentPlayer().equals(this)) match.makeMove(coords, card, side); else throw new WrongTurnException("Only the current player can play cards"); } + /** + * Gets two objectives from the match objectives deck considered to be secret. + * @return a pair of objectives + * @throws WrongStateException if called during the wrong match state + * @throws WrongTurnException if called by the player when it's not its turn + */ public Pair drawSecretObjectives() throws WrongStateException, WrongTurnException { if (match.getCurrentPlayer().equals(this)) { return match.proposeSecretObjectives(); @@ -55,11 +58,12 @@ public Pair drawSecretObjectives() throws WrongStateExcept } /** - * Get an initial card from the match. + * Gets an initial card from the match. + * @return an initial card * @throws WrongTurnException if called by the player when it's not its turn * @throws WrongStateException if called during the wrong match state */ - public InitialCard drawInitialCard() throws WrongTurnException, Exception, WrongStateException { + public InitialCard drawInitialCard() throws WrongTurnException, WrongStateException { if (match.getCurrentPlayer().equals(this)) return match.drawInitialCard(); else @@ -67,12 +71,12 @@ public InitialCard drawInitialCard() throws WrongTurnException, Exception, Wrong } /** - * Choose the initial card side. - * @param side the side of the initial + * Chooses the initial card side. + * @param side the side of the initial card * @throws WrongTurnException if called by the player when it's not its turn * @throws WrongStateException if called during the wrong match state */ - public void chooseInitialCardSide(Side side) throws WrongTurnException, WrongStateException, Exception { + public void chooseInitialCardSide(Side side) throws WrongTurnException, WrongStateException { if (match.getCurrentPlayer().equals(this)) match.setInitialSide(side); else @@ -86,7 +90,7 @@ public void chooseInitialCardSide(Side side) throws WrongTurnException, WrongSta * @throws WrongChoiceException if called on a drawing source which is empty (e.g. empty deck) * @throws WrongStateException if called during the wrong match state */ - public void drawCard(DrawSource source) throws Exception { + public void drawCard(DrawSource source) throws HandException, WrongStateException, WrongChoiceException, WrongTurnException { if (match.getCurrentPlayer().equals(this)) { PlayableCard card = match.drawCard(source); board.addHandCard(card); @@ -102,7 +106,7 @@ public void drawCard(DrawSource source) throws Exception { * @throws WrongStateException if called during the wrong match state * @throws WrongChoiceException if called on an objective which is not one of the proposed ones */ - public void chooseSecretObjective(Objective objective) throws WrongTurnException, WrongStateException, WrongChoiceException, Exception { + public void chooseSecretObjective(Objective objective) throws WrongTurnException, WrongStateException, WrongChoiceException { if (match.getCurrentPlayer().equals(this)) { match.setSecretObjective(objective); secretObjective = objective; From 302de79bbcff8f457a3ffd2d17f84b7c4bd4076a Mon Sep 17 00:00:00 2001 From: Antonino Ciancimino Date: Tue, 16 Apr 2024 13:35:58 +0200 Subject: [PATCH 68/68] Minor changes, removed wrong exception --- pom.xml | 1 - .../java/it/polimi/ingsw/gamemodel/AfterMoveState.java | 2 +- src/main/java/it/polimi/ingsw/gamemodel/Match.java | 2 +- src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java | 3 +-- src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java | 8 ++++---- 5 files changed, 7 insertions(+), 9 deletions(-) diff --git a/pom.xml b/pom.xml index 56be2d75..82bbc3e4 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,6 @@ 3.6.3 16 - 16 Plugin to execute project --> diff --git a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java index b7f503e4..1e8e85f4 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java @@ -9,7 +9,7 @@ public AfterMoveState(Match match) { } @Override - public void drawCard() throws WrongStateException { + public void drawCard() { } diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java index 51ef5290..aec05567 100644 --- a/src/main/java/it/polimi/ingsw/gamemodel/Match.java +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -566,7 +566,7 @@ protected void decideWinner() { // Create a comparator that firstly sorts based on player points // and secondly, in case of same points, on the number of achieved objectives // Please note: reversed() since the default sort is ascending (min first), but the expected - // results requires a a descending sort (max points/objectives first) + // results requires a descending sort (max points/objectives first) Comparator.comparingInt(Player::getPoints) .thenComparing(achievedObjectives::get) .reversed() diff --git a/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java b/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java index 8829cb30..961c7010 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/BoardTest.java @@ -81,8 +81,7 @@ public BoardTest() { verifyResources(Symbol.PLANT, 12); } catch (Exception e) { - System.err.println(e); - assertTrue(false); + throw new RuntimeException(e); } } diff --git a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java index 6b23bed4..2d396456 100644 --- a/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java +++ b/src/test/java/it/polimi/ingsw/gamemodel/MatchTest.java @@ -26,7 +26,7 @@ public void constructor() { player3 = new Player("Foingo", match); player4 = new Player("Francesco Edoardo Caracciolo", match); - // Verify that Match throws an exception when the initial cards deck has less + // Verify that Match throws an exception when the initial cards deck has fewer // cards than the number of players (3 cards in this case, minus than 4 // players in the match) initializeAllEqualDecks(); @@ -243,7 +243,7 @@ public void drawInitialCard() { @Test public void setInitialSide() { - // Setup a basic game + // Set up a basic game initializeBlankMatch(2); // For each player in the match @@ -882,7 +882,7 @@ private static Symbol randomSymbol(EnumSet validSymbols) { } private void initializeBlankMatch(int maxPlayers, int initialsNum, int objectivesNum, int resourcesNum, int goldsNum) { - // Setup a basic game + // Set up a basic game String names[] = {"Oingo", "Boingo", "Jotaro", "Polnareff"}; Player players[] = new Player[4]; @@ -905,7 +905,7 @@ private void initializeBlankMatch(int maxPlayers) { } private void initializeBlankStartedMatch(int maxPlayers, int initialsNum, int objectivesNum, int resourcesNum, int goldsNum) { - // Setup a basic game + // Set up a basic game initializeBlankMatch(maxPlayers, initialsNum, objectivesNum, resourcesNum, goldsNum); try {