summaryrefslogtreecommitdiff
path: root/game-engine/src
diff options
context:
space:
mode:
Diffstat (limited to 'game-engine/src')
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Cards.kt35
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Cards.kt79
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Requirements.kt2
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/RequirementsSatisfaction.kt89
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.kt2
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.kt2
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/PlayCardMove.kt3
-rw-r--r--game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt4
-rw-r--r--game-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/RequirementsTest.kt2
9 files changed, 87 insertions, 131 deletions
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Cards.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Cards.kt
index d238ab77..c0e51ec5 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Cards.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Cards.kt
@@ -7,8 +7,6 @@ import org.luxons.sevenwonders.game.cards.CardPlayability
import org.luxons.sevenwonders.game.cards.Color
import org.luxons.sevenwonders.game.cards.Requirements
import org.luxons.sevenwonders.game.moves.Move
-import org.luxons.sevenwonders.game.resources.ResourceTransactions
-import org.luxons.sevenwonders.game.resources.bestSolution
data class TableCard(
val name: String,
@@ -44,27 +42,16 @@ data class HandCard(
val chainChildren: List<String>,
val image: String,
val back: CardBack,
- val isChainable: Boolean,
- val isFree: Boolean,
- val isPlayable: Boolean,
- val minPrice: Int,
- val cheapestTransactions: Set<ResourceTransactions>
+ val playability: CardPlayability
)
-internal fun Card.toHandCard(player: Player): HandCard {
- val playability: CardPlayability = computePlayabilityBy(player)
- return HandCard(
- name = name,
- color = color,
- requirements = requirements,
- chainParent = chainParent,
- chainChildren = chainChildren,
- image = image,
- back = back,
- isChainable = playability.isChainable,
- isFree = playability.isFree,
- isPlayable = playability.isPlayable,
- minPrice = playability.minPrice,
- cheapestTransactions = playability.cheapestTransactions
- )
-}
+internal fun Card.toHandCard(player: Player): HandCard = HandCard(
+ name = name,
+ color = color,
+ requirements = requirements,
+ chainParent = chainParent,
+ chainChildren = chainChildren,
+ image = image,
+ back = back,
+ playability = computePlayabilityBy(player)
+)
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
index 94c1a81a..66f657b7 100644
--- 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
@@ -8,14 +8,6 @@ import org.luxons.sevenwonders.game.resources.noTransactions
data class CardBack(val image: String)
-data class CardPlayability(
- val isPlayable: Boolean,
- val isFree: Boolean = false,
- val isChainable: Boolean = false,
- val minPrice: Int = Int.MAX_VALUE,
- val cheapestTransactions: Set<ResourceTransactions> = emptySet()
-)
-
internal data class Card(
val name: String,
val color: Color,
@@ -26,40 +18,18 @@ internal data class Card(
val image: String,
val back: CardBack
) {
- fun computePlayabilityBy(player: Player): CardPlayability {
- if (!isAllowedOnBoard(player.board)) {
- return nonPlayable()
- }
- if (isParentOnBoard(player.board)) {
- return chainablePlayability()
- }
- return requirementsPlayability(player)
+ fun computePlayabilityBy(player: Player): CardPlayability = when {
+ isAlreadyOnBoard(player.board) -> CardPlayability.incompatibleWithBoard() // cannot play twice the same card
+ isParentOnBoard(player.board) -> CardPlayability.chainable()
+ else -> CardPlayability.requirementDependent(requirements.computeSatisfaction(player))
}
- private fun nonPlayable() = CardPlayability(isPlayable = false)
+ fun isPlayableOnBoardWith(board: Board, transactions: ResourceTransactions) =
+ isChainableOn(board) || requirements.areMetWithHelpBy(board, transactions)
- private fun chainablePlayability(): CardPlayability = CardPlayability(
- isPlayable = true,
- isFree = true,
- isChainable = true,
- minPrice = 0,
- cheapestTransactions = setOf(noTransactions())
- )
+ private fun isChainableOn(board: Board): Boolean = !isAlreadyOnBoard(board) && isParentOnBoard(board)
- private fun requirementsPlayability(player: Player): CardPlayability {
- val satisfaction = requirements.computeSatisfaction(player)
- return CardPlayability(
- isPlayable = satisfaction is RequirementsSatisfaction.Acceptable,
- isFree = satisfaction.minPrice == 0,
- isChainable = false,
- minPrice = satisfaction.minPrice,
- cheapestTransactions = satisfaction.cheapestTransactions
- )
- }
-
- fun isChainableOn(board: Board): Boolean = isAllowedOnBoard(board) && isParentOnBoard(board)
-
- private fun isAllowedOnBoard(board: Board): Boolean = !board.isPlayed(name) // cannot play twice the same card
+ private fun isAlreadyOnBoard(board: Board): Boolean = board.isPlayed(name)
private fun isParentOnBoard(board: Board): Boolean = chainParent != null && board.isPlayed(chainParent)
@@ -80,3 +50,36 @@ enum class Color {
RED,
PURPLE
}
+
+data class CardPlayability(
+ val isPlayable: Boolean,
+ val isFree: Boolean = false,
+ val isChainable: Boolean = false,
+ val minPrice: Int = Int.MAX_VALUE,
+ val cheapestTransactions: Set<ResourceTransactions> = emptySet(),
+ val playabilityLevel: PlayabilityLevel
+) {
+ companion object {
+
+ internal fun incompatibleWithBoard(): CardPlayability =
+ CardPlayability(isPlayable = false, playabilityLevel = PlayabilityLevel.INCOMPATIBLE_WITH_BOARD)
+
+ internal fun chainable(): CardPlayability = CardPlayability(
+ isPlayable = true,
+ isFree = true,
+ isChainable = true,
+ minPrice = 0,
+ cheapestTransactions = setOf(noTransactions()),
+ playabilityLevel = PlayabilityLevel.CHAINABLE
+ )
+
+ internal fun requirementDependent(satisfaction: RequirementsSatisfaction): CardPlayability = CardPlayability(
+ isPlayable = satisfaction.satisfied,
+ isFree = satisfaction.minPrice == 0,
+ isChainable = false,
+ minPrice = satisfaction.minPrice,
+ cheapestTransactions = satisfaction.cheapestTransactions,
+ playabilityLevel = satisfaction.level
+ )
+ }
+}
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
index 30c67683..36a09ba2 100644
--- 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
@@ -40,7 +40,7 @@ data class Requirements internal constructor(
val (minPriceForResources, possibleTransactions) = bestSolution(resources, player)
val minPrice = minPriceForResources + gold
if (possibleTransactions.isEmpty()) {
- return RequirementsSatisfaction.resourcesUnavailable()
+ return RequirementsSatisfaction.unavailableResources()
}
if (player.board.gold < minPrice) {
return RequirementsSatisfaction.missingGoldForResources(minPrice, possibleTransactions)
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/RequirementsSatisfaction.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/RequirementsSatisfaction.kt
index de78abad..7f21a9fd 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/RequirementsSatisfaction.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/RequirementsSatisfaction.kt
@@ -3,82 +3,49 @@ package org.luxons.sevenwonders.game.cards
import org.luxons.sevenwonders.game.resources.ResourceTransactions
import org.luxons.sevenwonders.game.resources.noTransactions
-internal sealed class RequirementsSatisfaction(
+enum class PlayabilityLevel {
+ CHAINABLE,
+ NO_REQUIREMENTS,
+ ENOUGH_RESOURCES,
+ ENOUGH_GOLD,
+ ENOUGH_GOLD_AND_RES,
+ REQUIRES_HELP,
+ MISSING_REQUIRED_GOLD,
+ MISSING_GOLD_FOR_RES,
+ UNAVAILABLE_RESOURCES,
+ INCOMPATIBLE_WITH_BOARD
+}
+
+internal data class RequirementsSatisfaction(
val satisfied: Boolean,
+ val level: PlayabilityLevel,
val minPrice: Int,
val cheapestTransactions: Set<ResourceTransactions>
) {
- sealed class Acceptable(minPrice: Int, cheapestTransactions: Set<ResourceTransactions>) :
- RequirementsSatisfaction(true, minPrice, cheapestTransactions) {
-
- sealed class WithoutHelp(minPrice: Int) : Acceptable(minPrice, setOf(noTransactions())) {
-
- sealed class Free : WithoutHelp(0) {
-
- object NoRequirement : Free()
-
- object EnoughResources : Free()
- }
-
- class EnoughGold(minPrice: Int) : WithoutHelp(minPrice)
-
- class EnoughResourcesAndGold(minPrice: Int) : WithoutHelp(minPrice)
- }
-
- class WithHelp(minPrice: Int, cheapestTransactions: Set<ResourceTransactions>) :
- Acceptable(minPrice, cheapestTransactions)
- }
-
- sealed class Insufficient(minPrice: Int, cheapestTransactions: Set<ResourceTransactions>) :
- RequirementsSatisfaction(false, minPrice, cheapestTransactions) {
-
- class MissingRequiredGold(minPrice: Int) : Insufficient(minPrice, emptySet())
-
- class MissingGoldForResources(minPrice: Int, cheapestTransactions: Set<ResourceTransactions>) :
- Insufficient(minPrice, cheapestTransactions)
-
- object UnavailableResources : Insufficient(Int.MAX_VALUE, emptySet())
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as RequirementsSatisfaction
-
- if (satisfied != other.satisfied) return false
- if (minPrice != other.minPrice) return false
- if (cheapestTransactions != other.cheapestTransactions) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = satisfied.hashCode()
- result = 31 * result + minPrice
- result = 31 * result + cheapestTransactions.hashCode()
- return result
- }
-
companion object {
- internal fun noRequirements() = RequirementsSatisfaction.Acceptable.WithoutHelp.Free.NoRequirement
+ internal fun noRequirements() =
+ RequirementsSatisfaction(true, PlayabilityLevel.NO_REQUIREMENTS, 0, setOf(noTransactions()))
- internal fun enoughResources() = RequirementsSatisfaction.Acceptable.WithoutHelp.Free.EnoughResources
+ internal fun enoughResources() =
+ RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_RESOURCES, 0, setOf(noTransactions()))
- internal fun enoughGold(minPrice: Int) = RequirementsSatisfaction.Acceptable.WithoutHelp.EnoughGold(minPrice)
+ internal fun enoughGold(minPrice: Int) =
+ RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_GOLD, minPrice, setOf(noTransactions()))
internal fun enoughResourcesAndGold(minPrice: Int) =
- RequirementsSatisfaction.Acceptable.WithoutHelp.EnoughResourcesAndGold(minPrice)
+ RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_GOLD_AND_RES, minPrice, setOf(noTransactions()))
internal fun metWithHelp(minPrice: Int, cheapestTransactions: Set<ResourceTransactions>) =
- RequirementsSatisfaction.Acceptable.WithHelp(minPrice, cheapestTransactions)
+ RequirementsSatisfaction(true, PlayabilityLevel.REQUIRES_HELP, minPrice, cheapestTransactions)
- internal fun missingRequiredGold(minPrice: Int) = RequirementsSatisfaction.Insufficient.MissingRequiredGold(minPrice)
+ internal fun missingRequiredGold(minPrice: Int) =
+ RequirementsSatisfaction(false, PlayabilityLevel.MISSING_REQUIRED_GOLD, minPrice, emptySet())
internal fun missingGoldForResources(minPrice: Int, cheapestTransactions: Set<ResourceTransactions>) =
- RequirementsSatisfaction.Insufficient.MissingGoldForResources(minPrice, cheapestTransactions)
+ RequirementsSatisfaction(false, PlayabilityLevel.MISSING_GOLD_FOR_RES, minPrice, cheapestTransactions)
- internal fun resourcesUnavailable() = RequirementsSatisfaction.Insufficient.UnavailableResources
+ internal fun unavailableResources() =
+ RequirementsSatisfaction(false, PlayabilityLevel.UNAVAILABLE_RESOURCES, Int.MAX_VALUE, emptySet())
}
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.kt
index 4520c821..99c364c5 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.kt
@@ -19,7 +19,7 @@ internal class ResourceTypeSerializer : JsonSerializer<ResourceType>, JsonDeseri
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ResourceType {
val str = json.asString
if (str.isEmpty()) {
- throw IllegalArgumentException("Empty string is not a valid resource type")
+ throw IllegalArgumentException("Empty string is not a valid resource level")
}
return ResourceType.fromSymbol(str[0])
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.kt
index 46ebeb25..d6dc9ae3 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.kt
@@ -50,6 +50,6 @@ internal class ScienceProgressSerializer : JsonSerializer<ScienceProgress>, Json
private fun deserializeScienceType(json: JsonElement, context: JsonDeserializationContext): ScienceType {
return context.deserialize<ScienceType>(json, ScienceType::class.java)
- ?: throw IllegalArgumentException("Invalid science type " + json.asString)
+ ?: throw IllegalArgumentException("Invalid science level " + json.asString)
}
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/PlayCardMove.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/PlayCardMove.kt
index f0c18bc7..3596b164 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/PlayCardMove.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/PlayCardMove.kt
@@ -9,8 +9,7 @@ internal class PlayCardMove(move: PlayerMove, card: Card, player: PlayerContext)
CardFromHandMove(move, card, player) {
init {
- val board = player.board
- if (!card.isChainableOn(board) && !card.requirements.areMetWithHelpBy(board, transactions)) {
+ if (!card.isPlayableOnBoardWith(player.board, transactions)) {
throw InvalidMoveException(this, "requirements not met to play the card ${card.name}")
}
}
diff --git a/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt b/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt
index 5b9d36d0..fd857168 100644
--- a/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt
+++ b/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/GameTest.kt
@@ -77,10 +77,10 @@ class GameTest {
}
private fun createPlayCardMove(turnInfo: PlayerTurnInfo): PlayerMove {
- val playableCard = turnInfo.hand.firstOrNull { it.isPlayable }
+ val playableCard = turnInfo.hand.firstOrNull { it.playability.isPlayable }
return if (playableCard != null) {
- PlayerMove(MoveType.PLAY, playableCard.name, playableCard.cheapestTransactions.first())
+ PlayerMove(MoveType.PLAY, playableCard.name, playableCard.playability.cheapestTransactions.first())
} else {
PlayerMove(MoveType.DISCARD, turnInfo.hand.first().name)
}
diff --git a/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/RequirementsTest.kt b/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/RequirementsTest.kt
index 4ed244b6..5f6c18b0 100644
--- a/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/RequirementsTest.kt
+++ b/game-engine/src/test/kotlin/org/luxons/sevenwonders/game/cards/RequirementsTest.kt
@@ -123,7 +123,7 @@ class RequirementsTest {
)
assertEquals(RequirementsSatisfaction.metWithHelp(2, transactions), satisfaction)
} else {
- assertEquals(RequirementsSatisfaction.resourcesUnavailable(), satisfaction)
+ assertEquals(RequirementsSatisfaction.unavailableResources(), satisfaction)
}
}
bgstack15