summaryrefslogtreecommitdiff
path: root/sw-engine/src
diff options
context:
space:
mode:
Diffstat (limited to 'sw-engine/src')
-rw-r--r--sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Cards.kt8
-rw-r--r--sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt17
-rw-r--r--sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsSatisfaction.kt27
-rw-r--r--sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/TransactionOptionsCalculator.kt (renamed from sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/BestPriceCalculator.kt)61
-rw-r--r--sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/wonders/Wonder.kt5
-rw-r--r--sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt17
-rw-r--r--sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsTest.kt4
-rw-r--r--sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/resources/BestPriceCalculatorTest.kt175
-rw-r--r--sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/resources/TransactionOptionsCalculatorTest.kt201
9 files changed, 269 insertions, 246 deletions
diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Cards.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Cards.kt
index d5468283..b097734a 100644
--- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Cards.kt
+++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Cards.kt
@@ -8,7 +8,7 @@ import org.luxons.sevenwonders.model.cards.CardPlayability
import org.luxons.sevenwonders.model.cards.Color
import org.luxons.sevenwonders.model.cards.PlayabilityLevel
import org.luxons.sevenwonders.model.resources.ResourceTransactions
-import org.luxons.sevenwonders.model.resources.noTransactions
+import org.luxons.sevenwonders.model.resources.singleOptionNoTransactionNeeded
internal data class Card(
val name: String,
@@ -55,7 +55,7 @@ private object Playability {
isPlayable = true,
isChainable = true,
minPrice = 0,
- cheapestTransactions = setOf(noTransactions()),
+ transactionOptions = singleOptionNoTransactionNeeded(),
playabilityLevel = PlayabilityLevel.CHAINABLE,
)
@@ -63,7 +63,7 @@ private object Playability {
isPlayable = satisfaction.satisfied,
isChainable = false,
minPrice = satisfaction.minPrice,
- cheapestTransactions = satisfaction.cheapestTransactions,
+ transactionOptions = satisfaction.transactionOptions,
playabilityLevel = satisfaction.level,
)
@@ -71,7 +71,7 @@ private object Playability {
isPlayable = true,
isChainable = false,
minPrice = 0,
- cheapestTransactions = setOf(noTransactions()),
+ transactionOptions = singleOptionNoTransactionNeeded(),
playabilityLevel = PlayabilityLevel.SPECIAL_FREE,
)
}
diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt
index 945f4463..b78a5057 100644
--- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt
+++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/Requirements.kt
@@ -2,12 +2,9 @@ package org.luxons.sevenwonders.engine.cards
import org.luxons.sevenwonders.engine.Player
import org.luxons.sevenwonders.engine.boards.Board
-import org.luxons.sevenwonders.engine.resources.Resources
-import org.luxons.sevenwonders.engine.resources.asResources
-import org.luxons.sevenwonders.engine.resources.bestSolution
-import org.luxons.sevenwonders.engine.resources.emptyResources
-import org.luxons.sevenwonders.engine.resources.execute
+import org.luxons.sevenwonders.engine.resources.*
import org.luxons.sevenwonders.model.resources.ResourceTransactions
+import org.luxons.sevenwonders.model.resources.bestPrice
data class Requirements internal constructor(
val gold: Int = 0,
@@ -37,15 +34,15 @@ data class Requirements internal constructor(
}
private fun satisfactionWithPotentialHelp(player: Player): RequirementsSatisfaction {
- val (minPriceForResources, possibleTransactions) = bestSolution(resources, player)
- val minPrice = minPriceForResources + gold
- if (possibleTransactions.isEmpty()) {
+ val options = transactionOptions(resources, player)
+ val minPrice = options.bestPrice + gold
+ if (options.isEmpty()) {
return RequirementsSatisfaction.unavailableResources()
}
if (player.board.gold < minPrice) {
- return RequirementsSatisfaction.missingGoldForResources(minPrice, possibleTransactions)
+ return RequirementsSatisfaction.missingGoldForResources(minPrice, options)
}
- return RequirementsSatisfaction.metWithHelp(minPrice, possibleTransactions)
+ return RequirementsSatisfaction.metWithHelp(minPrice, options)
}
/**
diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsSatisfaction.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsSatisfaction.kt
index f9566981..55ec1edb 100644
--- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsSatisfaction.kt
+++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsSatisfaction.kt
@@ -1,39 +1,40 @@
package org.luxons.sevenwonders.engine.cards
import org.luxons.sevenwonders.model.cards.PlayabilityLevel
-import org.luxons.sevenwonders.model.resources.PricedResourceTransactions
-import org.luxons.sevenwonders.model.resources.noTransactions
+import org.luxons.sevenwonders.model.resources.ResourceTransactionOptions
+import org.luxons.sevenwonders.model.resources.noTransactionOptions
+import org.luxons.sevenwonders.model.resources.singleOptionNoTransactionNeeded
internal data class RequirementsSatisfaction(
val satisfied: Boolean,
val level: PlayabilityLevel,
val minPrice: Int,
- val cheapestTransactions: Set<PricedResourceTransactions>,
+ val transactionOptions: ResourceTransactionOptions,
) {
companion object {
internal fun noRequirements() =
- RequirementsSatisfaction(true, PlayabilityLevel.NO_REQUIREMENTS, 0, setOf(noTransactions()))
+ RequirementsSatisfaction(true, PlayabilityLevel.NO_REQUIREMENTS, 0, singleOptionNoTransactionNeeded())
internal fun enoughResources() =
- RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_RESOURCES, 0, setOf(noTransactions()))
+ RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_RESOURCES, 0, singleOptionNoTransactionNeeded())
internal fun enoughGold(minPrice: Int) =
- RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_GOLD, minPrice, setOf(noTransactions()))
+ RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_GOLD, minPrice, singleOptionNoTransactionNeeded())
internal fun enoughResourcesAndGold(minPrice: Int) =
- RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_GOLD_AND_RES, minPrice, setOf(noTransactions()))
+ RequirementsSatisfaction(true, PlayabilityLevel.ENOUGH_GOLD_AND_RES, minPrice, singleOptionNoTransactionNeeded())
- internal fun metWithHelp(minPrice: Int, cheapestTransactions: Set<PricedResourceTransactions>) =
- RequirementsSatisfaction(true, PlayabilityLevel.REQUIRES_HELP, minPrice, cheapestTransactions)
+ internal fun metWithHelp(minPrice: Int, transactionOptions: ResourceTransactionOptions) =
+ RequirementsSatisfaction(true, PlayabilityLevel.REQUIRES_HELP, minPrice, transactionOptions)
internal fun missingRequiredGold(minPrice: Int) =
- RequirementsSatisfaction(false, PlayabilityLevel.MISSING_REQUIRED_GOLD, minPrice, emptySet())
+ RequirementsSatisfaction(false, PlayabilityLevel.MISSING_REQUIRED_GOLD, minPrice, noTransactionOptions())
- internal fun missingGoldForResources(minPrice: Int, cheapestTransactions: Set<PricedResourceTransactions>) =
- RequirementsSatisfaction(false, PlayabilityLevel.MISSING_GOLD_FOR_RES, minPrice, cheapestTransactions)
+ internal fun missingGoldForResources(minPrice: Int, transactionOptions: ResourceTransactionOptions) =
+ RequirementsSatisfaction(false, PlayabilityLevel.MISSING_GOLD_FOR_RES, minPrice, transactionOptions)
internal fun unavailableResources() =
- RequirementsSatisfaction(false, PlayabilityLevel.UNAVAILABLE_RESOURCES, Int.MAX_VALUE, emptySet())
+ RequirementsSatisfaction(false, PlayabilityLevel.UNAVAILABLE_RESOURCES, Int.MAX_VALUE, noTransactionOptions())
}
}
diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/BestPriceCalculator.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/TransactionOptionsCalculator.kt
index 967bee2c..fbbcaa6e 100644
--- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/BestPriceCalculator.kt
+++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/resources/TransactionOptionsCalculator.kt
@@ -1,15 +1,11 @@
package org.luxons.sevenwonders.engine.resources
import org.luxons.sevenwonders.engine.Player
-import org.luxons.sevenwonders.model.resources.PricedResourceTransactions
-import org.luxons.sevenwonders.model.resources.Provider
-import org.luxons.sevenwonders.model.resources.ResourceType
+import org.luxons.sevenwonders.model.resources.*
import java.util.*
-internal fun bestSolution(resources: Resources, player: Player): TransactionPlan =
- BestPriceCalculator(resources, player).computeBestSolution()
-
-data class TransactionPlan(val price: Int, val possibleTransactions: Set<PricedResourceTransactions>)
+internal fun transactionOptions(resources: Resources, player: Player): ResourceTransactionOptions =
+ TransactionOptionsCalculator(resources, player).computeOptions()
private class ResourcePool(
val provider: Provider?,
@@ -21,16 +17,14 @@ private class ResourcePool(
fun getCost(type: ResourceType): Int = if (provider == null) 0 else rules.getCost(type, provider)
}
-private class BestPriceCalculator(resourcesToPay: Resources, player: Player) {
+private class TransactionOptionsCalculator(resourcesToPay: Resources, player: Player) {
private val pools: List<ResourcePool>
private val resourcesLeftToPay: MutableResources
private val boughtResources: MutableMap<Provider, MutableResources> = EnumMap(Provider::class.java)
- private val pricePaid: MutableMap<Provider, Int> = EnumMap(Provider::class.java)
- private var totalPricePaid: Int = 0
+ private val pricePaidPerProvider: MutableMap<Provider, Int> = EnumMap(Provider::class.java)
- private var bestSolutions: MutableSet<PricedResourceTransactions> = mutableSetOf()
- private var bestPrice: Int = Integer.MAX_VALUE
+ private var optionsSoFar: MutableSet<PricedResourceTransactions> = mutableSetOf()
init {
val board = player.board
@@ -53,14 +47,17 @@ private class BestPriceCalculator(resourcesToPay: Resources, player: Player) {
return ResourcePool(this, player.board.tradingRules, choices)
}
- fun computeBestSolution(): TransactionPlan {
+ fun computeOptions(): ResourceTransactionOptions {
computePossibilities()
- return TransactionPlan(bestPrice, bestSolutions)
+ return optionsSoFar.distinctBy { it.costByProvider }.sortedBy { it.totalPrice }
}
+ private val PricedResourceTransactions.costByProvider: Map<Provider, Int>
+ get() = associate { it.provider to it.totalPrice }
+
private fun computePossibilities() {
if (resourcesLeftToPay.isEmpty()) {
- updateBestSolutionIfNeeded()
+ addCurrentOption()
return
}
for (type in ResourceType.values()) {
@@ -93,13 +90,11 @@ private class BestPriceCalculator(resourcesToPay: Resources, player: Player) {
fun buyOne(provider: Provider, type: ResourceType, cost: Int) {
boughtResources.getOrPut(provider) { MutableResources() }.add(type, 1)
- pricePaid.merge(provider, cost) { old, new -> old + new }
- totalPricePaid += cost
+ pricePaidPerProvider.merge(provider, cost) { old, new -> old + new }
}
fun unbuyOne(provider: Provider, type: ResourceType, cost: Int) {
- totalPricePaid -= cost
- pricePaid.merge(provider, -cost) { old, new -> old + new }
+ pricePaidPerProvider.merge(provider, -cost) { old, new -> old + new }
boughtResources[provider]!!.remove(type, 1)
}
@@ -114,15 +109,27 @@ private class BestPriceCalculator(resourcesToPay: Resources, player: Player) {
}
}
- private fun updateBestSolutionIfNeeded() {
- if (totalPricePaid > bestPrice) return
-
- if (totalPricePaid < bestPrice) {
- bestPrice = totalPricePaid
- bestSolutions.clear()
+ private fun addCurrentOption() {
+ if (optionsSoFar.any { it < pricePaidPerProvider }) {
+ return
}
// avoid mutating the resources from the transactions
- val transactionSet = boughtResources.mapValues { (_, res) -> res.copy() }.toTransactions(pricePaid)
- bestSolutions.add(transactionSet)
+ val transactionsOption = boughtResources.mapValues { (_, res) -> res.copy() }.toTransactions(pricePaidPerProvider)
+ optionsSoFar.add(transactionsOption)
+ optionsSoFar.removeIf { it > pricePaidPerProvider }
}
+
+ private operator fun PricedResourceTransactions.compareTo(prices: Map<Provider, Int>): Int = when {
+ left == prices.left -> right.compareTo(prices.right)
+ right == prices.right -> left.compareTo(prices.left)
+ else -> 0
+ }
+
+ private val Map<Provider, Int>.left: Int get() = this[Provider.LEFT_PLAYER] ?: 0
+ private val Map<Provider, Int>.right: Int get() = this[Provider.RIGHT_PLAYER] ?: 0
+
+ private val PricedResourceTransactions.left: Int
+ get() = firstOrNull { it.provider == Provider.LEFT_PLAYER }?.totalPrice ?: 0
+ private val PricedResourceTransactions.right: Int
+ get() = firstOrNull { it.provider == Provider.RIGHT_PLAYER }?.totalPrice ?: 0
}
diff --git a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/wonders/Wonder.kt b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/wonders/Wonder.kt
index 4a99b9e9..03ed2f8f 100644
--- a/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/wonders/Wonder.kt
+++ b/sw-engine/src/main/kotlin/org/luxons/sevenwonders/engine/wonders/Wonder.kt
@@ -7,6 +7,7 @@ import org.luxons.sevenwonders.model.cards.CardBack
import org.luxons.sevenwonders.model.cards.PlayabilityLevel
import org.luxons.sevenwonders.model.resources.ResourceTransactions
import org.luxons.sevenwonders.model.resources.ResourceType
+import org.luxons.sevenwonders.model.resources.noTransactionOptions
import org.luxons.sevenwonders.model.wonders.WonderBuildability
internal class Wonder(
@@ -53,14 +54,14 @@ private object Buildability {
fun wonderFullyBuilt() = WonderBuildability(
isBuildable = false,
minPrice = Int.MAX_VALUE,
- cheapestTransactions = emptySet(),
+ transactionsOptions = noTransactionOptions(),
playabilityLevel = PlayabilityLevel.WONDER_FULLY_BUILT,
)
fun requirementDependent(satisfaction: RequirementsSatisfaction) = WonderBuildability(
isBuildable = satisfaction.satisfied,
minPrice = satisfaction.minPrice,
- cheapestTransactions = satisfaction.cheapestTransactions,
+ transactionsOptions = satisfaction.transactionOptions,
playabilityLevel = satisfaction.level,
)
}
diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt
index 783d3eab..62267c7a 100644
--- a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt
+++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/GameTest.kt
@@ -4,23 +4,14 @@ import org.luxons.sevenwonders.engine.data.GameDefinition
import org.luxons.sevenwonders.engine.data.LAST_AGE
import org.luxons.sevenwonders.engine.test.SEED
import org.luxons.sevenwonders.engine.test.testSettings
-import org.luxons.sevenwonders.model.Action
-import org.luxons.sevenwonders.model.MoveType
-import org.luxons.sevenwonders.model.PlayedMove
-import org.luxons.sevenwonders.model.PlayerMove
-import org.luxons.sevenwonders.model.PlayerTurnInfo
+import org.luxons.sevenwonders.model.*
import org.luxons.sevenwonders.model.cards.HandCard
import org.luxons.sevenwonders.model.cards.TableCard
import org.luxons.sevenwonders.model.resources.ResourceTransactions
import org.luxons.sevenwonders.model.resources.noTransactions
import org.luxons.sevenwonders.model.wonders.deal
import kotlin.random.Random
-import kotlin.test.Test
-import kotlin.test.assertEquals
-import kotlin.test.assertFalse
-import kotlin.test.assertNotNull
-import kotlin.test.assertTrue
-import kotlin.test.fail
+import kotlin.test.*
class GameTest {
@@ -91,12 +82,12 @@ class GameTest {
private fun createPlayCardMove(turnInfo: PlayerTurnInfo): MoveExpectation {
val wonderBuildability = turnInfo.wonderBuildability
if (wonderBuildability.isBuildable) {
- val transactions = wonderBuildability.cheapestTransactions.first()
+ val transactions = wonderBuildability.transactionsOptions.first()
return planMove(turnInfo, MoveType.UPGRADE_WONDER, turnInfo.hand!!.first(), transactions)
}
val playableCard = turnInfo.hand!!.firstOrNull { it.playability.isPlayable }
return if (playableCard != null) {
- planMove(turnInfo, MoveType.PLAY, playableCard, playableCard.playability.cheapestTransactions.first())
+ planMove(turnInfo, MoveType.PLAY, playableCard, playableCard.playability.transactionOptions.first())
} else {
planMove(turnInfo, MoveType.DISCARD, turnInfo.hand!!.first(), noTransactions())
}
diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsTest.kt
index d3fdc94c..36dc8ed1 100644
--- a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsTest.kt
+++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/cards/RequirementsTest.kt
@@ -117,11 +117,11 @@ class RequirementsTest {
val satisfaction = requirements.assess(player)
if (neighbourHasResource) {
- val transactions = setOf(
+ val transactionOptions = listOf(
createPricedTransactions(Provider.LEFT_PLAYER, 2, requiredResource),
createPricedTransactions(Provider.RIGHT_PLAYER, 2, requiredResource),
)
- assertEquals(RequirementsSatisfaction.metWithHelp(2, transactions), satisfaction)
+ assertEquals(RequirementsSatisfaction.metWithHelp(2, transactionOptions), satisfaction)
} else {
assertEquals(RequirementsSatisfaction.unavailableResources(), satisfaction)
}
diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/resources/BestPriceCalculatorTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/resources/BestPriceCalculatorTest.kt
deleted file mode 100644
index 1a56caf4..00000000
--- a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/resources/BestPriceCalculatorTest.kt
+++ /dev/null
@@ -1,175 +0,0 @@
-package org.luxons.sevenwonders.engine.resources
-
-import org.junit.Test
-import org.luxons.sevenwonders.engine.SimplePlayer
-import org.luxons.sevenwonders.engine.boards.Table
-import org.luxons.sevenwonders.engine.test.createPricedTransaction
-import org.luxons.sevenwonders.engine.test.createPricedTransactions
-import org.luxons.sevenwonders.engine.test.testBoard
-import org.luxons.sevenwonders.engine.test.testTable
-import org.luxons.sevenwonders.model.resources.PricedResourceTransactions
-import org.luxons.sevenwonders.model.resources.Provider.LEFT_PLAYER
-import org.luxons.sevenwonders.model.resources.Provider.RIGHT_PLAYER
-import org.luxons.sevenwonders.model.resources.ResourceType.*
-import org.luxons.sevenwonders.model.resources.noTransactions
-import kotlin.test.assertEquals
-
-class BestPriceCalculatorTest {
-
- private fun solutions(price: Int, vararg resourceTransactions: PricedResourceTransactions) =
- TransactionPlan(price, setOf(*resourceTransactions))
-
- @Test
- fun bestPrice_0forEmptyResources() {
- val table = testTable(3)
- val player0 = SimplePlayer(0, table)
- val emptyResources = emptyResources()
- val emptyTransactions = noTransactions()
- assertEquals(solutions(0, emptyTransactions), bestSolution(emptyResources, player0))
- }
-
- @Test
- fun bestPrice_fixedResources_defaultCost() {
- val left = testBoard(STONE)
- val main = testBoard(STONE)
- val right = testBoard(WOOD)
- val table = Table(listOf(main, right, left))
-
- val player0 = SimplePlayer(0, table)
- val player1 = SimplePlayer(1, table)
- val player2 = SimplePlayer(2, table)
-
- val resources = resourcesOf(STONE, STONE)
-
- val stoneLeftSingle = createPricedTransaction(LEFT_PLAYER, 2, STONE)
- val stoneRightSingle = createPricedTransaction(RIGHT_PLAYER, 2, STONE)
-
- val stoneLeft = createPricedTransactions(stoneLeftSingle)
- val stoneRight = createPricedTransactions(stoneRightSingle)
- val stoneLeftAndRight = createPricedTransactions(stoneLeftSingle, stoneRightSingle)
-
- assertEquals(solutions(2, stoneLeft), bestSolution(resources, player0))
- assertEquals(solutions(4, stoneLeftAndRight), bestSolution(resources, player1))
- assertEquals(solutions(2, stoneRight), bestSolution(resources, player2))
- }
-
- @Test
- fun bestPrice_fixedResources_overridenCost() {
- val main = testBoard(STONE)
- main.tradingRules.setCost(WOOD, RIGHT_PLAYER, 1)
-
- val left = testBoard(WOOD)
- val right = testBoard(WOOD)
- val opposite = testBoard(GLASS)
- val table = Table(listOf(main, right, opposite, left))
-
- val player0 = SimplePlayer(0, table)
- val player1 = SimplePlayer(1, table)
- val player2 = SimplePlayer(2, table)
- val player3 = SimplePlayer(3, table)
-
- val resources = resourcesOf(WOOD)
-
- val woodLeft = createPricedTransactions(LEFT_PLAYER, 2, WOOD)
- val woodRightDiscounted = createPricedTransactions(RIGHT_PLAYER, 1, WOOD)
- val woodRight = createPricedTransactions(RIGHT_PLAYER, 2, WOOD)
-
- assertEquals(solutions(1, woodRightDiscounted), bestSolution(resources, player0))
- assertEquals(solutions(0, noTransactions()), bestSolution(resources, player1))
- assertEquals(solutions(2, woodLeft, woodRight), bestSolution(resources, player2))
- assertEquals(solutions(0, noTransactions()), bestSolution(resources, player3))
- }
-
- @Test
- fun bestPrice_mixedResources_overridenCost() {
- val left = testBoard(WOOD)
-
- val main = testBoard(STONE)
- main.tradingRules.setCost(WOOD, RIGHT_PLAYER, 1)
-
- val right = testBoard(ORE)
- right.production.addChoice(WOOD, CLAY)
- right.publicProduction.addChoice(WOOD, CLAY)
-
- val table = Table(listOf(main, right, left))
-
- val player0 = SimplePlayer(0, table)
- val player1 = SimplePlayer(1, table)
- val player2 = SimplePlayer(2, table)
-
- val resources = resourcesOf(WOOD)
- val woodRightDiscounted = createPricedTransactions(RIGHT_PLAYER, 1, WOOD)
-
- assertEquals(solutions(1, woodRightDiscounted), bestSolution(resources, player0))
- assertEquals(solutions(0, noTransactions()), bestSolution(resources, player1))
- assertEquals(solutions(0, noTransactions()), bestSolution(resources, player2))
- }
-
- @Test
- fun bestPrice_mixedResources_overridenCost_sameMultipleTimes() {
- val left = testBoard(WOOD)
-
- val main = testBoard(WOOD)
- main.tradingRules.setCost(CLAY, LEFT_PLAYER, 1)
- main.tradingRules.setCost(CLAY, RIGHT_PLAYER, 1)
-
- main.publicProduction.addFixedResource(STONE, 1)
- main.publicProduction.addChoice(CLAY, ORE)
- main.production.addFixedResource(STONE, 1)
- main.production.addChoice(CLAY, ORE)
-
- val right = testBoard(CLAY)
- right.publicProduction.addFixedResource(ORE, 1)
- right.publicProduction.addFixedResource(CLAY, 1)
- right.publicProduction.addFixedResource(WOOD, 2)
- right.production.addFixedResource(ORE, 1)
- right.production.addFixedResource(CLAY, 1)
- right.production.addFixedResource(WOOD, 2)
-
- val table = Table(listOf(main, right, left))
-
- val player0 = SimplePlayer(0, table)
- val player1 = SimplePlayer(1, table)
- val player2 = SimplePlayer(2, table)
-
- val resources = resourcesOf(WOOD, CLAY, CLAY, CLAY)
-
- val claysRightDiscounted = createPricedTransaction(RIGHT_PLAYER, 2, CLAY, CLAY)
- val claysLeft = createPricedTransaction(LEFT_PLAYER, 4, CLAY, CLAY)
- val clayLeft = createPricedTransaction(LEFT_PLAYER, 2, CLAY)
- val clayRight = createPricedTransaction(RIGHT_PLAYER, 2, CLAY)
-
- assertEquals(solutions(2, createPricedTransactions(claysRightDiscounted)), bestSolution(resources, player0))
- assertEquals(solutions(2, createPricedTransactions(clayLeft)), bestSolution(resources, player1))
- assertEquals(solutions(6, createPricedTransactions(claysLeft, clayRight)), bestSolution(resources, player2))
- }
-
- @Test
- fun bestPrice_chooseCheapest() {
- val left = testBoard(WOOD)
-
- val main = testBoard(WOOD)
- main.production.addChoice(CLAY, ORE)
- main.tradingRules.setCost(CLAY, RIGHT_PLAYER, 1)
-
- val right = testBoard(WOOD)
- right.production.addFixedResource(ORE, 1)
- right.production.addFixedResource(CLAY, 1)
- right.publicProduction.addFixedResource(ORE, 1)
- right.publicProduction.addFixedResource(CLAY, 1)
-
- val table = Table(listOf(main, right, left))
-
- val player0 = SimplePlayer(0, table)
- val player1 = SimplePlayer(1, table)
- val player2 = SimplePlayer(2, table)
-
- val resources = resourcesOf(ORE, CLAY)
- val oreAndClayLeft = createPricedTransactions(LEFT_PLAYER, 4, ORE, CLAY)
- val clayRightDiscounted = createPricedTransactions(RIGHT_PLAYER, 1, CLAY)
-
- assertEquals(solutions(1, clayRightDiscounted), bestSolution(resources, player0))
- assertEquals(solutions(0, noTransactions()), bestSolution(resources, player1))
- assertEquals(solutions(4, oreAndClayLeft), bestSolution(resources, player2))
- }
-}
diff --git a/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/resources/TransactionOptionsCalculatorTest.kt b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/resources/TransactionOptionsCalculatorTest.kt
new file mode 100644
index 00000000..da4b608e
--- /dev/null
+++ b/sw-engine/src/test/kotlin/org/luxons/sevenwonders/engine/resources/TransactionOptionsCalculatorTest.kt
@@ -0,0 +1,201 @@
+package org.luxons.sevenwonders.engine.resources
+
+import org.junit.Test
+import org.luxons.sevenwonders.engine.SimplePlayer
+import org.luxons.sevenwonders.engine.boards.Table
+import org.luxons.sevenwonders.engine.test.createPricedTransaction
+import org.luxons.sevenwonders.engine.test.createPricedTransactions
+import org.luxons.sevenwonders.engine.test.testBoard
+import org.luxons.sevenwonders.engine.test.testTable
+import org.luxons.sevenwonders.model.resources.Provider.LEFT_PLAYER
+import org.luxons.sevenwonders.model.resources.Provider.RIGHT_PLAYER
+import org.luxons.sevenwonders.model.resources.ResourceType.*
+import org.luxons.sevenwonders.model.resources.noTransactions
+import org.luxons.sevenwonders.model.resources.singleOptionNoTransactionNeeded
+import kotlin.test.assertEquals
+
+class TransactionOptionsCalculatorTest {
+
+ @Test
+ fun transactionOptions_noResourcesRequired_nothingToBuy() {
+ val table = testTable(3)
+ val player0 = SimplePlayer(0, table)
+ val emptyResources = emptyResources()
+ assertEquals(singleOptionNoTransactionNeeded(), transactionOptions(emptyResources, player0))
+ }
+
+ @Test
+ fun transactionOptions_initialResources_defaultCost() {
+ val board2 = testBoard(STONE)
+ val board0 = testBoard(STONE)
+ val board1 = testBoard(WOOD)
+ val table = Table(listOf(board0, board1, board2))
+
+ val player0 = SimplePlayer(0, table)
+ val player1 = SimplePlayer(1, table)
+ val player2 = SimplePlayer(2, table)
+
+ val resources = resourcesOf(STONE, STONE)
+
+ val stoneLeftSingle = createPricedTransaction(LEFT_PLAYER, 2, STONE)
+ val stoneRightSingle = createPricedTransaction(RIGHT_PLAYER, 2, STONE)
+
+ val stoneLeft = createPricedTransactions(stoneLeftSingle)
+ val stoneRight = createPricedTransactions(stoneRightSingle)
+ val stoneLeftAndRight = createPricedTransactions(stoneLeftSingle, stoneRightSingle)
+
+ assertEquals(listOf(stoneLeft), transactionOptions(resources, player0))
+ assertEquals(listOf(stoneLeftAndRight), transactionOptions(resources, player1))
+ assertEquals(listOf(stoneRight), transactionOptions(resources, player2))
+ }
+
+ @Test
+ fun transactionOptions_fixedResources_defaultCost() {
+ val board2 = testBoard(WOOD)
+
+ val board0 = testBoard(STONE)
+ board0.publicProduction.addFixedResource(CLAY, 1)
+ board0.production.addFixedResource(CLAY, 1)
+
+ val board1 = testBoard(ORE)
+
+ val table = Table(listOf(board0, board1, board2))
+
+ val player0 = SimplePlayer(0, table)
+ val player1 = SimplePlayer(1, table)
+ val player2 = SimplePlayer(2, table)
+
+ val resources = resourcesOf(STONE, CLAY)
+
+ val stoneAndClayLeft = createPricedTransaction(LEFT_PLAYER, 4, STONE, CLAY)
+ val stoneAndClayRight = createPricedTransaction(RIGHT_PLAYER, 4, STONE, CLAY)
+
+ assertEquals(singleOptionNoTransactionNeeded(), transactionOptions(resources, player0))
+ assertEquals(listOf(createPricedTransactions(stoneAndClayLeft)), transactionOptions(resources, player1))
+ assertEquals(listOf(createPricedTransactions(stoneAndClayRight)), transactionOptions(resources, player2))
+ }
+
+ @Test
+ fun transactionOptions_fixedResources_overridenCost() {
+ val board0 = testBoard(STONE)
+ board0.tradingRules.setCost(WOOD, RIGHT_PLAYER, 1)
+
+ val board1 = testBoard(WOOD)
+ val board2 = testBoard(GLASS)
+ val board3 = testBoard(WOOD)
+
+ val table = Table(listOf(board0, board1, board2, board3))
+
+ val player0 = SimplePlayer(0, table)
+ val player1 = SimplePlayer(1, table)
+ val player2 = SimplePlayer(2, table)
+ val player3 = SimplePlayer(3, table)
+
+ val resources = resourcesOf(WOOD)
+
+ val woodLeft = createPricedTransactions(LEFT_PLAYER, 2, WOOD)
+ val woodRightDiscounted = createPricedTransactions(RIGHT_PLAYER, 1, WOOD)
+ val woodRight = createPricedTransactions(RIGHT_PLAYER, 2, WOOD)
+
+ assertEquals(listOf(woodRightDiscounted, woodLeft), transactionOptions(resources, player0))
+ assertEquals(listOf(noTransactions()), transactionOptions(resources, player1))
+ assertEquals(listOf(woodLeft, woodRight), transactionOptions(resources, player2))
+ assertEquals(listOf(noTransactions()), transactionOptions(resources, player3))
+ }
+
+ @Test
+ fun transactionOptions_mixedResources_overridenCost() {
+ val board0 = testBoard(STONE)
+ board0.tradingRules.setCost(WOOD, RIGHT_PLAYER, 1)
+
+ val board1 = testBoard(ORE)
+ board1.production.addChoice(WOOD, CLAY)
+ board1.publicProduction.addChoice(WOOD, CLAY)
+
+ val board2 = testBoard(WOOD)
+
+ val table = Table(listOf(board0, board1, board2))
+
+ val player0 = SimplePlayer(0, table)
+ val player1 = SimplePlayer(1, table)
+ val player2 = SimplePlayer(2, table)
+
+ val resources = resourcesOf(WOOD)
+
+ val woodRightDiscounted = createPricedTransactions(RIGHT_PLAYER, 1, WOOD)
+ val woodLeft = createPricedTransactions(LEFT_PLAYER, 2, WOOD)
+
+ assertEquals(listOf(woodRightDiscounted, woodLeft), transactionOptions(resources, player0))
+ assertEquals(listOf(noTransactions()), transactionOptions(resources, player1))
+ assertEquals(listOf(noTransactions()), transactionOptions(resources, player2))
+ }
+
+ @Test
+ fun transactionOptions_mixedResources_overridenCost_sameMultipleTimes() {
+ val board0 = testBoard(WOOD)
+ board0.tradingRules.setCost(CLAY, LEFT_PLAYER, 1)
+ board0.tradingRules.setCost(CLAY, RIGHT_PLAYER, 1)
+
+ board0.publicProduction.addFixedResource(STONE, 1)
+ board0.publicProduction.addChoice(CLAY, ORE)
+ board0.production.addFixedResource(STONE, 1)
+ board0.production.addChoice(CLAY, ORE)
+
+ val board1 = testBoard(CLAY)
+ board1.publicProduction.addFixedResource(ORE, 1)
+ board1.publicProduction.addFixedResource(CLAY, 1)
+ board1.publicProduction.addFixedResource(WOOD, 2)
+ board1.production.addFixedResource(ORE, 1)
+ board1.production.addFixedResource(CLAY, 1)
+ board1.production.addFixedResource(WOOD, 2)
+
+ val board2 = testBoard(WOOD)
+
+ val table = Table(listOf(board0, board1, board2))
+
+ val player0 = SimplePlayer(0, table)
+ val player1 = SimplePlayer(1, table)
+ val player2 = SimplePlayer(2, table)
+
+ val resources = resourcesOf(WOOD, CLAY, CLAY, CLAY)
+
+ val claysRightDiscounted = createPricedTransaction(RIGHT_PLAYER, 2, CLAY, CLAY)
+ val claysLeft = createPricedTransaction(LEFT_PLAYER, 4, CLAY, CLAY)
+ val clayLeft = createPricedTransaction(LEFT_PLAYER, 2, CLAY)
+ val clayRight = createPricedTransaction(RIGHT_PLAYER, 2, CLAY)
+
+ assertEquals(listOf(createPricedTransactions(claysRightDiscounted)), transactionOptions(resources, player0))
+ assertEquals(listOf(createPricedTransactions(clayLeft)), transactionOptions(resources, player1))
+ assertEquals(listOf(createPricedTransactions(claysLeft, clayRight)), transactionOptions(resources, player2))
+ }
+
+ @Test
+ fun transactionOptions_cheapestFirst() {
+ val board0 = testBoard(WOOD)
+ board0.production.addChoice(CLAY, ORE)
+ board0.tradingRules.setCost(CLAY, RIGHT_PLAYER, 1)
+
+ val board1 = testBoard(WOOD)
+ board1.production.addFixedResource(ORE, 1)
+ board1.production.addFixedResource(CLAY, 1)
+ board1.publicProduction.addFixedResource(ORE, 1)
+ board1.publicProduction.addFixedResource(CLAY, 1)
+
+ val board2 = testBoard(WOOD)
+
+ val table = Table(listOf(board0, board1, board2))
+
+ val player0 = SimplePlayer(0, table)
+ val player1 = SimplePlayer(1, table)
+ val player2 = SimplePlayer(2, table)
+
+ val resources = resourcesOf(ORE, CLAY)
+
+ val clayRightDiscounted = createPricedTransactions(RIGHT_PLAYER, 1, CLAY)
+ val oreAndClayLeft = createPricedTransactions(LEFT_PLAYER, 4, ORE, CLAY)
+
+ assertEquals(listOf(clayRightDiscounted), transactionOptions(resources, player0))
+ assertEquals(listOf(noTransactions()), transactionOptions(resources, player1))
+ assertEquals(listOf(oreAndClayLeft), transactionOptions(resources, player2))
+ }
+}
bgstack15