Move item building and dice roll logic out of CardGame

This commit is contained in:
Arne Keller 2020-03-23 10:49:27 +01:00
parent fe77053ac9
commit cc4e75f64e
4 changed files with 144 additions and 86 deletions

View File

@ -39,6 +39,21 @@ public enum Card implements RequireDice {
*/ */
THUNDERSTORM; THUNDERSTORM;
/**
* Return value indicating that the player successfully fought against an animal.
*
* @see #activate(CardGame, int, int)
* @see CardCategory#ANIMAL
*/
public static final String SURVIVED = "survived";
/**
* Return value indicating that the player lost against an animal.
*
* @see #activate(CardGame, int, int)
* @see CardCategory#ANIMAL
*/
public static final String LOSE = "lose";
/** /**
* Activate this card. Behaviour depends on the {@link #category category} of this card. * Activate this card. Behaviour depends on the {@link #category category} of this card.
* *
@ -50,7 +65,7 @@ public enum Card implements RequireDice {
game.givePlayerCard(this); game.givePlayerCard(this);
break; break;
case ANIMAL: // animal cards trigger an encounter case ANIMAL: // animal cards trigger an encounter
game.startEncounter(this); game.startDiceRoll(this);
break; break;
case CATASTROPHE: case CATASTROPHE:
game.deletePlayerResources(); game.deletePlayerResources();
@ -61,6 +76,24 @@ public enum Card implements RequireDice {
} }
} }
@Override
public String activate(CardGame game, int diceSize, int roll) throws LogicException {
if (!this.minimumDiceRollNeeded().isPresent()) {
throw new IllegalStateException("can not process dice roll");
} else if (this.diceSizeNeeded().get() != diceSize) {
throw new LogicException("unexpected dice size");
} else if (roll > diceSize || roll < 1) {
throw new LogicException("impossible roll");
}
if (roll + game.getFightingBonus() >= this.minimumDiceRollNeeded().get()) {
return SURVIVED;
} else {
game.deletePlayerResources();
return LOSE;
}
}
/** /**
* Get the category of this card. * Get the category of this card.
* *

View File

@ -28,29 +28,6 @@ public class CardGame {
* @see #build(Item) * @see #build(Item)
*/ */
public static final String OK = "OK"; public static final String OK = "OK";
/**
* Return value indicating that the player won.
*
* @see #build(Item)
* @see #rollDice(int, int)
* @see ItemCategory#ESCAPE
*/
public static final String WIN = "win";
/**
* Return value indicating that the player successfully fought against an animal.
*
* @see #rollDice(int, int)
* @see CardCategory#ANIMAL
*/
public static final String SURVIVED = "survived";
/**
* Return value indicating that the player lost against an animal or failed to escape.
*
* @see #rollDice(int, int)
* @see CardCategory#ANIMAL
* @see ItemCategory#ESCAPE
*/
public static final String LOSE = "lose";
/** /**
* Card stack used. * Card stack used.
@ -120,16 +97,16 @@ public class CardGame {
} }
/** /**
* Start a new encounter. {@link #rollDice} has to be used next. * Wait for a dice roll to activate the specified object. {@link #rollDice} has to be used next.
* *
* @param card (animal) card * @param requireDice something that requires a dice roll
*/ */
public void startEncounter(Card card) { public void startDiceRoll(RequireDice requireDice) {
if (!card.diceSizeNeeded().isPresent()) { if (!requireDice.diceSizeNeeded().isPresent()) {
throw new IllegalArgumentException("card does not require dice"); throw new IllegalArgumentException("object does not require dice");
} }
requireDice = card; this.requireDice = requireDice;
phase = CardGame.Phase.ENCOUNTER; phase = Phase.AWAITING_DICE_ROLL;
} }
/** /**
@ -156,51 +133,38 @@ public class CardGame {
* *
* @param size dice size * @param size dice size
* @param roll dice result * @param roll dice result
* @return result of the throw ({@link #SURVIVED survived}, {@link #LOSE lose} or {@link #WIN win}) * @return result of the dice roll (see {@link RequireDice#activate})
* @throws LogicException if not expecting dice roll or dice roll is invalid * @throws LogicException if not expecting dice roll or dice roll is invalid
*/ */
public String rollDice(int size, int roll) throws LogicException { public String rollDice(int size, int roll) throws LogicException {
if (requireDice == null) { if (requireDice == null) {
throw new LogicException("not expecting dice roll"); throw new LogicException("not expecting dice roll");
} } else if (requireDice.diceSizeNeeded().get() != size) {
// compare with currently expected dice
final int sizeNeeded = requireDice.diceSizeNeeded().get();
final int minimumNeeded = requireDice.minimumDiceRollNeeded().get();
if (sizeNeeded != size) {
throw new LogicException("unexpected dice size"); throw new LogicException("unexpected dice size");
} else if (roll > sizeNeeded || roll < 1) { } else if (roll > size || roll < 1) {
throw new LogicException("impossible roll"); throw new LogicException("impossible roll");
} }
// no longer waiting for dice roll // leave encounter/endeavor phase
phase = Phase.SCAVENGE;
// compute the result of the dice roll
final String result = requireDice.activate(this, size, roll);
// no longer waiting
requireDice = null; requireDice = null;
if (phase == Phase.ENCOUNTER) { return result;
// calculate fighting bonus, selecting the most powerful item the player owns }
final int bonus = inventory.getItems().stream().mapToInt(Item::fightingBonus).max().orElse(0);
phase = Phase.SCAVENGE; /**
if (roll + bonus >= minimumNeeded) { * @return the player's fighting bonus against {@link CardCategory#ANIMAL animals}
return SURVIVED; */
} else { public int getFightingBonus() {
inventory.clearResources(); return inventory.getItems().stream().mapToInt(Item::fightingBonus).max().orElse(0);
return LOSE;
}
} else { // attempting to escape
// do not remove item used to escape
if (roll >= minimumNeeded) {
endGame();
phase = Phase.WON;
return WIN;
} else {
phase = Phase.SCAVENGE;
return LOSE;
}
}
} }
/** /**
* Attempt to build the specified item. * Attempt to build the specified item.
* *
* @param item item to build * @param item item to build
* @return building result ({@link #OK} or {@link #WIN win}, or null if item can not be built * @return building result (see {@link Item#activate(CardGame)}), or null if item can not be built
* @throws LogicException if in wrong phase or item already built * @throws LogicException if in wrong phase or item already built
*/ */
public String build(Item item) throws LogicException { public String build(Item item) throws LogicException {
@ -214,22 +178,18 @@ public class CardGame {
if (!inventory.build(item)) { if (!inventory.build(item)) {
throw new LogicException("can not build item: missing resources/items"); throw new LogicException("can not build item: missing resources/items");
} }
if (item.category() == ItemCategory.ESCAPE) { return item.activate(this);
if (item.diceSizeNeeded().isPresent()) {
// player needs to roll dice to escape
requireDice = item;
phase = Phase.ENDEAVOR;
} else {
// player won
endGame();
phase = Phase.WON;
return WIN;
}
}
return OK;
} }
} }
/**
* End the game by forcing the player to win.
*/
public void winGame() {
endGame();
phase = Phase.WON;
}
/** /**
* Check whether this game ever received a {@link #start start} command. * Check whether this game ever received a {@link #start start} command.
* *
@ -254,10 +214,12 @@ public class CardGame {
* @see #gameLost * @see #gameLost
*/ */
private void checkLost() { private void checkLost() {
// can not draw new cards // obviously, player can only lose after starting the game
if (cardStack != null && cardStack.isEmpty() if (gameStarted()
// can not draw new cards
&& (cardStack == null || cardStack.isEmpty())
// can not roll dice // can not roll dice
&& phase != Phase.ENCOUNTER && phase != Phase.ENDEAVOR && phase != Phase.AWAITING_DICE_ROLL
// can not build item // can not build item
&& Arrays.stream(Item.values()).noneMatch(inventory::canBuild) && Arrays.stream(Item.values()).noneMatch(inventory::canBuild)
&& phase != Phase.WON) { && phase != Phase.WON) {
@ -359,19 +321,12 @@ public class CardGame {
*/ */
SCAVENGE, SCAVENGE,
/** /**
* Player has to fight an animal. * Player has to fight an animal or attempt to escape using {@link #rollDice(int, int)}.
* *
* @see #rollDice
* @see CardCategory#ANIMAL * @see CardCategory#ANIMAL
*/
ENCOUNTER,
/**
* Player is attempting to escape.
*
* @see #rollDice
* @see ItemCategory#ESCAPE * @see ItemCategory#ESCAPE
*/ */
ENDEAVOR, AWAITING_DICE_ROLL,
/** /**
* Player won. * Player won.
*/ */

View File

@ -49,6 +49,65 @@ public enum Item implements RequireDice {
*/ */
BALLON; BALLON;
/**
* Return value indicating success.
*
* @see #activate
*/
public static final String OK = "OK";
/**
* Return value indicating that the player won.
*
* @see #activate
* @see ItemCategory#ESCAPE
*/
public static final String WIN = "win";
/**
* Return value indicating that the player failed to escape.
*
* @see #activate(CardGame, int, int)
* @see ItemCategory#ESCAPE
*/
public static final String LOSE = "lose";
/**
* Activate this item on the specified game.
*
* @param game card game to use
* @return result (either {@link #OK} or {@link #WIN})
*/
public String activate(CardGame game) {
if (this.category() == ItemCategory.ESCAPE) {
if (this.diceSizeNeeded().isPresent()) {
// player needs to roll dice to escape
game.startDiceRoll(this);
} else {
// player won
game.winGame();
return WIN;
}
}
return OK;
}
@Override
public String activate(CardGame game, int diceSize, int roll) throws LogicException {
if (!this.minimumDiceRollNeeded().isPresent()) {
throw new IllegalStateException("can not process dice roll");
} else if (this.diceSizeNeeded().get() != diceSize) {
throw new LogicException("unexpected dice size");
} else if (roll > diceSize || roll < 1) {
throw new LogicException("impossible roll");
}
if (roll >= this.minimumDiceRollNeeded().get()) {
game.winGame();
return WIN;
} else {
return LOSE;
}
}
/** /**
* Get the resources needed to build this item, in no particular order. * Get the resources needed to build this item, in no particular order.
* *

View File

@ -22,4 +22,15 @@ public interface RequireDice {
* @return minimum dice roll needed to use this item (empty if dice not required) * @return minimum dice roll needed to use this item (empty if dice not required)
*/ */
Optional<Integer> minimumDiceRollNeeded(); Optional<Integer> minimumDiceRollNeeded();
/**
* Activate this object in a game, if the dice roll is good enough.
*
* @param game card game to use
* @param diceSize size of the dice rolled
* @param rolled result of the dice roll
* @return activation result
* @throws LogicException if dice roll is incorrect
*/
String activate(CardGame game, int diceSize, int rolled) throws LogicException;
} }