summaryrefslogtreecommitdiff
path: root/game-engine/src/main/kotlin/org
diff options
context:
space:
mode:
Diffstat (limited to 'game-engine/src/main/kotlin/org')
-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
10 files changed, 236 insertions, 17 deletions
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