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 wood
wood wood
OK
lost lost
Error, can not get resources: game not started Error, can not get resources: game not started

View File

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