summaryrefslogtreecommitdiff
path: root/game-engine/src/main
diff options
context:
space:
mode:
authorJoffrey BION <joffrey.bion@gmail.com>2018-07-06 00:43:50 +0200
committerJoffrey Bion <joffrey.bion@amadeus.com>2018-07-06 16:06:23 +0200
commit9049dd004df71619e08a350c0a6e49455041d31b (patch)
tree3abc77d660f646bb64e9ab13549bdd16442de7cd /game-engine/src/main
parentKotlin mig: game definitions (diff)
downloadseven-wonders-9049dd004df71619e08a350c0a6e49455041d31b.tar.gz
seven-wonders-9049dd004df71619e08a350c0a6e49455041d31b.tar.bz2
seven-wonders-9049dd004df71619e08a350c0a6e49455041d31b.zip
Kotlin mig: cards package
Diffstat (limited to 'game-engine/src/main')
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java6
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Card.java128
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/cards/CardBack.java14
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Color.java11
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Decks.java65
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/cards/HandRotationDirection.java21
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Hands.java64
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Requirements.java108
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/HandCard.kt3
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerMove.kt4
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Table.kt8
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Cards.kt52
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Decks.kt38
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/HandRotationDirection.kt15
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt32
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Requirements.kt84
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt10
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt7
18 files changed, 239 insertions, 431 deletions
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java
index 2696adbf..1d88fb9f 100644
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java
+++ b/game-engine/src/main/java/org/luxons/sevenwonders/game/Game.java
@@ -109,7 +109,7 @@ public class Game {
}
public CardBack prepareMove(int playerIndex, PlayerMove playerMove) throws InvalidMoveException {
- Card card = decks.getCard(playerMove.getCardName());
+ Card card = decks.getCard(table.getCurrentAge(), playerMove.getCardName());
Move move = playerMove.getType().resolve(playerIndex, card, playerMove);
validate(move);
preparedMoves.put(playerIndex, move);
@@ -143,7 +143,7 @@ public class Game {
private void rotateHandsIfRelevant() {
// we don't rotate hands if some player can play his last card (with the special ability)
if (!hands.maxOneCardRemains()) {
- hands.rotate(table.getHandRotationDirection());
+ hands = hands.rotate(table.getHandRotationDirection());
}
}
@@ -196,7 +196,7 @@ public class Game {
private void discardHand(int playerIndex) {
List<Card> hand = hands.get(playerIndex);
discardedCards.addAll(hand);
- hand.clear();
+ hands = hands.discard(playerIndex);
}
private void removeFromHand(int playerIndex, Card card) {
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Card.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Card.java
deleted file mode 100644
index 2d2f6777..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Card.java
+++ /dev/null
@@ -1,128 +0,0 @@
-package org.luxons.sevenwonders.game.cards;
-
-import java.util.List;
-import java.util.Objects;
-
-import org.luxons.sevenwonders.game.api.Table;
-import org.luxons.sevenwonders.game.boards.Board;
-import org.luxons.sevenwonders.game.effects.Effect;
-import org.luxons.sevenwonders.game.resources.ResourceTransactions;
-
-public class Card {
-
- private final String name;
-
- private final Color color;
-
- private final Requirements requirements;
-
- private final List<Effect> effects;
-
- private final String chainParent;
-
- private final List<String> chainChildren;
-
- private final String image;
-
- private CardBack back;
-
- public Card(String name, Color color, Requirements requirements, List<Effect> effects, String chainParent,
- List<String> chainChildren, String image) {
- this.name = name;
- this.color = color;
- this.requirements = requirements;
- this.chainParent = chainParent;
- this.effects = effects;
- this.chainChildren = chainChildren;
- this.image = image;
- }
-
- public String getName() {
- return name;
- }
-
- public Color getColor() {
- return color;
- }
-
- public String getChainParent() {
- return chainParent;
- }
-
- public Requirements getRequirements() {
- return requirements;
- }
-
- public List<Effect> getEffects() {
- return effects;
- }
-
- public List<String> getChainChildren() {
- return chainChildren;
- }
-
- public String getImage() {
- return image;
- }
-
- public CardBack getBack() {
- return back;
- }
-
- public void setBack(CardBack back) {
- this.back = back;
- }
-
- private boolean isAllowedOnBoard(Board board) {
- return !board.isPlayed(name); // cannot play twice the same card
- }
-
- public boolean isChainableOn(Board board) {
- return isAllowedOnBoard(board) && board.isPlayed(chainParent);
- }
-
- public boolean isFreeFor(Board board) {
- if (!isAllowedOnBoard(board)) {
- return false;
- }
- return isChainableOn(board) || (requirements.areMetWithoutNeighboursBy(board) && requirements.getGold() == 0);
- }
-
- public boolean isPlayable(Table table, int playerIndex) {
- Board board = table.getBoard(playerIndex);
- if (!isAllowedOnBoard(board)) {
- return false;
- }
- return isChainableOn(board) || requirements.areMetBy(table, playerIndex);
- }
-
- public void applyTo(Table table, int playerIndex, ResourceTransactions transactions) {
- Board playerBoard = table.getBoard(playerIndex);
- if (!isChainableOn(playerBoard)) {
- requirements.pay(table, playerIndex, transactions);
- }
- effects.forEach(e -> e.apply(table, playerIndex));
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- Card card = (Card) o;
- return Objects.equals(name, card.name);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(name);
- }
-
- @Override
- public String toString() {
- return "Card{" + name + '}';
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/CardBack.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/CardBack.java
deleted file mode 100644
index f925b6c4..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/CardBack.java
+++ /dev/null
@@ -1,14 +0,0 @@
-package org.luxons.sevenwonders.game.cards;
-
-public class CardBack {
-
- private final String image;
-
- public CardBack(String image) {
- this.image = image;
- }
-
- public String getImage() {
- return image;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Color.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Color.java
deleted file mode 100644
index 80d06c55..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Color.java
+++ /dev/null
@@ -1,11 +0,0 @@
-package org.luxons.sevenwonders.game.cards;
-
-public enum Color {
- BROWN,
- GREY,
- YELLOW,
- BLUE,
- GREEN,
- RED,
- PURPLE
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Decks.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Decks.java
deleted file mode 100644
index aa2b00bf..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Decks.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.luxons.sevenwonders.game.cards;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-public class Decks {
-
- private Map<Integer, List<Card>> cardsPerAge = new HashMap<>();
-
- public Decks(Map<Integer, List<Card>> cardsPerAge) {
- this.cardsPerAge = cardsPerAge;
- }
-
- public Card getCard(String cardName) throws CardNotFoundException {
- return cardsPerAge.values()
- .stream()
- .flatMap(List::stream)
- .filter(c -> c.getName().equals(cardName))
- .findAny()
- .orElseThrow(() -> new CardNotFoundException(cardName));
- }
-
- public Hands deal(int age, int nbPlayers) {
- List<Card> deck = getDeck(age);
- validateNbCards(deck, nbPlayers);
- return deal(deck, nbPlayers);
- }
-
- private List<Card> getDeck(int age) {
- List<Card> deck = cardsPerAge.get(age);
- if (deck == null) {
- throw new IllegalArgumentException("No deck found for age " + age);
- }
- return deck;
- }
-
- private void validateNbCards(List<Card> deck, int nbPlayers) {
- if (nbPlayers == 0) {
- throw new IllegalArgumentException("Cannot deal cards between 0 players");
- }
- if (deck.size() % nbPlayers != 0) {
- throw new IllegalArgumentException(
- String.format("Cannot deal %d cards evenly between %d players", deck.size(), nbPlayers));
- }
- }
-
- private Hands deal(List<Card> deck, int nbPlayers) {
- Map<Integer, List<Card>> hands = new HashMap<>(nbPlayers);
- for (int i = 0; i < nbPlayers; i++) {
- hands.put(i, new ArrayList<>());
- }
- for (int i = 0; i < deck.size(); i++) {
- hands.get(i % nbPlayers).add(deck.get(i));
- }
- return new Hands(hands, nbPlayers);
- }
-
- class CardNotFoundException extends RuntimeException {
- CardNotFoundException(String message) {
- super(message);
- }
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/HandRotationDirection.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/HandRotationDirection.java
deleted file mode 100644
index f3902fb5..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/HandRotationDirection.java
+++ /dev/null
@@ -1,21 +0,0 @@
-package org.luxons.sevenwonders.game.cards;
-
-public enum HandRotationDirection {
- LEFT(-1),
- RIGHT(1);
-
- private final int indexOffset;
-
- HandRotationDirection(int i) {
- this.indexOffset = i;
- }
-
- public int getIndexOffset() {
- return indexOffset;
- }
-
- public static HandRotationDirection forAge(int age) {
- // clockwise (pass to the left) at age 1, and alternating
- return age % 2 == 0 ? HandRotationDirection.RIGHT : HandRotationDirection.LEFT;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Hands.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Hands.java
deleted file mode 100644
index 4a8bc143..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Hands.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.luxons.sevenwonders.game.cards;
-
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import org.luxons.sevenwonders.game.api.HandCard;
-import org.luxons.sevenwonders.game.api.Table;
-
-public class Hands {
-
- private final int nbPlayers;
-
- private Map<Integer, List<Card>> hands;
-
- Hands(Map<Integer, List<Card>> hands, int nbPlayers) {
- this.hands = hands;
- this.nbPlayers = nbPlayers;
- }
-
- public List<Card> get(int playerIndex) {
- if (!hands.containsKey(playerIndex)) {
- throw new PlayerIndexOutOfBoundsException(playerIndex);
- }
- return hands.get(playerIndex);
- }
-
- public List<HandCard> createHand(Table table, int playerIndex) {
- return hands.get(playerIndex)
- .stream()
- .map(c -> new HandCard(c, table, playerIndex))
- .collect(Collectors.toList());
- }
-
- public Hands rotate(HandRotationDirection direction) {
- Map<Integer, List<Card>> newHands = new HashMap<>(hands.size());
- for (int i = 0; i < nbPlayers; i++) {
- int newIndex = Math.floorMod(i + direction.getIndexOffset(), nbPlayers);
- newHands.put(newIndex, hands.get(i));
- }
- return new Hands(newHands, nbPlayers);
- }
-
- public boolean isEmpty() {
- return hands.values().stream().allMatch(List::isEmpty);
- }
-
- public boolean maxOneCardRemains() {
- return hands.values().stream().mapToInt(List::size).max().orElse(0) <= 1;
- }
-
- public List<Card> gatherAndClear() {
- List<Card> remainingCards = hands.values().stream().flatMap(List::stream).collect(Collectors.toList());
- hands.clear();
- return remainingCards;
- }
-
- class PlayerIndexOutOfBoundsException extends ArrayIndexOutOfBoundsException {
- PlayerIndexOutOfBoundsException(int index) {
- super(index);
- }
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Requirements.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Requirements.java
deleted file mode 100644
index 8c7245ed..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/cards/Requirements.java
+++ /dev/null
@@ -1,108 +0,0 @@
-package org.luxons.sevenwonders.game.cards;
-
-import org.luxons.sevenwonders.game.api.Table;
-import org.luxons.sevenwonders.game.boards.Board;
-import org.luxons.sevenwonders.game.resources.BestPriceCalculator;
-import org.luxons.sevenwonders.game.resources.ResourceTransactions;
-import org.luxons.sevenwonders.game.resources.Resources;
-
-public class Requirements {
-
- private int gold;
-
- private Resources resources = new Resources();
-
- public int getGold() {
- return gold;
- }
-
- public void setGold(int gold) {
- this.gold = gold;
- }
-
- public Resources getResources() {
- return resources;
- }
-
- public void setResources(Resources resources) {
- this.resources = resources;
- }
-
- /**
- * Returns whether the given board meets these requirements on its own.
- *
- * @param board
- * the board to check
- *
- * @return true if the given board meets these requirements without any transaction with its neighbours
- */
- boolean areMetWithoutNeighboursBy(Board board) {
- return hasRequiredGold(board) && producesRequiredResources(board);
- }
-
- /**
- * Returns whether the given board meets these requirements, if the specified resources are bought from neighbours.
- *
- * @param board
- * the board to check
- * @param boughtResources
- * the resources the player intends to buy
- *
- * @return true if the given board meets these requirements
- */
- public boolean areMetWithHelpBy(Board board, ResourceTransactions boughtResources) {
- if (!hasRequiredGold(board, boughtResources)) {
- return false;
- }
- if (producesRequiredResources(board)) {
- return true;
- }
- return producesRequiredResourcesWithHelp(board, boughtResources);
- }
-
- /**
- * Returns whether the given player's board meets these requirements, either on its own or by buying resources to
- * neighbours.
- *
- * @param table
- * the current game table
- * @param playerIndex
- * the index of the player to check
- *
- * @return true if the given player's board could meet these requirements
- */
- boolean areMetBy(Table table, int playerIndex) {
- Board board = table.getBoard(playerIndex);
- if (!hasRequiredGold(board)) {
- return false;
- }
- if (producesRequiredResources(board)) {
- return true;
- }
- return BestPriceCalculator.bestPrice(resources, table, playerIndex) <= board.getGold() - gold;
- }
-
- private boolean hasRequiredGold(Board board) {
- return board.getGold() >= gold;
- }
-
- private boolean hasRequiredGold(Board board, ResourceTransactions resourceTransactions) {
- int resourcesPrice = board.getTradingRules().computeCost(resourceTransactions);
- return board.getGold() >= gold + resourcesPrice;
- }
-
- private boolean producesRequiredResources(Board board) {
- return board.getProduction().contains(resources);
- }
-
- private boolean producesRequiredResourcesWithHelp(Board board, ResourceTransactions transactions) {
- Resources totalBoughtResources = transactions.asResources();
- Resources remainingResources = this.resources.minus(totalBoughtResources);
- return board.getProduction().contains(remainingResources);
- }
-
- public void pay(Table table, int playerIndex, ResourceTransactions transactions) {
- table.getBoard(playerIndex).removeGold(gold);
- transactions.execute(table, playerIndex);
- }
-}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/HandCard.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/HandCard.kt
index 4c3a7a8d..d487a9ac 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/HandCard.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/HandCard.kt
@@ -12,12 +12,11 @@ class HandCard(val card: Card, table: Table, playerIndex: Int) {
val isFree: Boolean
- val isPlayable: Boolean
+ val isPlayable: Boolean = card.isPlayable(table, playerIndex)
init {
val board = table.getBoard(playerIndex)
this.isChainable = card.isChainableOn(board)
this.isFree = card.isFreeFor(board)
- this.isPlayable = card.isPlayable(table, playerIndex)
}
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerMove.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerMove.kt
index d0a1e1b3..4d4ef7cc 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerMove.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/PlayerMove.kt
@@ -3,8 +3,8 @@ package org.luxons.sevenwonders.game.api
import org.luxons.sevenwonders.game.moves.MoveType
import org.luxons.sevenwonders.game.resources.ResourceTransaction
-data class PlayerMove(
+data class PlayerMove @JvmOverloads constructor(
val type: MoveType,
- val cardName: String?,
+ val cardName: String,
val transactions: Collection<ResourceTransaction> = emptyList()
)
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Table.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Table.kt
index 9ecfa6b0..60287c84 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Table.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Table.kt
@@ -40,18 +40,18 @@ class Table(val boards: List<Board>) {
for (i in 0 until nbPlayers) {
val board1 = getBoard(i)
val board2 = getBoard((i + 1) % nbPlayers)
- resolveConflict(board1, board2, currentAge)
+ resolveConflict(board1, board2)
}
}
- private fun resolveConflict(board1: Board, board2: Board, age: Int) {
+ private fun resolveConflict(board1: Board, board2: Board) {
val shields1 = board1.military.nbShields
val shields2 = board2.military.nbShields
if (shields1 < shields2) {
board1.military.defeat()
- board2.military.victory(age)
+ board2.military.victory(currentAge)
} else if (shields1 > shields2) {
- board1.military.victory(age)
+ board1.military.victory(currentAge)
board2.military.defeat()
}
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Cards.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Cards.kt
new file mode 100644
index 00000000..cf94bef4
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Cards.kt
@@ -0,0 +1,52 @@
+package org.luxons.sevenwonders.game.cards
+
+import org.luxons.sevenwonders.game.api.Table
+import org.luxons.sevenwonders.game.boards.Board
+import org.luxons.sevenwonders.game.effects.Effect
+import org.luxons.sevenwonders.game.resources.ResourceTransactions
+
+data class CardBack(val image: String)
+
+data class Card(
+ val name: String,
+ val color: Color,
+ val requirements: Requirements,
+ val effects: List<Effect>,
+ val chainParent: String?,
+ val chainChildren: List<String>,
+ val image: String,
+ val back: CardBack
+) {
+ private fun isAllowedOnBoard(board: Board): Boolean = !board.isPlayed(name) // cannot play twice the same card
+
+ fun isFreeFor(board: Board): Boolean = isChainableOn(board) || isFreeWithoutChainingOn(board)
+
+ fun isChainableOn(board: Board): Boolean =
+ isAllowedOnBoard(board) && chainParent != null && board.isPlayed(chainParent)
+
+ private fun isFreeWithoutChainingOn(board: Board) =
+ isAllowedOnBoard(board) && requirements.areMetWithoutNeighboursBy(board) && requirements.gold == 0
+
+ fun isPlayable(table: Table, playerIndex: Int): Boolean {
+ val board = table.getBoard(playerIndex)
+ return isAllowedOnBoard(board) && (isChainableOn(board) || requirements.areMetBy(table, playerIndex))
+ }
+
+ fun applyTo(table: Table, playerIndex: Int, transactions: ResourceTransactions) {
+ val playerBoard = table.getBoard(playerIndex)
+ if (!isChainableOn(playerBoard)) {
+ requirements.pay(table, playerIndex, transactions)
+ }
+ effects.forEach { e -> e.apply(table, playerIndex) }
+ }
+}
+
+enum class Color {
+ BROWN,
+ GREY,
+ YELLOW,
+ BLUE,
+ GREEN,
+ RED,
+ PURPLE
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Decks.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Decks.kt
new file mode 100644
index 00000000..bd7b4bde
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Decks.kt
@@ -0,0 +1,38 @@
+package org.luxons.sevenwonders.game.cards
+
+fun List<Card>.deal(nbPlayers: Int): Hands {
+ val hands: Map<Int, List<Card>> = this.withIndex()
+ .groupBy { (index, _) -> index % nbPlayers }
+ .mapValues { it.value.map { (_, cards) -> cards } }
+
+ val allHands = List(nbPlayers) { i -> hands[i] ?: emptyList() }
+ return Hands(allHands)
+}
+
+class Decks(private val cardsPerAge: Map<Int, List<Card>>) {
+
+ @Throws(Decks.CardNotFoundException::class)
+ fun getCard(age: Int, cardName: String): Card =
+ getDeck(age).firstOrNull { c -> c.name == cardName } ?: throw CardNotFoundException(cardName)
+
+ fun deal(age: Int, nbPlayers: Int): Hands {
+ val deck = getDeck(age)
+ validateNbCards(deck, nbPlayers)
+ return deck.deal(nbPlayers)
+ }
+
+ private fun getDeck(age: Int): List<Card> {
+ return cardsPerAge[age] ?: throw IllegalArgumentException("No deck found for age $age")
+ }
+
+ private fun validateNbCards(deck: List<Card>, nbPlayers: Int) {
+ if (nbPlayers == 0) {
+ throw IllegalArgumentException("Cannot deal cards between 0 players")
+ }
+ if (deck.size % nbPlayers != 0) {
+ throw IllegalArgumentException("Cannot deal ${deck.size} cards evenly between $nbPlayers players")
+ }
+ }
+
+ inner class CardNotFoundException(message: String) : RuntimeException(message)
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/HandRotationDirection.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/HandRotationDirection.kt
new file mode 100644
index 00000000..13494175
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/HandRotationDirection.kt
@@ -0,0 +1,15 @@
+package org.luxons.sevenwonders.game.cards
+
+enum class HandRotationDirection {
+ LEFT,
+ RIGHT;
+
+ companion object {
+
+ fun forAge(age: Int): HandRotationDirection {
+ // clockwise (pass to the left) at age 1, and alternating
+ return if (age % 2 == 0) HandRotationDirection.RIGHT else HandRotationDirection.LEFT
+ }
+ }
+}
+
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt
new file mode 100644
index 00000000..35d3599a
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Hands.kt
@@ -0,0 +1,32 @@
+package org.luxons.sevenwonders.game.cards
+
+import org.luxons.sevenwonders.game.api.HandCard
+import org.luxons.sevenwonders.game.api.Table
+
+class Hands internal constructor(private val hands: List<List<Card>>) {
+
+ val isEmpty: Boolean = this.hands.all(List<Card>::isEmpty)
+
+ operator fun get(playerIndex: Int): List<Card> {
+ return hands[playerIndex]
+ }
+
+ fun discard(playerIndex: Int): Hands {
+ val newHands = hands.mapIndexed { index, hand -> if (index == playerIndex) emptyList() else hand }
+ return Hands(newHands)
+ }
+
+ fun createHand(table: Table, playerIndex: Int): List<HandCard> {
+ return hands[playerIndex].map { c -> HandCard(c, table, playerIndex) }
+ }
+
+ fun rotate(direction: HandRotationDirection): Hands {
+ val newHands = when (direction) {
+ HandRotationDirection.RIGHT -> hands.takeLast(1) + hands.dropLast(1)
+ HandRotationDirection.LEFT -> hands.drop(1) + hands.take(1)
+ }
+ return Hands(newHands)
+ }
+
+ fun maxOneCardRemains(): Boolean = hands.map { it.size }.max() ?: 0 <= 1
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Requirements.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Requirements.kt
new file mode 100644
index 00000000..e04fa7a0
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Requirements.kt
@@ -0,0 +1,84 @@
+package org.luxons.sevenwonders.game.cards
+
+import org.luxons.sevenwonders.game.api.Table
+import org.luxons.sevenwonders.game.boards.Board
+import org.luxons.sevenwonders.game.resources.BestPriceCalculator
+import org.luxons.sevenwonders.game.resources.ResourceTransactions
+import org.luxons.sevenwonders.game.resources.Resources
+
+data class Requirements @JvmOverloads constructor(
+ val gold: Int = 0,
+ val resources: Resources = Resources()
+) {
+ /**
+ * Returns whether the given [board] meets these requirements on its own.
+ *
+ * @param board the board to check
+ *
+ * @return true if the given board meets these requirements without any transaction with its neighbours
+ */
+ fun areMetWithoutNeighboursBy(board: Board): Boolean {
+ return hasRequiredGold(board) && producesRequiredResources(board)
+ }
+
+ /**
+ * Returns whether the given board meets these requirements, if the specified resources are bought from neighbours.
+ *
+ * @param board the board to check
+ * @param boughtResources the resources the player intends to buy
+ *
+ * @return true if the given board meets these requirements
+ */
+ fun areMetWithHelpBy(board: Board, boughtResources: ResourceTransactions): Boolean {
+ if (!hasRequiredGold(board, boughtResources)) {
+ return false
+ }
+ return if (producesRequiredResources(board)) {
+ true
+ } else producesRequiredResourcesWithHelp(board, boughtResources)
+ }
+
+ /**
+ * Returns whether the given player's board meets these requirements, either on its own or by buying resources to
+ * neighbours.
+ *
+ * @param table the current game table
+ * @param playerIndex the index of the player to check
+ *
+ * @return true if the given player's board could meet these requirements
+ */
+ fun areMetBy(table: Table, playerIndex: Int): Boolean {
+ val board = table.getBoard(playerIndex)
+ if (!hasRequiredGold(board)) {
+ return false
+ }
+ if (producesRequiredResources(board)) {
+ return true
+ }
+ return BestPriceCalculator.bestPrice(resources, table, playerIndex) <= board.gold - gold
+ }
+
+ private fun hasRequiredGold(board: Board): Boolean {
+ return board.gold >= gold
+ }
+
+ private fun hasRequiredGold(board: Board, resourceTransactions: ResourceTransactions): Boolean {
+ val resourcesPrice = board.tradingRules.computeCost(resourceTransactions)
+ return board.gold >= gold + resourcesPrice
+ }
+
+ private fun producesRequiredResources(board: Board): Boolean {
+ return board.production.contains(resources)
+ }
+
+ private fun producesRequiredResourcesWithHelp(board: Board, transactions: ResourceTransactions): Boolean {
+ val totalBoughtResources = transactions.asResources()
+ val remainingResources = this.resources.minus(totalBoughtResources)
+ return board.production.contains(remainingResources)
+ }
+
+ fun pay(table: Table, playerIndex: Int, transactions: ResourceTransactions) {
+ table.getBoard(playerIndex).removeGold(gold)
+ transactions.execute(table, playerIndex)
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt
index 2827564b..3f42746c 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt
@@ -1,20 +1,22 @@
package org.luxons.sevenwonders.game.data.definitions
import org.luxons.sevenwonders.game.cards.Card
+import org.luxons.sevenwonders.game.cards.CardBack
import org.luxons.sevenwonders.game.cards.Color
import org.luxons.sevenwonders.game.cards.Requirements
internal class CardDefinition(
private val name: String,
private val color: Color,
- private val requirements: Requirements? = null,
+ private val requirements: Requirements = Requirements(),
private val effect: EffectsDefinition,
private val chainParent: String? = null,
private val chainChildren: List<String> = emptyList(),
- private val image: String? = null,
+ private val image: String,
private val countPerNbPlayer: Map<Int, Int>
) {
- fun create(nbPlayers: Int): List<Card> = List( countPerNbPlayer[nbPlayers]!!) { create() }
+ fun create(back: CardBack, nbPlayers: Int): List<Card> = List(countPerNbPlayer[nbPlayers] ?: 0) { create(back) }
- fun create(): Card = Card(name, color, requirements, effect.create(), chainParent, chainChildren, image)
+ fun create(back: CardBack): Card =
+ Card(name, color, requirements, effect.create(), chainParent, chainChildren, image, back)
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt
index a09f0642..bc025ea2 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt
@@ -39,14 +39,11 @@ internal class DecksDefinition(
}
private fun createDeck(defs: List<CardDefinition>, settings: Settings, back: CardBack): List<Card> {
- return defs.flatMap { it.create(settings.nbPlayers) }.onEach { it.back = back }
+ return defs.flatMap { it.create(back, settings.nbPlayers) }
}
private fun createGuildCards(settings: Settings, back: CardBack): List<Card> {
- val guild = guildCards
- .map { it.create() }
- .map { c -> c.back = back; c }
- .toMutableList()
+ val guild = guildCards.map { it.create(back) }.toMutableList()
guild.shuffle(settings.random)
return guild.subList(0, settings.nbPlayers + 2)
}
bgstack15