diff options
author | jbion <joffrey.bion@amadeus.com> | 2019-02-20 22:25:44 +0100 |
---|---|---|
committer | jbion <joffrey.bion@amadeus.com> | 2019-02-20 22:30:17 +0100 |
commit | 3d372676e5db4abf180ba194b9aacc9c4d02320a (patch) | |
tree | f47c53d2f41370016462f93e22e9277b2a5f097e /game-engine | |
parent | Improve hand playability calculations and API output (diff) | |
download | seven-wonders-3d372676e5db4abf180ba194b9aacc9c4d02320a.tar.gz seven-wonders-3d372676e5db4abf180ba194b9aacc9c4d02320a.tar.bz2 seven-wonders-3d372676e5db4abf180ba194b9aacc9c4d02320a.zip |
Simplify playability further
Diffstat (limited to 'game-engine')
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) } } |