diff --git a/.classpath b/.classpath index 653dfd75..d39d3b10 100644 --- a/.classpath +++ b/.classpath @@ -26,7 +26,7 @@ - + diff --git a/deliveries/UML/class_diagram.mmd b/deliveries/UML/class_diagram.mmd index 4115ea89..b9edabc3 100644 --- a/deliveries/UML/class_diagram.mmd +++ b/deliveries/UML/class_diagram.mmd @@ -1,5 +1,5 @@ --- -title: Stati +title: Model diagram --- classDiagram Match --> GameDeck @@ -8,11 +8,11 @@ classDiagram Match -- Player MatchState <|-- WaitState MatchState <|-- SetupState - MatchState <|-- ChoosePlayerState + MatchState <|-- NextTurnState MatchState <|-- ChooseSecretObjectiveState - MatchState <|-- UpdatePlayerStatusState - MatchState <|-- DrawPhaseState - MatchState <|-- RankingsState + MatchState <|-- AfterMoveState + MatchState <|-- AfterDrawState + MatchState <|-- FinalState Card <|-- InitialCard Card <|-- PlayableCard @@ -35,6 +35,7 @@ classDiagram %%Side <-- Player %%Side <-- Match Side <-- PlacedCard + Corner <-- CardFace %%Symbol <-- GoldCard %% DrawSource <-- Player @@ -54,24 +55,35 @@ classDiagram CORNER_OBJ } + class Corner { + <> + TOP_LEFT + TOP_RIGHT + BOTTOM_LEFT + BOTTOM_RIGHT + } + class CardFace { + <> - topLeft: Symbol - topRight: Symbol - 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~ } class Card { <> - - front: CardFace - - back: CardFace + # sides : Map~Side, CardFace~ + + getSide(Side side) CardFace } class PlayableCard { <> - - points: int + # points: int } class InitialCard { @@ -80,40 +92,46 @@ classDiagram class ResourceCard { + ResourceCard(CardFace front, CardFace back, int points) + + getPoints() : int } class GoldCard { - multiplier: Symbol - req: QuantityRequirement + GoldCard(CardFace front, CardFace back, int points, Symbol multiplier, QuantityRequirement req) - + totPoints(Board) int + + getMultiplier() Symbol + + getRequirement() QuantityRequirement + + totPoints(Board board) int } class Requirement { <> - + isSatisfied(Board bord) bool + + isSatisfied(Board board) bool } class QuantityRequirement { - reqs : Map~Integer, Symbol~ + QuantityRequirement(Symbol simbol, Integer quantity) + + isSatisfied(Board board) bool } class PositionRequirement { - reqs: Map<Pair<Integer, Integer>, Color> + PositionRequirement(Map<Pair<Integer, Integer>, Color>) + + isSatisfied(Board board) bool } 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 - - resources: Map - color: Color - objective: Objective @@ -122,6 +140,11 @@ classDiagram + drawCard(DrawSource draw) void + chooseObjective(Objective objective) void + getBoard() Board + + getPoints() int + + getSecretObjective() Objective + + getColor() Color + + getNickname() String + # setColor() void } class Color{ <> @@ -138,18 +161,26 @@ classDiagram class Board { - currentHand: List~PlayableCard~ - placed: Map<Pair<Integer,Integer>, PlacedCard> + - availableResources: Map~Symbol, Integer~ + Board() + getCurrentHand() List~PlayableCard~ - + addCardHand(PlayableCard card) void - + removeCardHand(PlayableCard card) void - + checkRequirement(Requirement req) bool - + placeCard(Pair, Card card, Side side) Map~Symbols, Integer~ + + getAvailableResources() Map~Symbol, Integer~ + # 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 + # setInitialCard(InitialCard card) void + + verifyCardPlacement(Pair~Integer, Integer~ coords, Card card, Side side) bool } class PlacedCard { + <> - card: Card - turn: int + PlacedCard(Card card, int turn) + + getCard() Card + + getTurn() int } class Match { @@ -157,25 +188,39 @@ classDiagram - maxPlayers: int - currState: MatchState - currentPlayer: Player + - initialsDeck: GameDeck~InitialCard~ - resourcesDeck: GameDeck~ResourceCard~ - goldsDeck: GameDeck~GoldCard~ - objectivesDeck: GameDeck~Objective~ - - visibleGolds: Pair - - visibleResources: Pair - - visibleObjectives: Pair + - visibleGolds: Pair~GoldCard, GoldCard~ + - visibleResources: Pair~ResourceCard, ResourceCard~ + - visibleObjectives: Pair~Objective, Objective~ + - currentProposedObjectives: Pair~Objective, Objective~ + - started: bool + - lastTurn: bool + - finished: bool - + Match(int maxPlayers) void - + setState() void + + Match(int maxPlayers, resourceDeck ) void + isFull() bool + + isStarted() bool + isFinished() bool + addPlayer(Player player) void + removePlayer(Player player) void + getCurrentPlayer() Player - + chooseSecretObjective(Objective obj) void - + getSecretObjectives() Pair~Objective, Objective~ - + makeMove(Player player, Pair~Integer, Integer~ coords, PlayableCard card, Side side) void - + drawCard(Player player, DrawSource draw) PlayableCard - - setupMatch() void + + getPlayers() List~Player~ + # getPoints() int + # addPoints() void + # chooseSecretObjective(Objective obj) void + # proposeSecretObjectives() Pair~Objective, Objective~ + # makeMove(Pair~Integer, Integer~ coords, PlayableCard card, Side side) void + # drawCard(DrawSource draw) PlayableCard + # doStart() void + # doFinish() void + # setState() void + # setupDecks() void + # setupPlayers() void + # setupBoards() void + # nextPlayer() void } class DrawSource { @@ -191,44 +236,54 @@ classDiagram class MatchState { <> + match: Match + + MatchState(Match match) + transition() void - + join() void - + quit() void + + addPlayer() void + + removePlayer() void + + proposeSecretObjectives() void + + chooseSecretObjective() void + + makeMove() void + + drawCard() void + } class WaitState{ - + WaitState() void + + WaitState(Match match) void + transition() void - + join() void - + quit() void + + addPlayer() void + + removePlayer() void } class SetupState{ - + SetupState() void + + SetupState(Match match) void + transition() void } - class ChoosePlayerState { - + ChoosePlayerState() void + class NextTurnState { + + NextTurnState(Match match) void + + proposeSecretObjectives() void + + makeMove() void + transition() void } class ChooseSecretObjectiveState { + ChooseSecretObjectiveState() void + + chooseSecretObjective() void + transition() void } - class UpdatePlayerStatusState { - + UpdatePlayerStatusState() void + class AfterMoveState { + + AfterMoveState() void + + drawCard() void + transition() void } - class DrawPhaseState { - + DrawPhaseState() void + class AfterDrawState { + + AfterDrawState() void + transition() void } - class RankingsState{ - + RankingsState() void + class FinalState{ + + FinalState() void + transition() void } @@ -236,7 +291,7 @@ classDiagram class GameDeck { <> - int size - - cardList: List~U~ + - cardsList: List~U~ + GameDeck(int size) + add(U card) void + pop() U diff --git a/deliveries/UML/model.pdf b/deliveries/UML/model.pdf index 9afe0d48..a9db4c7d 100644 Binary files a/deliveries/UML/model.pdf and b/deliveries/UML/model.pdf differ diff --git a/pom.xml b/pom.xml index 9eecf715..56be2d75 100644 --- a/pom.xml +++ b/pom.xml @@ -74,6 +74,8 @@ maven-javadoc-plugin 3.6.3 + 16 + 16 Plugin to execute project --> @@ -87,5 +89,15 @@ + + + org.apache.maven.plugins + maven-compiler-plugin + + 16 + 16 + + + diff --git a/src/main/java/it/polimi/ingsw/App.java b/src/main/java/it/polimi/ingsw/App.java deleted file mode 100644 index e15eafa3..00000000 --- a/src/main/java/it/polimi/ingsw/App.java +++ /dev/null @@ -1,14 +0,0 @@ -package it.polimi.ingsw; - -/** - * Main Loop - * - */ -public class App -{ - public static void main( String[] args ) - { - String test = new String("Hello world!"); - System.out.println(test); - } -} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/AfterDrawState.java b/src/main/java/it/polimi/ingsw/gamemodel/AfterDrawState.java new file mode 100644 index 00000000..d8289e45 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/AfterDrawState.java @@ -0,0 +1,20 @@ +package it.polimi.ingsw.gamemodel; + +public class AfterDrawState extends MatchState { + + public AfterDrawState(Match match) { + super(match); + } + + @Override + public void transition() { + MatchState nextState; + + if(match.isFinished()) + nextState = new FinalState(match); + else + nextState = new NextTurnState(match); + + match.setState(nextState); + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java new file mode 100644 index 00000000..245790d6 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/AfterMoveState.java @@ -0,0 +1,19 @@ +package it.polimi.ingsw.gamemodel; + +public class AfterMoveState extends MatchState { + + public AfterMoveState(Match match) { + super(match); + } + + @Override + public void drawCard() throws WrongStateException { + this.transition(); + } + + @Override + public void transition() { + MatchState nextState = new AfterDrawState(match); + match.setState(nextState); + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Board.java b/src/main/java/it/polimi/ingsw/gamemodel/Board.java new file mode 100644 index 00000000..667e102f --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Board.java @@ -0,0 +1,81 @@ +package it.polimi.ingsw.gamemodel; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import it.polimi.ingsw.utils.Pair; + +/** + * 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; + private Map availableResources; + + /** + * Class constructor. No inputs taken as the board starts empty + */ + public Board() { + currentHand = new ArrayList<>(); + placed = new HashMap<>(); + availableResources = new HashMap<>(); + } + + /** + * Getter for the hand of the player (which must be composed of three {@link PlayableCard}), which is visible + * to every player + */ + public List getCurrentHand() { + return this.currentHand; + } + + /** + * 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) { + currentHand.addLast(card); + } + + /** + * 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 + */ + 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) { + 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 + * @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 + * @return the points gained from playing card + */ + protected int placeCard(Pair coord, Card card, Side side) { + return 0; + } + + /** + * 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 + */ + public boolean verifyCardPlacement(Pair coord, Card card, Side side) { + return true; + } + +} + diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Card.java b/src/main/java/it/polimi/ingsw/gamemodel/Card.java new file mode 100644 index 00000000..c7968527 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Card.java @@ -0,0 +1,27 @@ +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 { + protected CardFace front; + protected CardFace back; + + /** + * @param side the desired side + * @return the structure of the specified side + * @see CardFace + */ + public CardFace getSide(Side side) { + switch (side) { + case FRONT: + return this.front; + + case BACK: + return this.back; + + default: + return null; + } + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java b/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java new file mode 100644 index 00000000..92b0ad5c --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/CardFace.java @@ -0,0 +1,38 @@ +package it.polimi.ingsw.gamemodel; + +import java.util.Set; + +/* +* Topological definition of a card's side +*/ +public class CardFace { + private Symbol topLeft; + private Symbol topRight; + private Symbol bottomLeft; + private Symbol bottomRight; + private Set center; + + public CardFace(Symbol topLeft, Symbol topRight, Symbol bottomLeft, Symbol bottomRight, Set center) { + this.topLeft = topLeft; + this.topRight = topRight; + this.bottomLeft = bottomLeft; + this.bottomRight = bottomRight; + this.center = center; + } + + /** + * 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 + */ + public Symbol getCorner(Corner corner) { + return topLeft; + } + + /** + * @return the set containing all symbols the center of the card contains + */ + public Set getCenter() { + return center; + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java b/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java new file mode 100644 index 00000000..2c453cb9 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/ChooseInitialSideState.java @@ -0,0 +1,5 @@ +package it.polimi.ingsw.gamemodel; + +public class ChooseInitialSideState { + +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java b/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java new file mode 100644 index 00000000..0699042d --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/ChooseSecretObjectiveState.java @@ -0,0 +1,24 @@ +package it.polimi.ingsw.gamemodel; + +public class ChooseSecretObjectiveState extends MatchState { + + public ChooseSecretObjectiveState(Match match) { + super(match); + } + + @Override + public void chooseSecretObjectives() { + Player lastPlayer = match.getPlayers().getLast(); + + if (match.getCurrentPlayer().equals(lastPlayer)) + match.doStart(); + + this.transition(); + } + + @Override + public void transition() { + MatchState nextState = new NextTurnState(match); + match.setState(nextState); + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Color.java b/src/main/java/it/polimi/ingsw/gamemodel/Color.java new file mode 100644 index 00000000..471a1123 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Color.java @@ -0,0 +1,9 @@ +package it.polimi.ingsw.gamemodel; + + +public enum Color { + RED, + BLUE, + GREEN, + YELLOW +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Corner.java b/src/main/java/it/polimi/ingsw/gamemodel/Corner.java new file mode 100644 index 00000000..48cc4437 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Corner.java @@ -0,0 +1,11 @@ +package it.polimi.ingsw.gamemodel; + +/** +* All the corners of a card, which can contain a {@link Symbol} +*/ +public enum Corner { + TOP_LEFT, + TOP_RIGHT, + BOTTOM_LEFT, + BOTTOM_RIGHT +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/DrawSource.java b/src/main/java/it/polimi/ingsw/gamemodel/DrawSource.java new file mode 100644 index 00000000..37859df2 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/DrawSource.java @@ -0,0 +1,13 @@ +package it.polimi.ingsw.gamemodel; + +/** +* 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 +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/FinalState.java b/src/main/java/it/polimi/ingsw/gamemodel/FinalState.java new file mode 100644 index 00000000..1eb8bc1b --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/FinalState.java @@ -0,0 +1,13 @@ +package it.polimi.ingsw.gamemodel; + +public class FinalState extends MatchState { + + public FinalState(Match match) { + super(match); + } + + @Override + public void transition() { + System.err.println("ERROR: State transition tried in the final state!"); + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java new file mode 100644 index 00000000..7b22a295 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/GameDeck.java @@ -0,0 +1,37 @@ +package it.polimi.ingsw.gamemodel; + +import java.util.ArrayList; +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 + */ + public GameDeck(int size) { + this.size = size; + cardsList = new ArrayList<>(); + } + + public void add(T card) { + + } + + public T pop() { + return null; + } + + public void shuffle() { + + } + + public void isEmpty() { + + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java new file mode 100644 index 00000000..cdf89e71 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/GoldCard.java @@ -0,0 +1,35 @@ +package it.polimi.ingsw.gamemodel; + +/** +* The front side of these cards always gives points, but needs a certain requirement to be met in order to be played +* @see CardFace +*/ +public class GoldCard extends PlayableCard{ + private Symbol multiplier; + 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 + * @param front the front side of the card + * @param back the back side 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 + */ + public GoldCard(CardFace front, CardFace back, Symbol multiplier, int points, QuantityRequirement req) { + this.front = front; + this.back = back; + this.points = points; + this.multiplier = multiplier; + this.req = 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 + */ + public int totPoints(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/InitialCard.java b/src/main/java/it/polimi/ingsw/gamemodel/InitialCard.java new file mode 100644 index 00000000..8efaee8a --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/InitialCard.java @@ -0,0 +1,15 @@ +package it.polimi.ingsw.gamemodel; + +/** +* Every player has an initial card (which will automatically be placed in the center of the board) +*/ +public class InitialCard extends Card{ + /** + * The initial card only gives corners and resources, never points, so we only need to know its topologic description + * @param front the front side of the card + * @param back the back side of the card + */ + public InitialCard(CardFace front, CardFace back) { + this.front = front; this.back = back; + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Match.java b/src/main/java/it/polimi/ingsw/gamemodel/Match.java new file mode 100644 index 00000000..fdd0cc5d --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Match.java @@ -0,0 +1,279 @@ +package it.polimi.ingsw.gamemodel; + +import it.polimi.ingsw.utils.Pair; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Match { + private final List players; + private final int maxPlayers; + private Player currentPlayer; + + private MatchState currentState; + + // All cards decks + private final GameDeck initialsDeck; + private final GameDeck resourcesDeck; + private final GameDeck goldsDeck; + private final GameDeck objectivesDeck; + + // All the visible cards on the common table + private Pair visibleResources; + private Pair visibleGolds; + private Pair visibleObjectives; + + private Pair currentProposedObjectives; + + // Denotes if the match has been started or has finished + private boolean started = false; + private boolean lastTurn = false; + private boolean finished = false; + + + /** + * 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 + * @param initialsDeck deck of initial cards + * @param resourcesDeck deck of resource cards + * @param goldsDeck deck of gold cards + * @param objectivesDeck deck of objectives + */ + public Match(int maxPlayers, GameDeck initialsDeck, GameDeck resourcesDeck, GameDeck goldsDeck, GameDeck objectivesDeck) { + this.maxPlayers = maxPlayers; + this.initialsDeck = initialsDeck; + this.resourcesDeck = resourcesDeck; + this.goldsDeck = goldsDeck; + this.objectivesDeck = objectivesDeck; + + this.players = new ArrayList(); + } + + // Called by the controller + /** + * Method that adds a new player to the match; if the player is already in, throws an exception. + * @param player player to be added to the match + * @throws IllegalArgumentException thrown if player already in the match + */ + public void addPlayer(Player player) throws IllegalArgumentException, WrongStateException { + if(!players.contains(player)) { + currentState.addPlayer(); + players.add(player); + } else { + throw new IllegalArgumentException("Duplicated Player in a Match"); + } + } + + // Called by the controller + /** + * + * @param player + */ + public void removePlayer(Player player) { + players.remove(player); + } + + // Called by the controller + /** + * + * @return + */ + public boolean isFull() { + return players.size() == maxPlayers; + } + + /** + * Method that changes the currentPlayer based on the next turn + * If it is the first turn, currentPlayer gets initialized as the + * first one in the players List.The turn order follows the list order. + * Called by ChoosePlayerState 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(); + } else { + // Get the index of the current player and choose the next one + int currentPlayerIndex = players.indexOf(currentPlayer); + currentPlayer = players.get(currentPlayerIndex + 1); + } + } + // Called by the state + /** + * + */ + protected void doFinish() { + finished = true; + } + + // Called by the controller + /** + * + * @return + */ + public boolean isFinished() { + return finished; + } + + protected void doStart() { + started = true; + } + + public boolean isStarted() { + return started; + } + + // Called by the controller + /** + * + * @return + */ + public Player getCurrentPlayer() { + return currentPlayer; + } + + public List getPlayers() { + return players; + } + // Called by the state + + /** + * + * @param state + */ + protected void setState(MatchState state) { + this.currentState = state; + } + + /** + * + * @return + */ + protected Pair proposeSecretObjectives() { + Objective obj1 = objectivesDeck.pop(); + Objective obj2 = objectivesDeck.pop(); + currentProposedObjectives = new Pair<>(obj1, obj2); + return currentProposedObjectives; + } + + // Called by the controller + /** + * + * @param objective + */ + protected void chooseSecretObjective(Objective objective) { + // Put back the player's refused secret objective + objectivesDeck.add(objective); + } + + /** + * + */ + protected void setupPlayers() { + // Shuffle players List + Collections.shuffle(players); + + // Set players' colors + for (int i = 0; i < maxPlayers; i++) { + players.get(i).setColor(Color.values()[i]); + } + } + + /** + * + */ + protected void setupDecks() { + // Shuffle each deck + initialsDeck.shuffle(); + resourcesDeck.shuffle(); + goldsDeck.shuffle(); + objectivesDeck.shuffle(); + + // 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 + Objective objective1 = objectivesDeck.pop(); + Objective objective2 = objectivesDeck.pop(); + + // Set popped cards in Match attributes + visibleGolds = new Pair<>(goldCard1, goldCard2); + visibleResources = new Pair<>(resourceCard1, resourceCard2); + visibleObjectives = new Pair<>(objective1, objective2); + } + + /** + * + */ + 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); + + // 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); + + } + } + + /** + * + * @param coords + * @param card + * @param side + * @throws WrongStateException + * @throws WrongCardPlacementException + */ + protected void makeMove(Pair coords, PlayableCard card, Side side) throws WrongStateException, WrongCardPlacementException { + Board currentPlayerBoard = currentPlayer.getBoard(); + + // 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); + + // 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!"); + } + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java new file mode 100644 index 00000000..e26251f3 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/MatchState.java @@ -0,0 +1,35 @@ +package it.polimi.ingsw.gamemodel; + +public abstract class MatchState { + Match match; + + public MatchState (Match match) { + this.match = match; + } + + public abstract void transition(); + + 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!"); + } + + public void makeMove() throws WrongStateException{ + throw new WrongStateException("makeMove not allowed from the current match state!"); + } + + public void drawCard() throws WrongStateException{ + throw new WrongStateException("drawCard not allowed from the current match state!"); + } + + public void proposeSecretObjectives() throws WrongStateException{ + throw new WrongStateException("proposeSecretObjective not allowed from the current match state!"); + } + + public void chooseSecretObjectives() 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 new file mode 100644 index 00000000..878c4175 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/NextTurnState.java @@ -0,0 +1,38 @@ +package it.polimi.ingsw.gamemodel; + +public class NextTurnState extends MatchState { + + public NextTurnState(Match match) { + super(match); + + match.nextPlayer(); + } + + @Override + public void proposeSecretObjectives() throws WrongStateException { + if (match.isStarted()) + 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 + throw new WrongStateException("makeMove called when match was not started yet"); + } + + @Override + public void transition() { + MatchState nextState; + + if (match.isStarted()) + nextState = new AfterMoveState(match); + else + nextState = new ChooseSecretObjectiveState(match); + + match.setState(nextState); + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Objective.java b/src/main/java/it/polimi/ingsw/gamemodel/Objective.java new file mode 100644 index 00000000..ec04d950 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Objective.java @@ -0,0 +1,20 @@ +package it.polimi.ingsw.gamemodel; + +/** +* Every player has a secret objective, and at the start of the game two objectives common to every +* player get randomly chosen. The objective asks for a certain requirement to be satisfied and gives points only when +* the game ends, and does not stack on itself (e.g. if an objective requires three feathers and a player has 6, he will receive the points only once) +*/ +public class Objective { + private int points; + private Requirement req; + + /** + * @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 + */ + public Objective(int points, Requirement req) { + this.points = points; this.req = req; + } +} + diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java b/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java new file mode 100644 index 00000000..248d485d --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlacedCard.java @@ -0,0 +1,19 @@ +package it.polimi.ingsw.gamemodel; + +/** +* This class handles the card already placed on the board, since we need to remember which card covers which +*/ +public class PlacedCard { + private Card card; + private int turn; + + + /** + * @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; + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java b/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java new file mode 100644 index 00000000..ff6b1c31 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/PlayableCard.java @@ -0,0 +1,12 @@ +package it.polimi.ingsw.gamemodel; + +/** +* Class that represents every kind of card that can be played during the game. +* All these cards have at least a side (the back) that does not require any resouce to be played. +* @see CardFace +*/ + +public abstract class PlayableCard extends Card{ + protected int points; +} + diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Player.java b/src/main/java/it/polimi/ingsw/gamemodel/Player.java new file mode 100644 index 00000000..695b13ed --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Player.java @@ -0,0 +1,100 @@ +package it.polimi.ingsw.gamemodel; +import it.polimi.ingsw.utils.Pair; + +/** +* Player represents each in-game user. The class also manages the board's logic +*/ +public class Player { + private String nickname; + private Match match; + private int points; + private Board board; + private Color pawnColor; + private Objective secretObjective; + + public Player(String nickname, Match match) { + this.nickname = nickname; + this.match = match; + + //Initialize values + board = new Board(); + points = 0; + + } + + /** + * 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 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 + } + + /** + * 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 + } + + /** + * 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 + } + + /** + * Getter for the player's board + */ + public Board getBoard() { + return board; + } + + /** + * Getter for the player's points + */ + public int getPoints() { + return points; + } + /** + * Getter for the player's pawn color + */ + public Color getPawnColor() { + return pawnColor; + } + + /** + * Setter for the player's color + */ + protected void setColor(Color color) { + this.pawnColor = color; + } + + /** + * Getter for the player's objective + * @see #chooseSecretObjective(Objective) + */ + protected Objective getSecretObjective() { + return secretObjective; + } + + /** + * Adds points to the player + * @param points number of points to add to the player + */ + protected void addPoints(int points) { + this.points += points; + } + + /** + * Getter for the player's nickname + */ + public String getNickname() { + return nickname; + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java new file mode 100644 index 00000000..08abf3f0 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/PositionRequirement.java @@ -0,0 +1,32 @@ +package it.polimi.ingsw.gamemodel; + +import java.util.Map; + +import it.polimi.ingsw.utils.Pair; + +/** +* This class handles requirements involving relative positioning of cards, e.g. three red cards placed in the top right corner of each other +*/ +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) + */ + public PositionRequirement(Map, Symbol> reqs) { + this.reqs = reqs; + } + + /** + * The requirement will be satisfied if the board has cards of the specified faction in the correct relative positions + * @param board the {@link Board} on which the requirement must be checked + * @return wheter the board actually meets the requirement or not + */ + @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 new file mode 100644 index 00000000..f1436fe6 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/QuantityRequirement.java @@ -0,0 +1,29 @@ +package it.polimi.ingsw.gamemodel; + +import java.util.Map; + +import it.polimi.ingsw.utils.Pair; + +/** +* This class handles requirements involving relative positioning of cards, e.g. three red cards placed in the top right corner of each other +*/ +public class QuantityRequirement extends Requirement{ + private Map reqs; + + /** + * @param reqs how many resources of a certain type are needed to fulfill the requirement + */ + public QuantityRequirement(Map reqs) { + 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 + */ + @Override + public boolean isSatisfied(Board board) { + return true; + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/RankingState.java b/src/main/java/it/polimi/ingsw/gamemodel/RankingState.java new file mode 100644 index 00000000..1684ce6b --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/RankingState.java @@ -0,0 +1,4 @@ +package it.polimi.ingsw.gamemodel; + +public class RankingState { +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java new file mode 100644 index 00000000..df254369 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Requirement.java @@ -0,0 +1,15 @@ +package it.polimi.ingsw.gamemodel; + +/* +* 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 { + + /** + * 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 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 new file mode 100644 index 00000000..1b1533ab --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/ResourceCard.java @@ -0,0 +1,16 @@ +package it.polimi.ingsw.gamemodel; + +/* +* Card that does not require any conditions to be played +*/ +public class ResourceCard extends PlayableCard{ + /** + * @param front the front side of the card + * @param back the back side of the card + * @param points the number of points the card gives (must be an absolute value) + */ + public ResourceCard(CardFace front, CardFace back, int points) { + this.front = front; this.back = back; this.points = points; + } + +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/SetupState.java b/src/main/java/it/polimi/ingsw/gamemodel/SetupState.java new file mode 100644 index 00000000..a54ad47c --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/SetupState.java @@ -0,0 +1,20 @@ +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/Side.java b/src/main/java/it/polimi/ingsw/gamemodel/Side.java new file mode 100644 index 00000000..4237e949 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Side.java @@ -0,0 +1,9 @@ +package it.polimi.ingsw.gamemodel; + +/** +* Used to know which of the two faces of a card we want to use +*/ +public enum Side { + FRONT, + BACK +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java b/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java new file mode 100644 index 00000000..35dbd68a --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/Symbol.java @@ -0,0 +1,20 @@ +package it.polimi.ingsw.gamemodel; + +/** +* Contains all the possible symbols a corner can store or a multiplier can have. +* Besides the basic 4 factions and 3 symbols, there is also EMPTY_CORNER, which represents a corner withtout the +* possibility of placing another card on top of it (missing slot), FULL_CORNER which represents a valid corner without any symbol and +* CORNER_OBJ which represents a {@link QuantityRequirement} in which the multiplier is how many corners the card covered +*/ +public enum Symbol { + ANIMAL, + PLANT, + INSECT, + FUNGUS, + FEATHER, + INKWELL, + PARCHMENT, + EMPTY_CORNER, + FULL_CORNER, + CORNER_OBJ +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/WaitState.java b/src/main/java/it/polimi/ingsw/gamemodel/WaitState.java new file mode 100644 index 00000000..517851d1 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/WaitState.java @@ -0,0 +1,25 @@ +package it.polimi.ingsw.gamemodel; + +public class WaitState extends MatchState{ + + public WaitState(Match match) { + super(match); + } + + @Override + public void transition() { + MatchState nextState = new SetupState(match); + match.setState(nextState); + } + + @Override + public void removePlayer() { + //TBD + } + + @Override + public void addPlayer() { + if (match.isFull()) + transition(); + } +} diff --git a/src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java b/src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java new file mode 100644 index 00000000..f8829974 --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/WrongCardPlacementException.java @@ -0,0 +1,7 @@ +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 new file mode 100644 index 00000000..a7f6ff6e --- /dev/null +++ b/src/main/java/it/polimi/ingsw/gamemodel/WrongStateException.java @@ -0,0 +1,7 @@ +package it.polimi.ingsw.gamemodel; + +public class WrongStateException extends Exception{ + public WrongStateException(String message) { + super(message); + } +} diff --git a/src/main/java/it/polimi/ingsw/utils/Pair.java b/src/main/java/it/polimi/ingsw/utils/Pair.java new file mode 100644 index 00000000..edff9c9c --- /dev/null +++ b/src/main/java/it/polimi/ingsw/utils/Pair.java @@ -0,0 +1,3 @@ +package it.polimi.ingsw.utils; + +public record Pair (T first, U second) {}