diff options
Diffstat (limited to 'game-engine/src/main/kotlin')
7 files changed, 235 insertions, 2 deletions
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 60287c84..2c2e5066 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 @@ -5,6 +5,7 @@ import org.luxons.sevenwonders.game.boards.RelativeBoardPosition import org.luxons.sevenwonders.game.cards.Card import org.luxons.sevenwonders.game.cards.Color import org.luxons.sevenwonders.game.cards.HandRotationDirection +import org.luxons.sevenwonders.game.data.Age import org.luxons.sevenwonders.game.moves.Move import org.luxons.sevenwonders.game.resources.Provider @@ -16,7 +17,7 @@ class Table(val boards: List<Board>) { val nbPlayers: Int = boards.size - var currentAge = 0 + var currentAge: Age = 0 private set val handRotationDirection: HandRotationDirection diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Board.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Board.kt new file mode 100644 index 00000000..b02777e5 --- /dev/null +++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Board.kt @@ -0,0 +1,99 @@ +package org.luxons.sevenwonders.game.boards + +import org.luxons.sevenwonders.game.Settings +import org.luxons.sevenwonders.game.api.Table +import org.luxons.sevenwonders.game.cards.Card +import org.luxons.sevenwonders.game.cards.Color +import org.luxons.sevenwonders.game.data.Age +import org.luxons.sevenwonders.game.effects.SpecialAbility +import org.luxons.sevenwonders.game.resources.Production +import org.luxons.sevenwonders.game.resources.TradingRules +import org.luxons.sevenwonders.game.scoring.PlayerScore +import org.luxons.sevenwonders.game.scoring.ScoreCategory +import org.luxons.sevenwonders.game.wonders.Wonder + +class Board(val wonder: Wonder, val playerIndex: Int, settings: Settings) { + + val production = Production() + val publicProduction = Production() + val science = Science() + val tradingRules: TradingRules = TradingRules(settings.defaultTradingCost) + val military: Military = Military(settings.lostPointsPerDefeat, settings.wonPointsPerVictoryPerAge) + private val pointsPer3Gold: Int = settings.pointsPer3Gold + + private val playedCards: MutableList<Card> = arrayListOf() + private val specialAbilities: MutableSet<SpecialAbility> = hashSetOf() + private val consumedFreeCards: MutableMap<Age, Boolean> = mutableMapOf() + + var gold: Int = settings.initialGold + + var copiedGuild: Card? = null + set(copiedGuild) { + if (copiedGuild!!.color !== Color.PURPLE) { + throw IllegalArgumentException("The given card '$copiedGuild' is not a Guild card") + } + field = copiedGuild + } + + init { + this.production.addFixedResource(wonder.initialResource, 1) + this.publicProduction.addFixedResource(wonder.initialResource, 1) + } + + fun getPlayedCards(): List<Card> = playedCards + + fun addCard(card: Card) { + playedCards.add(card) + } + + internal fun getNbCardsOfColor(colorFilter: List<Color>): Int = playedCards.count { colorFilter.contains(it.color) } + + fun isPlayed(cardName: String): Boolean = playedCards.count { it.name == cardName } > 0 + + fun addGold(amount: Int) { + this.gold += amount + } + + fun removeGold(amount: Int) { + if (gold < amount) { + throw InsufficientFundsException(gold, amount) + } + this.gold -= amount + } + + fun addSpecial(specialAbility: SpecialAbility) { + specialAbilities.add(specialAbility) + } + + fun hasSpecial(specialAbility: SpecialAbility): Boolean = specialAbilities.contains(specialAbility) + + fun canPlayFreeCard(age: Age): Boolean = + hasSpecial(SpecialAbility.ONE_FREE_PER_AGE) && !consumedFreeCards.getOrDefault(age, false) + + fun consumeFreeCard(age: Age) { + consumedFreeCards[age] = true + } + + fun computePoints(table: Table): PlayerScore { + val score = PlayerScore(gold) + score.put(ScoreCategory.CIVIL, computePointsForCards(table, Color.BLUE)) + score.put(ScoreCategory.MILITARY, military.totalPoints) + score.put(ScoreCategory.SCIENCE, science.computePoints()) + score.put(ScoreCategory.TRADE, computePointsForCards(table, Color.YELLOW)) + score.put(ScoreCategory.GUILD, computePointsForCards(table, Color.PURPLE)) + score.put(ScoreCategory.WONDER, wonder.computePoints(table, playerIndex)) + score.put(ScoreCategory.GOLD, computeGoldPoints()) + return score + } + + private fun computePointsForCards(table: Table, color: Color): Int = playedCards + .filter { (_, color1) -> color1 === color } + .flatMap { (_, _, _, effects) -> effects } + .map { e -> e.computePoints(table, playerIndex) } + .sum() + + private fun computeGoldPoints(): Int = gold / 3 * pointsPer3Gold + + internal class InsufficientFundsException(current: Int, required: Int) : + RuntimeException(String.format("Current balance is %d gold, but %d are required", current, required)) +} diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/BoardElementType.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/BoardElementType.kt new file mode 100644 index 00000000..d35e9777 --- /dev/null +++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/BoardElementType.kt @@ -0,0 +1,17 @@ +package org.luxons.sevenwonders.game.boards + +import org.luxons.sevenwonders.game.cards.Color + +enum class BoardElementType { + CARD { + override fun getElementCount(board: Board, colors: List<Color>?): Int = board.getNbCardsOfColor(colors!!) + }, + BUILT_WONDER_STAGES { + override fun getElementCount(board: Board, colors: List<Color>?): Int = board.wonder.nbBuiltStages + }, + DEFEAT_TOKEN { + override fun getElementCount(board: Board, colors: List<Color>?): Int = board.military.nbDefeatTokens + }; + + abstract fun getElementCount(board: Board, colors: List<Color>?): Int +} diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Military.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Military.kt new file mode 100644 index 00000000..c00d1ace --- /dev/null +++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Military.kt @@ -0,0 +1,33 @@ +package org.luxons.sevenwonders.game.boards + +import org.luxons.sevenwonders.game.data.Age + +class Military internal constructor( + private val lostPointsPerDefeat: Int, + private val wonPointsPerVictoryPerAge: Map<Age, Int> +) { + var nbShields = 0 + private set + + var totalPoints = 0 + private set + + var nbDefeatTokens = 0 + private set + + fun addShields(nbShields: Int) { + this.nbShields += nbShields + } + + fun victory(age: Age) { + val wonPoints = wonPointsPerVictoryPerAge[age] ?: throw UnknownAgeException(age) + totalPoints += wonPoints + } + + fun defeat() { + totalPoints -= lostPointsPerDefeat + nbDefeatTokens++ + } + + internal class UnknownAgeException(unknownAge: Age) : IllegalArgumentException(unknownAge.toString()) +} diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/RelativeBoardPosition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/RelativeBoardPosition.kt new file mode 100644 index 00000000..fcd629ec --- /dev/null +++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/RelativeBoardPosition.kt @@ -0,0 +1,25 @@ +package org.luxons.sevenwonders.game.boards + +enum class RelativeBoardPosition { + LEFT { + override fun getIndexFrom(playerIndex: Int, nbPlayers: Int): Int { + return wrapIndex(playerIndex - 1, nbPlayers) + } + }, + SELF { + override fun getIndexFrom(playerIndex: Int, nbPlayers: Int): Int { + return playerIndex + } + }, + RIGHT { + override fun getIndexFrom(playerIndex: Int, nbPlayers: Int): Int { + return wrapIndex(playerIndex + 1, nbPlayers) + } + }; + + abstract fun getIndexFrom(playerIndex: Int, nbPlayers: Int): Int + + internal fun wrapIndex(index: Int, nbPlayers: Int): Int { + return Math.floorMod(index, nbPlayers) + } +} diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Science.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Science.kt new file mode 100644 index 00000000..839986e0 --- /dev/null +++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Science.kt @@ -0,0 +1,56 @@ +package org.luxons.sevenwonders.game.boards + +enum class ScienceType { + COMPASS, + WHEEL, + TABLET +} + +class Science { + + private val quantities : MutableMap<ScienceType, Int> = mutableMapOf() + + var jokers: Int = 0 + private set + + fun size(): Int = quantities.values.sum() + jokers + + fun add(type: ScienceType, quantity: Int) { + quantities.merge(type, quantity) { x, y -> x + y } + } + + fun addJoker(quantity: Int) { + jokers += quantity + } + + fun addAll(science: Science) { + science.quantities.forEach { type, quantity -> this.add(type, quantity) } + jokers += science.jokers + } + + fun getQuantity(type: ScienceType): Int = quantities.getOrDefault(type, 0) + + fun computePoints(): Int { + val values = ScienceType.values().map(::getQuantity).toMutableList() + return computePoints(values, jokers) + } + + private fun computePoints(values: MutableList<Int>, jokers: Int): Int { + if (jokers == 0) { + return computePointsNoJoker(values) + } + var maxPoints = 0 + for (i in values.indices) { + values[i]++ + maxPoints = Math.max(maxPoints, computePoints(values, jokers - 1)) + values[i]-- + } + return maxPoints + } + + private fun computePointsNoJoker(values: List<Int>): Int { + val independentSquaresSum = values.map { i -> i * i }.sum() + val nbGroupsOfAll = values.min() ?: 0 + return independentSquaresSum + nbGroupsOfAll * 7 + } +} diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt index 49212ab2..a590efc9 100644 --- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt +++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt @@ -1,6 +1,8 @@ package org.luxons.sevenwonders.game.data -const val LAST_AGE: Int = 3 +typealias Age = Int + +const val LAST_AGE: Age = 3 internal data class GlobalRules( val minPlayers: Int, |