summaryrefslogtreecommitdiff
path: root/game-engine/src/main
diff options
context:
space:
mode:
Diffstat (limited to 'game-engine/src/main')
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Board.java179
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/boards/BoardElementType.java28
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Military.java56
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/boards/RelativeBoardPosition.java28
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Science.java65
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/boards/ScienceType.java7
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/Table.kt3
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Board.kt99
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/BoardElementType.kt17
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Military.kt33
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/RelativeBoardPosition.kt25
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/boards/Science.kt56
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt4
13 files changed, 235 insertions, 365 deletions
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Board.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Board.java
deleted file mode 100644
index 23ce5da5..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Board.java
+++ /dev/null
@@ -1,179 +0,0 @@
-package org.luxons.sevenwonders.game.boards;
-
-import java.util.ArrayList;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-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.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;
-
-public class Board {
-
- private final Wonder wonder;
-
- private final int playerIndex;
-
- private final List<Card> playedCards = new ArrayList<>();
-
- private final Production production = new Production();
-
- private final Production publicProduction = new Production();
-
- private final Science science = new Science();
-
- private final TradingRules tradingRules;
-
- private final Military military;
-
- private final Set<SpecialAbility> specialAbilities = EnumSet.noneOf(SpecialAbility.class);
-
- private Map<Integer, Boolean> consumedFreeCards = new HashMap<>();
-
- private Card copiedGuild;
-
- private int gold;
-
- private int pointsPer3Gold;
-
- public Board(Wonder wonder, int playerIndex, Settings settings) {
- this.wonder = wonder;
- this.playerIndex = playerIndex;
- this.gold = settings.getInitialGold();
- this.tradingRules = new TradingRules(settings.getDefaultTradingCost());
- this.military = new Military(settings.getLostPointsPerDefeat(), settings.getWonPointsPerVictoryPerAge());
- this.pointsPer3Gold = settings.getPointsPer3Gold();
- this.production.addFixedResource(wonder.getInitialResource(), 1);
- this.publicProduction.addFixedResource(wonder.getInitialResource(), 1);
- }
-
- public int getPlayerIndex() {
- return playerIndex;
- }
-
- public Wonder getWonder() {
- return wonder;
- }
-
- public List<Card> getPlayedCards() {
- return playedCards;
- }
-
- public void addCard(Card card) {
- playedCards.add(card);
- }
-
- int getNbCardsOfColor(List<Color> colorFilter) {
- return (int) playedCards.stream().filter(c -> colorFilter.contains(c.getColor())).count();
- }
-
- public boolean isPlayed(String cardName) {
- return getPlayedCards().stream().map(Card::getName).filter(name -> name.equals(cardName)).count() > 0;
- }
-
- public Production getProduction() {
- return production;
- }
-
- public Production getPublicProduction() {
- return publicProduction;
- }
-
- public TradingRules getTradingRules() {
- return tradingRules;
- }
-
- public Science getScience() {
- return science;
- }
-
- public int getGold() {
- return gold;
- }
-
- public void setGold(int amount) {
- this.gold = amount;
- }
-
- public void addGold(int amount) {
- this.gold += amount;
- }
-
- public void removeGold(int amount) {
- if (gold < amount) {
- throw new InsufficientFundsException(gold, amount);
- }
- this.gold -= amount;
- }
-
- public Military getMilitary() {
- return military;
- }
-
- public void addSpecial(SpecialAbility specialAbility) {
- specialAbilities.add(specialAbility);
- }
-
- public boolean hasSpecial(SpecialAbility specialAbility) {
- return specialAbilities.contains(specialAbility);
- }
-
- public boolean canPlayFreeCard(int age) {
- return hasSpecial(SpecialAbility.ONE_FREE_PER_AGE) && !consumedFreeCards.getOrDefault(age, false);
- }
-
- public void consumeFreeCard(int age) {
- consumedFreeCards.put(age, true);
- }
-
- public void setCopiedGuild(Card copiedGuild) {
- if (copiedGuild.getColor() != Color.PURPLE) {
- throw new IllegalArgumentException("The given card '" + copiedGuild + "' is not a Guild card");
- }
- this.copiedGuild = copiedGuild;
- }
-
- public Card getCopiedGuild() {
- return copiedGuild;
- }
-
- public PlayerScore computePoints(Table table) {
- PlayerScore score = new PlayerScore(gold);
- score.put(ScoreCategory.CIVIL, computePointsForCards(table, Color.BLUE));
- score.put(ScoreCategory.MILITARY, military.getTotalPoints());
- 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 int computePointsForCards(Table table, Color color) {
- return playedCards.stream()
- .filter(c -> c.getColor() == color)
- .flatMap(c -> c.getEffects().stream())
- .mapToInt(e -> e.computePoints(table, playerIndex))
- .sum();
- }
-
- private int computeGoldPoints() {
- return gold / 3 * pointsPer3Gold;
- }
-
- static class InsufficientFundsException extends RuntimeException {
- InsufficientFundsException(int current, int required) {
- super(String.format("Current balance is %d gold, but %d are required", current, required));
- }
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/BoardElementType.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/BoardElementType.java
deleted file mode 100644
index e50f4ea0..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/BoardElementType.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.luxons.sevenwonders.game.boards;
-
-import java.util.List;
-
-import org.luxons.sevenwonders.game.cards.Color;
-
-public enum BoardElementType {
- CARD {
- @Override
- public int getElementCount(Board board, List<Color> colors) {
- return board.getNbCardsOfColor(colors);
- }
- },
- BUILT_WONDER_STAGES {
- @Override
- public int getElementCount(Board board, List<Color> colors) {
- return board.getWonder().getNbBuiltStages();
- }
- },
- DEFEAT_TOKEN {
- @Override
- public int getElementCount(Board board, List<Color> colors) {
- return board.getMilitary().getNbDefeatTokens();
- }
- };
-
- public abstract int getElementCount(Board board, List<Color> colors);
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Military.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Military.java
deleted file mode 100644
index e5cc7033..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Military.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package org.luxons.sevenwonders.game.boards;
-
-import java.util.Map;
-
-public class Military {
-
- private final int lostPointsPerDefeat;
-
- private final Map<Integer, Integer> wonPointsPerVictoryPerAge;
-
- private int nbShields = 0;
-
- private int totalPoints = 0;
-
- private int nbDefeatTokens = 0;
-
- Military(int lostPointsPerDefeat, Map<Integer, Integer> wonPointsPerVictoryPerAge) {
- this.lostPointsPerDefeat = lostPointsPerDefeat;
- this.wonPointsPerVictoryPerAge = wonPointsPerVictoryPerAge;
- }
-
- public int getNbShields() {
- return nbShields;
- }
-
- public void addShields(int nbShields) {
- this.nbShields += nbShields;
- }
-
- public int getTotalPoints() {
- return totalPoints;
- }
-
- public int getNbDefeatTokens() {
- return nbDefeatTokens;
- }
-
- public void victory(int age) {
- Integer wonPoints = wonPointsPerVictoryPerAge.get(age);
- if (wonPoints == null) {
- throw new UnknownAgeException(age);
- }
- totalPoints += wonPoints;
- }
-
- public void defeat() {
- totalPoints -= lostPointsPerDefeat;
- nbDefeatTokens++;
- }
-
- static final class UnknownAgeException extends IllegalArgumentException {
- UnknownAgeException(int unknownAge) {
- super(String.valueOf(unknownAge));
- }
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/RelativeBoardPosition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/RelativeBoardPosition.java
deleted file mode 100644
index 16b2f3a9..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/RelativeBoardPosition.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.luxons.sevenwonders.game.boards;
-
-public enum RelativeBoardPosition {
- LEFT {
- @Override
- public int getIndexFrom(int playerIndex, int nbPlayers) {
- return wrapIndex(playerIndex - 1, nbPlayers);
- }
- },
- SELF {
- @Override
- public int getIndexFrom(int playerIndex, int nbPlayers) {
- return playerIndex;
- }
- },
- RIGHT {
- @Override
- public int getIndexFrom(int playerIndex, int nbPlayers) {
- return wrapIndex(playerIndex + 1, nbPlayers);
- }
- };
-
- public abstract int getIndexFrom(int playerIndex, int nbPlayers);
-
- int wrapIndex(int index, int nbPlayers) {
- return Math.floorMod(index, nbPlayers);
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Science.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Science.java
deleted file mode 100644
index 34928bcc..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/Science.java
+++ /dev/null
@@ -1,65 +0,0 @@
-package org.luxons.sevenwonders.game.boards;
-
-import java.util.Arrays;
-import java.util.EnumMap;
-import java.util.Map;
-
-public class Science {
-
- private Map<ScienceType, Integer> quantities = new EnumMap<>(ScienceType.class);
-
- private int jokers;
-
- public void add(ScienceType type, int quantity) {
- quantities.merge(type, quantity, (x, y) -> x + y);
- }
-
- public void addJoker(int quantity) {
- jokers += quantity;
- }
-
- public int getJokers() {
- return jokers;
- }
-
- public void addAll(Science science) {
- science.quantities.forEach(this::add);
- jokers += science.jokers;
- }
-
- public int getQuantity(ScienceType type) {
- return quantities.getOrDefault(type, 0);
- }
-
- public int size() {
- return quantities.values().stream().mapToInt(q -> q).sum() + jokers;
- }
-
- public int computePoints() {
- ScienceType[] types = ScienceType.values();
- Integer[] values = new Integer[types.length];
- for (int i = 0; i < types.length; i++) {
- values[i] = quantities.getOrDefault(types[i], 0);
- }
- return computePoints(values, jokers);
- }
-
- private static int computePoints(Integer[] values, int jokers) {
- if (jokers == 0) {
- return computePointsNoJoker(values);
- }
- int maxPoints = 0;
- for (int i = 0; i < values.length; i++) {
- values[i]++;
- maxPoints = Math.max(maxPoints, computePoints(values, jokers - 1));
- values[i]--;
- }
- return maxPoints;
- }
-
- private static int computePointsNoJoker(Integer[] values) {
- int independentSquaresSum = Arrays.stream(values).mapToInt(i -> i * i).sum();
- int nbGroupsOfAll = Arrays.stream(values).mapToInt(i -> i).min().orElse(0);
- return independentSquaresSum + nbGroupsOfAll * 7;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/ScienceType.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/ScienceType.java
deleted file mode 100644
index f1b14c6d..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/boards/ScienceType.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package org.luxons.sevenwonders.game.boards;
-
-public enum ScienceType {
- COMPASS,
- WHEEL,
- TABLET
-}
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,
bgstack15