diff --git a/src/edu/kit/informatik/cardgame/model/CardGame.java b/src/edu/kit/informatik/cardgame/model/CardGame.java index 2a89c0e..9c991ff 100644 --- a/src/edu/kit/informatik/cardgame/model/CardGame.java +++ b/src/edu/kit/informatik/cardgame/model/CardGame.java @@ -1,12 +1,9 @@ package edu.kit.informatik.cardgame.model; -import java.util.ArrayDeque; -import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Deque; import java.util.List; -import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; @@ -22,13 +19,9 @@ public class CardGame { */ private CardStack cardStack; /** - * Resources collected by the player. Obviously only contains {@link CardCategory#RESOURCE resource} cards. + * Inventory of the player, containing resources and items. */ - private final Deque resources = new ArrayDeque<>(); - /** - * Items built by the player. - */ - private final List items = new ArrayList<>(); + private Inventory inventory; /** * Current game phase, null if game not yet started. */ @@ -54,6 +47,7 @@ public class CardGame { throw new LogicException("invalid deck: missing or surplus cards"); } this.cardStack = new CardStack(cardStack); + this.inventory = new Inventory(); reset(); return true; } @@ -74,15 +68,15 @@ public class CardGame { final Card card = cardStack.draw().orElseThrow(() -> new LogicException("no card to draw exists")); switch (card.category()) { case RESOURCE: - resources.add(card); + inventory.addResource(card); break; case ANIMAL: requireDice = card; phase = Phase.ENCOUNTER; break; case CATASTROPHE: - clearResources(); - items.remove(Item.FIREPLACE); + inventory.clearResources(); + inventory.removeItem(Item.FIREPLACE); break; default: throw new IllegalStateException("encountered unknown card category!"); @@ -112,13 +106,13 @@ public class CardGame { } requireDice = null; if (phase == Phase.ENCOUNTER) { - final int bonus = items.stream().mapToInt(Item::fightingBonus).max().orElse(0); + final int bonus = inventory.itemStream().mapToInt(Item::fightingBonus).max().orElse(0); phase = Phase.SCAVENGE; checkLost(); if (roll + bonus >= minimumNeeded) { return "survived"; } else { - clearResources(); + inventory.clearResources(); return "lose"; } } else { // attempting to escape @@ -135,18 +129,6 @@ public class CardGame { } } - /** - * Clear player resources, keeping a few items in the shack if one is built. - */ - private void clearResources() { - // calculate the resources saved by player items - final int keepLastResources = items.stream().mapToInt(Item::itemsSecured).sum(); - while (resources.size() > keepLastResources) { - // remove resources that were picked up earlier first - resources.removeFirst(); - } - } - /** * Attempt to build the specified item. * @@ -159,15 +141,10 @@ public class CardGame { throw new LogicException("can not build item"); } else if (phase != Phase.SCAVENGE) { throw new LogicException("can only build in scavenge phase"); - } else if (items.contains(item)) { + } else if (inventory.contains(item)) { throw new LogicException("already built"); - } else if (canBuild(item)) { - // remove used resources - for (final Card resource : item.resourcesNeeded()) { - // resources picked up last get used up first - resources.removeLastOccurrence(resource); - } - items.add(item); + } else if (inventory.canBuild(item)) { + inventory.build(item); switch (item.category()) { case ESCAPE: if (item.diceSizeNeeded().isPresent()) { @@ -192,34 +169,6 @@ public class CardGame { } } - /** - * Check whether the player can build the specified item with the currently available resources and items. - * - * @param item item to build - * @return whether that item can be built - */ - private boolean canBuild(Item item) { - // check whether item is already built - if (items.contains(item)) { - return false; - } - // make sure all of the required items are already built - if (!items.containsAll(item.itemsNeededToBuild())) { - return false; - } - // get the needed resources and overwrite entries with null if available - final Card[] resourcesNeeded = item.resourcesNeeded(); - for (final Card resource : resources) { - for (int j = 0; j < resourcesNeeded.length; j++) { - if (resourcesNeeded[j] != null && resourcesNeeded[j] == resource) { - resourcesNeeded[j] = null; - break; - } - } - } - return Arrays.stream(resourcesNeeded).allMatch(Objects::isNull); - } - /** * @return whether the game was ever started */ @@ -245,7 +194,7 @@ public class CardGame { // can not roll dice && phase != Phase.ENCOUNTER && phase != Phase.ENDEAVOR // can not build item - && Arrays.stream(Item.values()).noneMatch(this::canBuild)) { + && Arrays.stream(Item.values()).noneMatch(inventory::canBuild)) { endGame(); phase = Phase.LOST; } @@ -271,8 +220,7 @@ public class CardGame { if (!gameStarted()) { throw new LogicException("can not get resources: game not started"); } - // have to copy here: caller does not expect an auto-updating collection - return new ArrayDeque<>(resources); + return inventory.getResources(); } /** @@ -285,8 +233,7 @@ public class CardGame { if (!gameStarted()) { throw new LogicException("can not get buildings: game not started"); } - // have to copy here: caller does not expect an auto-updating list - return new ArrayList<>(items); + return inventory.getItems(); } /** @@ -301,7 +248,7 @@ public class CardGame { } else if (phase != Phase.SCAVENGE) { throw new LogicException("can not get buildable items: awaiting dice roll"); } - return Arrays.stream(Item.values()).filter(this::canBuild).collect(Collectors.toSet()); + return Arrays.stream(Item.values()).filter(inventory::canBuild).collect(Collectors.toSet()); } /** @@ -322,8 +269,7 @@ public class CardGame { } else { this.cardStack.reset(); } - this.resources.clear(); - this.items.clear(); + this.inventory.clear(); this.requireDice = null; this.phase = Phase.SCAVENGE; } diff --git a/src/edu/kit/informatik/cardgame/model/Inventory.java b/src/edu/kit/informatik/cardgame/model/Inventory.java new file mode 100644 index 0000000..d457848 --- /dev/null +++ b/src/edu/kit/informatik/cardgame/model/Inventory.java @@ -0,0 +1,95 @@ +package edu.kit.informatik.cardgame.model; + +import java.util.*; +import java.util.stream.Stream; + +public class Inventory { + /** + * Resources collected by the player. Obviously only contains {@link CardCategory#RESOURCE resource} cards. + */ + private final Deque resources = new ArrayDeque<>(); + /** + * Items built by the player. + */ + private final List items = new ArrayList<>(); + + public void addResource(Card resourceCard) { + resources.add(resourceCard); + } + + public Deque getResources() { + // have to copy here: caller does not expect an auto-updating collection + return new ArrayDeque<>(resources); + } + + /** + * Clear player resources, keeping a few items in the shack if one is built. + */ + public void clearResources() { + // calculate the resources saved by player items + final int keepLastResources = this.itemStream().mapToInt(Item::itemsSecured).sum(); + while (resources.size() > keepLastResources) { + // remove resources that were picked up earlier first + resources.removeFirst(); + } + } + + public void build(Item item) { + // remove used resources + for (final Card resource : item.resourcesNeeded()) { + // resources picked up last get used up first + resources.removeLastOccurrence(resource); + } + items.add(item); + } + + public List getItems() { + // have to copy here: caller does not expect an auto-updating list + return new ArrayList<>(items); + } + + public Stream itemStream() { + return items.stream(); + } + + public boolean contains(Item item) { + return items.contains(item); + } + + public void removeItem(Item item) { + items.remove(item); + } + + /** + * Check whether the player can build the specified item with the currently available resources and items. + * + * @param item item to build + * @return whether that item can be built + */ + public boolean canBuild(Item item) { + // check whether item is already built + if (items.contains(item)) { + return false; + } + // make sure all of the required items are already built + if (!items.containsAll(item.itemsNeededToBuild())) { + return false; + } + // get the needed resources and overwrite entries with null if available + final Card[] resourcesNeeded = item.resourcesNeeded(); + for (final Card resource : resources) { + for (int j = 0; j < resourcesNeeded.length; j++) { + if (resourcesNeeded[j] != null && resourcesNeeded[j] == resource) { + resourcesNeeded[j] = null; + break; + } + } + } + return Arrays.stream(resourcesNeeded).allMatch(Objects::isNull); + } + + public void clear() { + resources.clear(); + items.clear(); + } +}