summaryrefslogtreecommitdiff
path: root/game-engine/src/main/kotlin/org/luxons
diff options
context:
space:
mode:
authorJoffrey BION <joffrey.bion@gmail.com>2018-07-16 02:14:44 +0200
committerJoffrey BION <joffrey.bion@gmail.com>2018-07-16 02:14:44 +0200
commit16e248ea36b8f61e4e94cf1c9c229929340db4d1 (patch)
treede4d8dd8faceefb9d82b07edcf6103fd9d0f5848 /game-engine/src/main/kotlin/org/luxons
parentAdd DTO classes for webservice output (diff)
downloadseven-wonders-16e248ea36b8f61e4e94cf1c9c229929340db4d1.tar.gz
seven-wonders-16e248ea36b8f61e4e94cf1c9c229929340db4d1.tar.bz2
seven-wonders-16e248ea36b8f61e4e94cf1c9c229929340db4d1.zip
Rework resources representations
Diffstat (limited to 'game-engine/src/main/kotlin/org/luxons')
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/cards/Requirements.kt13
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.kt4
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.kt9
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/Move.kt2
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/BestPriceCalculator.kt59
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Production.kt66
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransaction.kt15
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransactions.kt39
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Resources.kt95
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/TradingRules.kt20
10 files changed, 136 insertions, 186 deletions
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 6a38965f..ad50a1c6 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
@@ -4,11 +4,14 @@ import org.luxons.sevenwonders.game.Player
import org.luxons.sevenwonders.game.boards.Board
import org.luxons.sevenwonders.game.resources.ResourceTransactions
import org.luxons.sevenwonders.game.resources.Resources
-import org.luxons.sevenwonders.game.resources.bestPrice
+import org.luxons.sevenwonders.game.resources.asResources
+import org.luxons.sevenwonders.game.resources.bestSolution
+import org.luxons.sevenwonders.game.resources.emptyResources
+import org.luxons.sevenwonders.game.resources.execute
data class Requirements internal constructor(
val gold: Int = 0,
- val resources: Resources = Resources()
+ val resources: Resources = emptyResources()
) {
/**
* Returns whether the given [board] meets these requirements on its own.
@@ -52,8 +55,8 @@ data class Requirements internal constructor(
if (producesRequiredResources(board)) {
return true
}
- val bestPrice = bestPrice(resources, player)
- return bestPrice != null && bestPrice <= board.gold - gold
+ val solution = bestSolution(resources, player)
+ return !solution.possibleTransactions.isEmpty() && solution.price <= board.gold - gold
}
private fun hasRequiredGold(board: Board): Boolean {
@@ -71,7 +74,7 @@ data class Requirements internal constructor(
private fun producesRequiredResourcesWithHelp(board: Board, transactions: ResourceTransactions): Boolean {
val totalBoughtResources = transactions.asResources()
- val remainingResources = this.resources.minus(totalBoughtResources)
+ val remainingResources = resources - totalBoughtResources
return board.production.contains(remainingResources)
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.kt
index 06b46bb2..b766dd31 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.kt
@@ -15,10 +15,10 @@ import java.lang.reflect.Type
internal class ProductionSerializer : JsonSerializer<Production>, JsonDeserializer<Production> {
override fun serialize(production: Production, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
- val fixedResources = production.fixedResources
+ val fixedResources = production.getFixedResources()
val choices = production.getAlternativeResources()
return when {
- fixedResources.isEmpty -> serializeAsChoice(choices, context)
+ fixedResources.isEmpty() -> serializeAsChoice(choices, context)
choices.isEmpty() -> serializeAsResources(fixedResources, context)
else -> throw IllegalArgumentException("Cannot serialize a production with mixed fixed resources and choices")
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.kt
index c6c6a962..fcf66d79 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.kt
@@ -10,22 +10,19 @@ import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import org.luxons.sevenwonders.game.resources.ResourceType
import org.luxons.sevenwonders.game.resources.Resources
+import org.luxons.sevenwonders.game.resources.toResources
import java.lang.reflect.Type
internal class ResourcesSerializer : JsonSerializer<Resources>, JsonDeserializer<Resources> {
override fun serialize(resources: Resources, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
- val s = resources.asList().map { it.symbol }.joinToString("")
+ val s = resources.toList().map { it.symbol }.joinToString("")
return if (s.isEmpty()) JsonNull.INSTANCE else JsonPrimitive(s)
}
@Throws(JsonParseException::class)
override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Resources {
val s = json.asString
- val resources = Resources()
- for (c in s.toCharArray()) {
- resources.add(ResourceType.fromSymbol(c), 1)
- }
- return resources
+ return s.toCharArray().map { ResourceType.fromSymbol(it) }.toResources()
}
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/Move.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/Move.kt
index ce50e3e0..ed982abb 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/Move.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/moves/Move.kt
@@ -14,7 +14,7 @@ abstract class Move internal constructor(
val type: MoveType = move.type
// TODO restore visibility to public
- internal val transactions: ResourceTransactions = ResourceTransactions(move.transactions)
+ internal val transactions: ResourceTransactions = move.transactions
internal abstract fun place(discardedCards: MutableList<Card>, settings: Settings)
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/BestPriceCalculator.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/BestPriceCalculator.kt
index 3b86fd97..4159cea3 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/BestPriceCalculator.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/BestPriceCalculator.kt
@@ -4,20 +4,10 @@ import org.luxons.sevenwonders.game.Player
import java.util.ArrayList
import java.util.EnumSet
-internal fun bestPrice(resources: Resources, player: Player): Int? {
- return bestSolution(resources, player)?.price
-}
-
-internal fun bestTransaction(resources: Resources, player: Player): ResourceTransactions? {
- return bestSolution(resources, player)?.transactions
-}
+internal fun bestSolution(resources: Resources, player: Player): TransactionPlan =
+ BestPriceCalculator(resources, player).computeBestSolution()
-internal fun bestSolution(resources: Resources, player: Player): TransactionPlan? {
- val calculator = BestPriceCalculator(resources, player)
- return calculator.computeBestSolution()
-}
-
-internal data class TransactionPlan(val price: Int, val transactions: ResourceTransactions)
+internal data class TransactionPlan(val price: Int, val possibleTransactions: Set<ResourceTransactions>)
private class ResourcePool(
val provider: Provider?,
@@ -30,16 +20,16 @@ private class ResourcePool(
private class BestPriceCalculator(resourcesToPay: Resources, player: Player) {
private val pools: List<ResourcePool>
- private val resourcesLeftToPay: Resources
- private val boughtResources: ResourceTransactions = ResourceTransactions()
+ private val resourcesLeftToPay: MutableResources
+ private val boughtResources: MutableMap<Provider, MutableResources> = HashMap()
private var pricePaid: Int = 0
- var bestSolution: ResourceTransactions? = null
- var bestPrice: Int = Integer.MAX_VALUE
+ private var bestSolutions: MutableSet<ResourceTransactions> = mutableSetOf()
+ private var bestPrice: Int = Integer.MAX_VALUE
init {
val board = player.board
- this.resourcesLeftToPay = resourcesToPay.minus(board.production.fixedResources)
+ this.resourcesLeftToPay = resourcesToPay.minus(board.production.getFixedResources()).toMutableResources()
this.pools = createResourcePools(player)
}
@@ -56,24 +46,25 @@ private class BestPriceCalculator(resourcesToPay: Resources, player: Player) {
for (provider in providers) {
val providerBoard = player.getBoard(provider.boardPosition)
- val pool = ResourcePool(provider, rules, providerBoard.publicProduction.asChoices().map { it.toMutableSet() }.toSet())
+ val choices = providerBoard.publicProduction.asChoices().map { it.toMutableSet() }.toSet()
+ val pool = ResourcePool(provider, rules, choices)
pools.add(pool)
}
return pools
}
- fun computeBestSolution(): TransactionPlan? {
+ fun computeBestSolution(): TransactionPlan {
computePossibilities()
- return if (bestSolution == null) null else TransactionPlan(bestPrice, bestSolution!!)
+ return TransactionPlan(bestPrice, bestSolutions)
}
private fun computePossibilities() {
- if (resourcesLeftToPay.isEmpty) {
+ if (resourcesLeftToPay.isEmpty()) {
updateBestSolutionIfNeeded()
return
}
for (type in ResourceType.values()) {
- if (resourcesLeftToPay.getQuantity(type) > 0) {
+ if (resourcesLeftToPay[type] > 0) {
for (pool in pools) {
if (pool.provider == null) {
computeSelfPossibilities(type, pool)
@@ -94,14 +85,22 @@ private class BestPriceCalculator(resourcesToPay: Resources, player: Player) {
private fun computeNeighbourPossibilities(pool: ResourcePool, type: ResourceType, provider: Provider) {
val cost = pool.getCost(type)
resourcesLeftToPay.remove(type, 1)
- boughtResources.add(provider, Resources(type))
- pricePaid += cost
+ buyOne(provider, type, cost)
computePossibilitiesWhenUsing(type, pool)
- pricePaid -= cost
- boughtResources.remove(provider, Resources(type))
+ unbuyOne(provider, type, cost)
resourcesLeftToPay.add(type, 1)
}
+ fun buyOne(provider: Provider, type: ResourceType, cost: Int) {
+ boughtResources.getOrPut(provider) { MutableResources() }.add(type, 1)
+ pricePaid += cost
+ }
+
+ fun unbuyOne(provider: Provider, type: ResourceType, cost: Int) {
+ pricePaid -= cost
+ boughtResources.get(provider)!!.remove(type, 1)
+ }
+
private fun computePossibilitiesWhenUsing(type: ResourceType, pool: ResourcePool) {
for (choice in pool.choices) {
if (choice.contains(type)) {
@@ -114,9 +113,13 @@ private class BestPriceCalculator(resourcesToPay: Resources, player: Player) {
}
private fun updateBestSolutionIfNeeded() {
+ if (pricePaid > bestPrice) return
+
if (pricePaid < bestPrice) {
bestPrice = pricePaid
- bestSolution = ResourceTransactions(boughtResources.asList())
+ bestSolutions.clear()
}
+ // avoid mutating the resources from the transactions
+ bestSolutions.add(boughtResources.mapValues { MutableResources(HashMap(it.value.quantities)) }.toTransactions())
}
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Production.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Production.kt
index 2dd6d60f..0b069677 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Production.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Production.kt
@@ -3,35 +3,30 @@ package org.luxons.sevenwonders.game.resources
import java.util.Arrays
import java.util.EnumSet
-class Production internal constructor() {
-
- val fixedResources = Resources()
+data class Production internal constructor(
+ private val fixedResources: MutableResources = mutableResourcesOf(),
private val alternativeResources: MutableSet<Set<ResourceType>> = mutableSetOf()
+) {
+ fun getFixedResources(): Resources = fixedResources
- fun getAlternativeResources(): Set<Set<ResourceType>> {
- return alternativeResources
- }
+ fun getAlternativeResources(): Set<Set<ResourceType>> = alternativeResources
- fun addFixedResource(type: ResourceType, quantity: Int) {
- fixedResources.add(type, quantity)
- }
+ fun addFixedResource(type: ResourceType, quantity: Int) = fixedResources.add(type, quantity)
fun addChoice(vararg options: ResourceType) {
val optionSet = EnumSet.copyOf(Arrays.asList(*options))
alternativeResources.add(optionSet)
}
- fun addAll(resources: Resources) {
- fixedResources.addAll(resources)
- }
+ fun addAll(resources: Resources) = fixedResources.add(resources)
fun addAll(production: Production) {
- fixedResources.addAll(production.fixedResources)
+ fixedResources.add(production.fixedResources)
alternativeResources.addAll(production.getAlternativeResources())
}
internal fun asChoices(): Set<Set<ResourceType>> {
- val fixedAsChoices = fixedResources.asList().map{ EnumSet.of(it) }.toSet()
+ val fixedAsChoices = fixedResources.toList().map { EnumSet.of(it) }.toSet()
return fixedAsChoices + alternativeResources
}
@@ -42,20 +37,22 @@ class Production internal constructor() {
return containedInAlternatives(resources - fixedResources)
}
- private fun containedInAlternatives(resources: Resources): Boolean {
- return containedInAlternatives(resources, alternativeResources)
- }
+ private fun containedInAlternatives(resources: Resources): Boolean =
+ containedInAlternatives(resources.toMutableResources(), alternativeResources)
- private fun containedInAlternatives(resources: Resources, alternatives: MutableSet<Set<ResourceType>>): Boolean {
- if (resources.isEmpty) {
+ private fun containedInAlternatives(
+ resources: MutableResources,
+ alternatives: MutableSet<Set<ResourceType>>
+ ): Boolean {
+ if (resources.isEmpty()) {
return true
}
for (type in ResourceType.values()) {
- if (resources.getQuantity(type) <= 0) {
+ if (resources[type] <= 0) {
continue
}
- val candidate = findFirstAlternativeContaining(alternatives, type)
- ?: return false // no alternative produces the resource of this entry
+ // return if no alternative produces the resource of this entry
+ val candidate = alternatives.firstOrNull { a -> a.contains(type) } ?: return false
resources.remove(type, 1)
alternatives.remove(candidate)
val remainingAreContainedToo = containedInAlternatives(resources, alternatives)
@@ -67,29 +64,4 @@ class Production internal constructor() {
}
return false
}
-
- private fun findFirstAlternativeContaining(
- alternatives: Set<Set<ResourceType>>,
- type: ResourceType
- ): Set<ResourceType>? {
- return alternatives.stream().filter { a -> a.contains(type) }.findAny().orElse(null)
- }
-
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
-
- other as Production
-
- if (fixedResources != other.fixedResources) return false
- if (alternativeResources != other.alternativeResources) return false
-
- return true
- }
-
- override fun hashCode(): Int {
- var result = fixedResources.hashCode()
- result = 31 * result + alternativeResources.hashCode()
- return result
- }
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransaction.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransaction.kt
deleted file mode 100644
index 30f4d014..00000000
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransaction.kt
+++ /dev/null
@@ -1,15 +0,0 @@
-package org.luxons.sevenwonders.game.resources
-
-import org.luxons.sevenwonders.game.Player
-
-data class ResourceTransaction(val provider: Provider, val resources: Resources) {
-
- internal fun execute(player: Player) {
- val board = player.board
- val price = board.tradingRules.computeCost(this)
- board.removeGold(price)
- val providerPosition = provider.boardPosition
- val providerBoard = player.getBoard(providerPosition)
- providerBoard.addGold(price)
- }
-}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransactions.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransactions.kt
index 52d10064..c7ab3636 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransactions.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/ResourceTransactions.kt
@@ -2,36 +2,25 @@ package org.luxons.sevenwonders.game.resources
import org.luxons.sevenwonders.game.Player
-internal data class ResourceTransactions(private val resourcesByProvider: MutableMap<Provider, Resources> = mutableMapOf()) {
+typealias ResourceTransactions = Collection<ResourceTransaction>
- constructor(transactions: Collection<ResourceTransaction>) : this() {
- transactions.forEach { t -> add(t.provider, t.resources) }
- }
+fun noTransactions(): ResourceTransactions = emptyList()
- fun add(provider: Provider, resources: Resources) {
- resourcesByProvider.merge(provider, resources) { old, new -> old + new }
- }
+fun Map<Provider, Resources>.toTransactions() =
+ filter { (_, res) -> !res.isEmpty() }.map { (p, res) -> ResourceTransaction(p, res) }
- fun remove(provider: Provider, resources: Resources) {
- resourcesByProvider.compute(provider) { _, prevResources ->
- if (prevResources == null) {
- throw IllegalStateException("Cannot remove resources from resource transactions")
- }
- prevResources.minus(resources)
- }
- }
+fun ResourceTransactions.asResources(): Resources = map { it.resources }.merge()
- fun execute(player: Player) {
- asList().forEach { it.execute(player) }
- }
+internal fun ResourceTransactions.execute(player: Player) = forEach { it.execute(player) }
- fun asList(): List<ResourceTransaction> {
- return resourcesByProvider
- .filter { (_, resources) -> !resources.isEmpty }
- .map { (provider, resources) -> ResourceTransaction(provider, resources) }
- }
+data class ResourceTransaction(val provider: Provider, val resources: Resources) {
- fun asResources(): Resources {
- return resourcesByProvider.values.fold(Resources(), Resources::plus)
+ internal fun execute(player: Player) {
+ val board = player.board
+ val price = board.tradingRules.computeCost(this)
+ board.removeGold(price)
+ val providerPosition = provider.boardPosition
+ val providerBoard = player.getBoard(providerPosition)
+ providerBoard.addGold(price)
}
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Resources.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Resources.kt
index 96ad0b20..15673bd2 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Resources.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/Resources.kt
@@ -2,71 +2,84 @@ package org.luxons.sevenwonders.game.resources
import java.util.NoSuchElementException
-class Resources(quantities: Map<ResourceType, Int> = emptyMap()) {
+fun emptyResources(): Resources = MutableResources()
- private val quantities: MutableMap<ResourceType, Int> = quantities.toMutableMap()
+fun resourcesOf(singleResource: ResourceType): Resources = MutableResources(mutableMapOf(singleResource to 1))
- constructor(singleResource: ResourceType): this(mapOf(singleResource to 1))
+fun resourcesOf(vararg resources: ResourceType): Resources =
+ resources.fold(MutableResources()) { rs, r -> rs.add(r, 1); rs }
- val isEmpty: Boolean
- get() = size() == 0
+fun resourcesOf(resources: Iterable<ResourceType>): Resources =
+ resources.fold(MutableResources()) { rs, r -> rs.add(r, 1); rs }
- fun add(type: ResourceType, quantity: Int) {
- quantities.merge(type, quantity) { x, y -> x + y }
- }
+fun resourcesOf(vararg resources: Pair<ResourceType, Int>): Resources =
+ resources.fold(MutableResources()) { rs, (type, qty) -> rs.add(type, qty); rs }
- fun remove(type: ResourceType, quantity: Int) {
- if (getQuantity(type) < quantity) {
- throw NoSuchElementException("Can't remove $quantity resources of type $type")
- }
- quantities.computeIfPresent(type) { _, oldQty -> oldQty - quantity }
- }
+internal fun mutableResourcesOf() = MutableResources()
- fun addAll(resources: Resources) {
- resources.quantities.forEach { type, quantity -> this.add(type, quantity) }
- }
+internal fun mutableResourcesOf(vararg resources: Pair<ResourceType, Int>) =
+ resources.fold(MutableResources()) { rs, (type, qty) -> rs.add(type, qty); rs }
+
+fun Iterable<ResourceType>.toResources(): Resources = resourcesOf(this)
+
+fun Iterable<Resources>.merge(): Resources = fold(MutableResources()) { r1, r2 -> r1.add(r2); r1}
+
+internal fun Resources.toMutableResources(): MutableResources {
+ val resources = MutableResources()
+ resources.add(this)
+ return resources
+}
- fun getQuantity(type: ResourceType): Int = quantities[type] ?: 0
+interface Resources {
- fun asList(): List<ResourceType> = quantities.flatMap { e -> List(e.value) { e.key } }
+ val quantities: Map<ResourceType, Int>
- fun containsAll(resources: Resources): Boolean = resources.quantities.all { it.value <= this.getQuantity(it.key) }
+ val size: Int
+ get() = quantities.map { it.value }.sum()
+
+ fun isEmpty(): Boolean = size == 0
+
+ operator fun get(key: ResourceType): Int = quantities.getOrDefault(key, 0)
+
+ fun containsAll(resources: Resources): Boolean = resources.quantities.all { it.value <= this[it.key] }
operator fun plus(resources: Resources): Resources {
- val new = Resources(this.quantities)
- new.addAll(resources)
+ val new = MutableResources()
+ new.add(this)
+ new.add(resources)
return new
}
- /**
- * Creates a new [Resources] object containing these resources minus the given resources.
- *
- * @param resources
- * the resources to subtract from these resources
- *
- * @return a new [Resources] object containing these resources minus the given resources.
- */
operator fun minus(resources: Resources): Resources {
- val diff = Resources()
+ val diff = MutableResources()
quantities.forEach { type, count ->
- val remainder = count - resources.getQuantity(type)
+ val remainder = count - resources[type]
diff.quantities[type] = Math.max(0, remainder)
}
return diff
}
- fun size(): Int = quantities.values.sum()
+ fun toList(): List<ResourceType> = quantities.flatMap { (type, quantity) -> List(quantity) { type } }
+}
- override fun equals(other: Any?): Boolean {
- if (this === other) return true
- if (javaClass != other?.javaClass) return false
+data class MutableResources(
+ override val quantities: MutableMap<ResourceType, Int> = mutableMapOf()
+) : Resources {
- other as Resources
+ fun add(type: ResourceType, quantity: Int) {
+ quantities.merge(type, quantity) { x, y -> x + y }
+ }
- if (quantities != other.quantities) return false
+ fun add(resources: Resources) = resources.quantities.forEach { type, quantity -> this.add(type, quantity) }
- return true
+ fun remove(type: ResourceType, quantity: Int) {
+ if (this[type] < quantity) {
+ throw NoSuchElementException("Can't remove $quantity resources of type $type")
+ }
+ quantities.computeIfPresent(type) { _, oldQty -> oldQty - quantity }
+ // to ensure equals() work properly
+ if (quantities[type] == 0) {
+ quantities.remove(type)
+ }
}
-
- override fun hashCode(): Int = quantities.hashCode()
}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/TradingRules.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/TradingRules.kt
index dc0dc489..3b3c81ea 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/TradingRules.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/resources/TradingRules.kt
@@ -15,22 +15,10 @@ class TradingRules internal constructor(private val defaultCost: Int) {
costs.computeIfAbsent(type) { mutableMapOf() }[provider] = cost
}
- internal fun computeCost(transactions: ResourceTransactions): Int {
- return transactions.asList().map { this.computeCost(it) }.sum()
- }
+ internal fun computeCost(transactions: ResourceTransactions): Int = transactions.map { computeCost(it) }.sum()
- internal fun computeCost(transaction: ResourceTransaction): Int {
- val resources = transaction.resources
- val provider = transaction.provider
- return computeCost(resources, provider)
- }
+ internal fun computeCost(transact: ResourceTransaction) = computeCost(transact.resources, transact.provider)
- private fun computeCost(resources: Resources, provider: Provider): Int {
- var total = 0
- for (type in ResourceType.values()) {
- val count = resources.getQuantity(type)
- total += getCost(type, provider) * count
- }
- return total
- }
+ private fun computeCost(resources: Resources, provider: Provider): Int =
+ resources.quantities.map { (type, qty) -> getCost(type, provider) * qty }.sum()
}
bgstack15