Simplify game phase logic

This commit is contained in:
Arne Keller 2020-03-12 09:18:45 +01:00
parent 172e085d28
commit 0b913bf943
3 changed files with 43 additions and 20 deletions

View File

@ -78,5 +78,6 @@ thunderstorm
wood
wood
wood
OK
lost
Error, can not get resources: game not started

View File

@ -22,9 +22,9 @@ public class CardGame {
private Deque<Card> currentCardStack;
private final Deque<Card> resources = new ArrayDeque<>();
private final List<Item> items = new ArrayList<>();
private boolean fightingAnimal = false;
private Integer awaitedDiceSize = null;
private Integer minimumDiceRoll = null;
private Phase phase = null;
/**
* Start a new game with the specified stack of cards.
@ -33,7 +33,7 @@ public class CardGame {
* @return whether the game could be successfully started
*/
public boolean start(Deque<Card> cardStack) throws LogicException {
if (gameStarted()) {
if (gameActive()) {
return false;
}
if (!Arrays.stream(Card.values())
@ -48,28 +48,29 @@ public class CardGame {
public Card draw() throws LogicException {
if (currentCardStack == null || currentCardStack.isEmpty()) {
throw new LogicException("no card to draw exists");
} else if (awaitingDiceRoll()) {
throw new LogicException("roll dice, please");
} else if (phase != Phase.SCAVENGE) {
throw new LogicException("can only draw cards in scavenge phase");
}
final Card card = currentCardStack.removeFirst();
if (card.isResource()) {
resources.add(card);
} else if (card.needsDiceRoll()) {
fightingAnimal = true;
awaitedDiceSize = card.diceSizeNeeded();
minimumDiceRoll = card.minimumDiceRollNeeded();
phase = Phase.ENCOUNTER;
} else if (card.isCatastrophe()) {
clearResources();
items.remove(FIREPLACE);
}
if (currentCardStack.isEmpty() && getBuildableItems().isEmpty()) {
endGame(); // TODO: somehow print "lost" ??? (after the card!) [awaiting ILIAS..]
endGame();
this.phase = Phase.LOST;
}
return card;
}
public String rollDice(int size, int roll) throws LogicException {
if (!awaitingDiceRoll()) {
if (phase != Phase.ENDEAVOR && phase != Phase.ENCOUNTER) {
throw new LogicException("not expecting dice roll");
} else if (awaitedDiceSize != size) {
throw new LogicException("unexpected dice size");
@ -79,8 +80,9 @@ public class CardGame {
awaitedDiceSize = null;
final int minimumNeeded = minimumDiceRoll;
minimumDiceRoll = null;
if (fightingAnimal) {
if (phase == Phase.ENCOUNTER) {
final int bonus = items.stream().mapToInt(Item::fightingBonus).max().orElse(0);
phase = Phase.SCAVENGE;
if (roll + bonus >= minimumNeeded) {
return "survived";
} else {
@ -92,8 +94,10 @@ public class CardGame {
items.remove(items.size() - 1);
if (roll >= minimumNeeded) {
endGame();
phase = Phase.WON;
return "win";
} else {
phase = Phase.SCAVENGE;
return "lose";
}
}
@ -109,8 +113,8 @@ public class CardGame {
public String build(Item item) throws LogicException {
if (item == null) {
throw new LogicException("can not build item");
} else if (awaitingDiceRoll()) {
throw new LogicException("awaiting dice roll, can not build");
} else if (phase != Phase.SCAVENGE) {
throw new LogicException("can only build in scavenge phase");
} else if (items.contains(item)) {
throw new LogicException("already built");
} else if (canBuild(item)) {
@ -122,15 +126,16 @@ public class CardGame {
if (item.equals(STEAMBOAT) || item.equals(BALLON)) {
// player won
endGame();
phase = Phase.WON;
return "win";
} else if (item.equals(SAILING_RAFT) || item.equals(HANG_GLIDER)) {
// need at least a 4/d6
fightingAnimal = false;
awaitedDiceSize = 6;
minimumDiceRoll = 4;
phase = Phase.ENDEAVOR;
} else if (currentCardStack != null && currentCardStack.isEmpty() && getBuildableItems().isEmpty()) {
endGame();
return "lost";
phase = Phase.LOST;
}
return "OK";
} else {
@ -158,15 +163,19 @@ public class CardGame {
}
private boolean gameStarted() {
return currentCardStack != null;
return phase != null;
}
private boolean awaitingDiceRoll() {
return awaitedDiceSize != null;
private boolean gameActive() {
return gameStarted() && phase != Phase.WON && phase != Phase.LOST;
}
public boolean gameLost() {
return phase == Phase.LOST;
}
public Deque<Card> getResources() throws LogicException {
if (!gameStarted()) {
if (!gameActive()) {
throw new LogicException("can not get resources: game not started");
}
// do not allow caller to modify internal queue
@ -174,7 +183,7 @@ public class CardGame {
}
public List<Item> getItems() throws LogicException {
if (!gameStarted()) {
if (!gameActive()) {
throw new LogicException("can not get buildings: game not started");
}
// do not allow caller to modify internal list
@ -182,10 +191,10 @@ public class CardGame {
}
public Set<Item> getBuildableItems() throws LogicException {
if (!gameStarted()) {
if (!gameActive()) {
throw new LogicException("can not get buildable items: game not started");
}
if (awaitingDiceRoll()) {
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());
@ -203,8 +212,16 @@ public class CardGame {
}
this.resources.clear();
this.items.clear();
this.fightingAnimal = false;
this.awaitedDiceSize = null;
this.minimumDiceRoll = null;
this.phase = Phase.SCAVENGE;
}
enum Phase {
SCAVENGE,
ENCOUNTER,
ENDEAVOR,
WON,
LOST
}
}

View File

@ -34,6 +34,7 @@ public final class CommandLine {
public static void startInteractive() {
// create a new simulation
CardGame simulation = new CardGame();
boolean lost = false;
while (true) {
String input = Terminal.readLine();
@ -67,6 +68,10 @@ public final class CommandLine {
} catch (LogicException e) {
Terminal.printError(e.getMessage());
}
if (lost != simulation.gameLost() && simulation.gameLost()) {
Terminal.printLine("lost");
}
lost = simulation.gameLost();
}
}
}