Refactor player inventory into own class

This commit is contained in:
Arne Keller 2020-03-17 12:57:39 +01:00
parent 7a09111164
commit 7e499c0c9a
2 changed files with 111 additions and 70 deletions

View File

@ -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<Card> resources = new ArrayDeque<>();
/**
* Items built by the player.
*/
private final List<Item> 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;
}

View File

@ -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<Card> resources = new ArrayDeque<>();
/**
* Items built by the player.
*/
private final List<Item> items = new ArrayList<>();
public void addResource(Card resourceCard) {
resources.add(resourceCard);
}
public Deque<Card> 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<Item> getItems() {
// have to copy here: caller does not expect an auto-updating list
return new ArrayList<>(items);
}
public Stream<Item> 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();
}
}