summaryrefslogtreecommitdiff
path: root/game-engine
diff options
context:
space:
mode:
Diffstat (limited to 'game-engine')
-rw-r--r--game-engine/build.gradle8
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/GameDefinition.java59
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/GameDefinitionLoader.java91
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/GlobalRules.java17
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/CardDefinition.java38
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.java76
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/Definition.java24
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/EffectsDefinition.java66
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderDefinition.java27
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSide.java6
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSideDefinition.java31
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethod.java36
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderStageDefinition.java18
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializer.java48
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializer.java55
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.java78
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.java31
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializer.java37
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.java40
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.java64
-rw-r--r--game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourceType.java8
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/Settings.kt (renamed from game-engine/src/main/java/org/luxons/sevenwonders/game/Settings.kt)4
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/CustomizableSettings.kt2
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt30
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinitionLoader.kt63
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt6
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/WonderSide.kt6
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/WonderSidePickMethod.kt28
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt20
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt53
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/EffectsDefinition.kt53
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderDefinition.kt21
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderSideDefinition.kt14
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderStageDefinition.kt13
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializer.kt40
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializer.kt51
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.kt58
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.kt29
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializer.kt39
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.kt34
-rw-r--r--game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.kt57
-rw-r--r--game-engine/src/test/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethodTest.java2
42 files changed, 632 insertions, 849 deletions
diff --git a/game-engine/build.gradle b/game-engine/build.gradle
index 5a3cc4ea..97067176 100644
--- a/game-engine/build.gradle
+++ b/game-engine/build.gradle
@@ -16,11 +16,11 @@ configurations {
}
dependencies {
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
- implementation 'com.google.code.gson:gson:2.8.0'
+ implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
+ implementation 'com.github.salomonbrys.kotson:kotson:2.5.0'
testImplementation 'junit:junit:4.12'
- checkstyleConfig "org.hildan.checkstyle:checkstyle-config:2.1.0"
- ktlint "com.github.shyiko:ktlint:0.24.0"
+ checkstyleConfig 'org.hildan.checkstyle:checkstyle-config:2.1.0'
+ ktlint 'com.github.shyiko:ktlint:0.24.0'
}
checkstyle {
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GameDefinition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GameDefinition.java
deleted file mode 100644
index 0402a26d..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GameDefinition.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package org.luxons.sevenwonders.game.data;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import org.luxons.sevenwonders.game.Game;
-import org.luxons.sevenwonders.game.Settings;
-import org.luxons.sevenwonders.game.api.CustomizableSettings;
-import org.luxons.sevenwonders.game.boards.Board;
-import org.luxons.sevenwonders.game.cards.Decks;
-import org.luxons.sevenwonders.game.data.definitions.DecksDefinition;
-import org.luxons.sevenwonders.game.data.definitions.WonderDefinition;
-import org.luxons.sevenwonders.game.wonders.Wonder;
-
-public class GameDefinition {
-
- private final GlobalRules rules;
-
- private final WonderDefinition[] wonders;
-
- private final DecksDefinition decksDefinition;
-
- GameDefinition(GlobalRules rules, WonderDefinition[] wonders, DecksDefinition decksDefinition) {
- this.rules = rules;
- this.wonders = wonders;
- this.decksDefinition = decksDefinition;
- }
-
- public int getMinPlayers() {
- return rules.getMinPlayers();
- }
-
- public int getMaxPlayers() {
- return rules.getMaxPlayers();
- }
-
- public Game initGame(long id, CustomizableSettings customSettings, int nbPlayers) {
- Settings settings = new Settings(nbPlayers, customSettings);
- List<Board> boards = assignBoards(settings, nbPlayers);
- Decks decks = decksDefinition.create(settings);
- return new Game(id, settings, nbPlayers, boards, decks);
- }
-
- private List<Board> assignBoards(Settings settings, int nbPlayers) {
- List<WonderDefinition> randomizedWonders = Arrays.asList(wonders);
- Collections.shuffle(randomizedWonders, settings.getRandom());
-
- List<Board> boards = new ArrayList<>(nbPlayers);
- for (int i = 0; i < nbPlayers; i++) {
- WonderDefinition def = randomizedWonders.get(i);
- Wonder w = def.create(settings);
- Board b = new Board(w, i, settings);
- boards.add(b);
- }
- return boards;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GameDefinitionLoader.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GameDefinitionLoader.java
deleted file mode 100644
index a1bd7b8d..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GameDefinitionLoader.java
+++ /dev/null
@@ -1,91 +0,0 @@
-package org.luxons.sevenwonders.game.data;
-
-import java.io.BufferedReader;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.Reader;
-import java.lang.reflect.Type;
-import java.util.List;
-
-import org.luxons.sevenwonders.game.data.definitions.DecksDefinition;
-import org.luxons.sevenwonders.game.data.definitions.WonderDefinition;
-import org.luxons.sevenwonders.game.data.serializers.NumericEffectSerializer;
-import org.luxons.sevenwonders.game.data.serializers.ProductionIncreaseSerializer;
-import org.luxons.sevenwonders.game.data.serializers.ProductionSerializer;
-import org.luxons.sevenwonders.game.data.serializers.ResourceTypeSerializer;
-import org.luxons.sevenwonders.game.data.serializers.ResourceTypesSerializer;
-import org.luxons.sevenwonders.game.data.serializers.ResourcesSerializer;
-import org.luxons.sevenwonders.game.data.serializers.ScienceProgressSerializer;
-import org.luxons.sevenwonders.game.effects.GoldIncrease;
-import org.luxons.sevenwonders.game.effects.MilitaryReinforcements;
-import org.luxons.sevenwonders.game.effects.ProductionIncrease;
-import org.luxons.sevenwonders.game.effects.RawPointsIncrease;
-import org.luxons.sevenwonders.game.effects.ScienceProgress;
-import org.luxons.sevenwonders.game.resources.Production;
-import org.luxons.sevenwonders.game.resources.ResourceType;
-import org.luxons.sevenwonders.game.resources.Resources;
-
-import com.google.gson.Gson;
-import com.google.gson.GsonBuilder;
-import com.google.gson.reflect.TypeToken;
-
-public class GameDefinitionLoader {
-
- private static final String BASE_PACKAGE = GameDefinitionLoader.class.getPackage().getName();
-
- private static final String BASE_PACKAGE_PATH = '/' + BASE_PACKAGE.replace('.', '/');
-
- private static final String GLOBAL_RULES_FILE = "global_rules.json";
-
- private static final String CARDS_FILE = "cards.json";
-
- private static final String WONDERS_FILE = "wonders.json";
-
- private final GameDefinition gameDefinition;
-
- public GameDefinitionLoader() {
- gameDefinition = load();
- }
-
- public GameDefinition getGameDefinition() {
- return gameDefinition;
- }
-
- private static GameDefinition load() {
- return new GameDefinition(loadGlobalRules(), loadWonders(), loadDecks());
- }
-
- private static GlobalRules loadGlobalRules() {
- return readJsonFile(GLOBAL_RULES_FILE, GlobalRules.class);
- }
-
- private static WonderDefinition[] loadWonders() {
- return readJsonFile(WONDERS_FILE, WonderDefinition[].class);
- }
-
- private static DecksDefinition loadDecks() {
- return readJsonFile(CARDS_FILE, DecksDefinition.class);
- }
-
- private static <T> T readJsonFile(String filename, Class<T> clazz) {
- InputStream in = GameDefinitionLoader.class.getResourceAsStream(BASE_PACKAGE_PATH + '/' + filename);
- Reader reader = new BufferedReader(new InputStreamReader(in));
- Gson gson = createGson();
- return gson.fromJson(reader, clazz);
- }
-
- private static Gson createGson() {
- Type resourceTypeList = new TypeToken<List<ResourceType>>() {}.getType();
- return new GsonBuilder().disableHtmlEscaping()
- .registerTypeAdapter(Resources.class, new ResourcesSerializer())
- .registerTypeAdapter(ResourceType.class, new ResourceTypeSerializer())
- .registerTypeAdapter(resourceTypeList, new ResourceTypesSerializer())
- .registerTypeAdapter(Production.class, new ProductionSerializer())
- .registerTypeAdapter(ProductionIncrease.class, new ProductionIncreaseSerializer())
- .registerTypeAdapter(MilitaryReinforcements.class, new NumericEffectSerializer())
- .registerTypeAdapter(RawPointsIncrease.class, new NumericEffectSerializer())
- .registerTypeAdapter(GoldIncrease.class, new NumericEffectSerializer())
- .registerTypeAdapter(ScienceProgress.class, new ScienceProgressSerializer())
- .create();
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GlobalRules.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GlobalRules.java
deleted file mode 100644
index 526bebad..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/GlobalRules.java
+++ /dev/null
@@ -1,17 +0,0 @@
-package org.luxons.sevenwonders.game.data;
-
-@SuppressWarnings("unused") // fields are set by Gson
-class GlobalRules {
-
- private int minPlayers;
-
- private int maxPlayers;
-
- int getMinPlayers() {
- return minPlayers;
- }
-
- int getMaxPlayers() {
- return maxPlayers;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/CardDefinition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/CardDefinition.java
deleted file mode 100644
index 621bed2c..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/CardDefinition.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-import java.util.List;
-import java.util.Map;
-
-import org.luxons.sevenwonders.game.Settings;
-import org.luxons.sevenwonders.game.cards.Card;
-import org.luxons.sevenwonders.game.cards.Color;
-import org.luxons.sevenwonders.game.cards.Requirements;
-
-@SuppressWarnings("unused") // the fields are injected by Gson
-public class CardDefinition implements Definition<Card> {
-
- private String name;
-
- private Color color;
-
- private Requirements requirements;
-
- private EffectsDefinition effect;
-
- private String chainParent;
-
- private List<String> chainChildren;
-
- private Map<Integer, Integer> countPerNbPlayer;
-
- private String image;
-
- @Override
- public Card create(Settings settings) {
- return new Card(name, color, requirements, effect.create(settings), chainParent, chainChildren, image);
- }
-
- Map<Integer, Integer> getCountPerNbPlayer() {
- return countPerNbPlayer;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.java
deleted file mode 100644
index 6f97e55f..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.stream.Collectors;
-
-import org.luxons.sevenwonders.game.Settings;
-import org.luxons.sevenwonders.game.cards.Card;
-import org.luxons.sevenwonders.game.cards.CardBack;
-import org.luxons.sevenwonders.game.cards.Decks;
-
-@SuppressWarnings("unused,MismatchedQueryAndUpdateOfCollection") // the fields are injected by Gson
-public class DecksDefinition implements Definition<Decks> {
-
- private List<CardDefinition> age1;
-
- private List<CardDefinition> age2;
-
- private List<CardDefinition> age3;
-
- private String age1Back;
-
- private String age2Back;
-
- private String age3Back;
-
- private List<CardDefinition> guildCards;
-
- @Override
- public Decks create(Settings settings) {
- Map<Integer, List<Card>> cardsPerAge = new HashMap<>();
- cardsPerAge.put(1, prepareStandardDeck(age1, settings, age1Back));
- cardsPerAge.put(2, prepareStandardDeck(age2, settings, age2Back));
- cardsPerAge.put(3, prepareAge3Deck(settings));
- return new Decks(cardsPerAge);
- }
-
- private static List<Card> prepareStandardDeck(List<CardDefinition> defs, Settings settings, String backImage) {
- CardBack back = new CardBack(backImage);
- List<Card> cards = createDeck(defs, settings, back);
- Collections.shuffle(cards, settings.getRandom());
- return cards;
- }
-
- private List<Card> prepareAge3Deck(Settings settings) {
- CardBack back = new CardBack(age3Back);
- List<Card> age3deck = createDeck(age3, settings, back);
- age3deck.addAll(createGuildCards(settings, back));
- Collections.shuffle(age3deck, settings.getRandom());
- return age3deck;
- }
-
- private static List<Card> createDeck(List<CardDefinition> defs, Settings settings, CardBack back) {
- List<Card> cards = new ArrayList<>();
- for (CardDefinition def : defs) {
- for (int i = 0; i < def.getCountPerNbPlayer().get(settings.getNbPlayers()); i++) {
- Card card = def.create(settings);
- card.setBack(back);
- cards.add(card);
- }
- }
- return cards;
- }
-
- private List<Card> createGuildCards(Settings settings, CardBack back) {
- List<Card> guild = guildCards.stream()
- .map((def) -> def.create(settings))
- .peek(c -> c.setBack(back))
- .collect(Collectors.toList());
- Collections.shuffle(guild, settings.getRandom());
- return guild.subList(0, settings.getNbPlayers() + 2);
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/Definition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/Definition.java
deleted file mode 100644
index 6c6b4b19..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/Definition.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-import org.luxons.sevenwonders.game.Settings;
-
-/**
- * Represents a deserialized JSON definition of some data about the game. It is settings-agnostic. An instance of
- * in-game data can be generated from this, given specific game settings.
- *
- * @param <T>
- * the type of in-game object that can be generated from this definition
- */
-public interface Definition<T> {
-
- /**
- * Creates a T object from the given settings. This method mustn't mutate this Definition as it may be called
- * multiple times with different settings.
- *
- * @param settings
- * the game settings to use to generate a game-specific object from this definition
- *
- * @return the new game-specific object created from this definition
- */
- T create(Settings settings);
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/EffectsDefinition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/EffectsDefinition.java
deleted file mode 100644
index e35463d4..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/EffectsDefinition.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import org.luxons.sevenwonders.game.Settings;
-import org.luxons.sevenwonders.game.effects.BonusPerBoardElement;
-import org.luxons.sevenwonders.game.effects.Discount;
-import org.luxons.sevenwonders.game.effects.Effect;
-import org.luxons.sevenwonders.game.effects.GoldIncrease;
-import org.luxons.sevenwonders.game.effects.MilitaryReinforcements;
-import org.luxons.sevenwonders.game.effects.ProductionIncrease;
-import org.luxons.sevenwonders.game.effects.RawPointsIncrease;
-import org.luxons.sevenwonders.game.effects.ScienceProgress;
-import org.luxons.sevenwonders.game.effects.SpecialAbility;
-import org.luxons.sevenwonders.game.effects.SpecialAbilityActivation;
-
-@SuppressWarnings("unused") // the fields are injected by Gson
-public class EffectsDefinition implements Definition<List<Effect>> {
-
- private GoldIncrease gold;
-
- private MilitaryReinforcements military;
-
- private ScienceProgress science;
-
- private Discount discount;
-
- private BonusPerBoardElement perBoardElement;
-
- private ProductionIncrease production;
-
- private RawPointsIncrease points;
-
- private SpecialAbility action;
-
- @Override
- public List<Effect> create(Settings settings) {
- List<Effect> effects = new ArrayList<>();
- if (gold != null) {
- effects.add(gold);
- }
- if (military != null) {
- effects.add(military);
- }
- if (science != null) {
- effects.add(science);
- }
- if (discount != null) {
- effects.add(discount);
- }
- if (perBoardElement != null) {
- effects.add(perBoardElement);
- }
- if (production != null) {
- effects.add(production);
- }
- if (points != null) {
- effects.add(points);
- }
- if (action != null) {
- effects.add(new SpecialAbilityActivation(action));
- }
- return effects;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderDefinition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderDefinition.java
deleted file mode 100644
index a972a517..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderDefinition.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-import java.util.Map;
-
-import org.luxons.sevenwonders.game.Settings;
-import org.luxons.sevenwonders.game.wonders.Wonder;
-
-@SuppressWarnings("unused,MismatchedQueryAndUpdateOfCollection") // the fields are injected by Gson
-public class WonderDefinition implements Definition<Wonder> {
-
- private String name;
-
- private Map<WonderSide, WonderSideDefinition> sides;
-
- @Override
- public Wonder create(Settings settings) {
- Wonder wonder = new Wonder();
- wonder.setName(name);
-
- WonderSideDefinition wonderSideDef = sides.get(settings.pickWonderSide());
- wonder.setInitialResource(wonderSideDef.getInitialResource());
- wonder.setStages(wonderSideDef.createStages(settings));
- wonder.setImage(wonderSideDef.getImage());
- return wonder;
- }
-
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSide.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSide.java
deleted file mode 100644
index 34091350..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSide.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-public enum WonderSide {
- A,
- B
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSideDefinition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSideDefinition.java
deleted file mode 100644
index c84bba4e..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSideDefinition.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.luxons.sevenwonders.game.Settings;
-import org.luxons.sevenwonders.game.resources.ResourceType;
-import org.luxons.sevenwonders.game.wonders.WonderStage;
-
-// the fields are injected by Gson
-@SuppressWarnings("unused,MismatchedQueryAndUpdateOfCollection")
-class WonderSideDefinition {
-
- private ResourceType initialResource;
-
- private List<WonderStageDefinition> stages;
-
- private String image;
-
- ResourceType getInitialResource() {
- return initialResource;
- }
-
- List<WonderStage> createStages(Settings settings) {
- return stages.stream().map(def -> def.create(settings)).collect(Collectors.toList());
- }
-
- String getImage() {
- return image;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethod.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethod.java
deleted file mode 100644
index 08aaad14..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethod.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-import java.util.Random;
-
-public enum WonderSidePickMethod {
- ALL_A {
- @Override
- public WonderSide pickSide(Random random, WonderSide lastPickedSide) {
- return WonderSide.A;
- }
- },
- ALL_B {
- @Override
- public WonderSide pickSide(Random random, WonderSide lastPickedSide) {
- return WonderSide.B;
- }
- },
- EACH_RANDOM {
- @Override
- public WonderSide pickSide(Random random, WonderSide lastPickedSide) {
- return random.nextBoolean() ? WonderSide.A : WonderSide.B;
- }
- },
- SAME_RANDOM_FOR_ALL {
- @Override
- public WonderSide pickSide(Random random, WonderSide lastPickedSide) {
- if (lastPickedSide == null) {
- return random.nextBoolean() ? WonderSide.A : WonderSide.B;
- } else {
- return lastPickedSide;
- }
- }
- };
-
- public abstract WonderSide pickSide(Random random, WonderSide lastPickedSide);
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderStageDefinition.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderStageDefinition.java
deleted file mode 100644
index 230484ee..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/definitions/WonderStageDefinition.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package org.luxons.sevenwonders.game.data.definitions;
-
-import org.luxons.sevenwonders.game.Settings;
-import org.luxons.sevenwonders.game.cards.Requirements;
-import org.luxons.sevenwonders.game.wonders.WonderStage;
-
-@SuppressWarnings("unused") // the fields are injected by Gson
-public class WonderStageDefinition implements Definition<WonderStage> {
-
- private Requirements requirements;
-
- private EffectsDefinition effects;
-
- @Override
- public WonderStage create(Settings settings) {
- return new WonderStage(requirements, effects.create(settings));
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializer.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializer.java
deleted file mode 100644
index c0a75d80..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializer.java
+++ /dev/null
@@ -1,48 +0,0 @@
-package org.luxons.sevenwonders.game.data.serializers;
-
-import java.lang.reflect.Type;
-
-import org.luxons.sevenwonders.game.effects.Effect;
-import org.luxons.sevenwonders.game.effects.GoldIncrease;
-import org.luxons.sevenwonders.game.effects.MilitaryReinforcements;
-import org.luxons.sevenwonders.game.effects.RawPointsIncrease;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-public class NumericEffectSerializer implements JsonSerializer<Effect>, JsonDeserializer<Effect> {
-
- @Override
- public JsonElement serialize(Effect effect, Type typeOfSrc, JsonSerializationContext context) {
- int value;
- if (MilitaryReinforcements.class.equals(typeOfSrc)) {
- value = ((MilitaryReinforcements) effect).getCount();
- } else if (GoldIncrease.class.equals(typeOfSrc)) {
- value = ((GoldIncrease) effect).getAmount();
- } else if (RawPointsIncrease.class.equals(typeOfSrc)) {
- value = ((RawPointsIncrease) effect).getPoints();
- } else {
- throw new IllegalArgumentException("Unknown numeric effect " + typeOfSrc.getTypeName());
- }
- return new JsonPrimitive(value);
- }
-
- @Override
- public Effect deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- int value = json.getAsInt();
- if (MilitaryReinforcements.class.equals(typeOfT)) {
- return new MilitaryReinforcements(value);
- } else if (GoldIncrease.class.equals(typeOfT)) {
- return new GoldIncrease(value);
- } else if (RawPointsIncrease.class.equals(typeOfT)) {
- return new RawPointsIncrease(value);
- }
- throw new IllegalArgumentException("Unknown numeric effet " + typeOfT.getTypeName());
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializer.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializer.java
deleted file mode 100644
index c3eb1386..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializer.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package org.luxons.sevenwonders.game.data.serializers;
-
-import java.lang.reflect.Type;
-
-import org.luxons.sevenwonders.game.effects.ProductionIncrease;
-import org.luxons.sevenwonders.game.resources.Production;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-public class ProductionIncreaseSerializer implements JsonSerializer<ProductionIncrease>,
- JsonDeserializer<ProductionIncrease> {
-
- @Override
- public JsonElement serialize(ProductionIncrease productionIncrease, Type typeOfSrc,
- JsonSerializationContext context) {
- Production production = productionIncrease.getProduction();
- JsonElement json = context.serialize(production);
- if (!json.isJsonNull() && !productionIncrease.isSellable()) {
- return new JsonPrimitive(wrapInBrackets(json.getAsString()));
- }
- return json;
- }
-
- private String wrapInBrackets(String str) {
- return '(' + str + ')';
- }
-
- @Override
- public ProductionIncrease deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- ProductionIncrease productionIncrease = new ProductionIncrease();
-
- String resourcesStr = json.getAsString();
- boolean isSellable = !resourcesStr.startsWith("(");
- if (!isSellable) {
- resourcesStr = unwrapBrackets(resourcesStr);
- json = new JsonPrimitive(resourcesStr);
- }
- productionIncrease.setSellable(isSellable);
-
- Production production = context.deserialize(json, Production.class);
- productionIncrease.setProduction(production);
- return productionIncrease;
- }
-
- private static String unwrapBrackets(String str) {
- return str.substring(1, str.length() - 1);
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.java
deleted file mode 100644
index 178134bb..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package org.luxons.sevenwonders.game.data.serializers;
-
-import java.lang.reflect.Type;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import org.luxons.sevenwonders.game.resources.Production;
-import org.luxons.sevenwonders.game.resources.ResourceType;
-import org.luxons.sevenwonders.game.resources.Resources;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonNull;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-public class ProductionSerializer implements JsonSerializer<Production>, JsonDeserializer<Production> {
-
- @Override
- public JsonElement serialize(Production production, Type typeOfSrc, JsonSerializationContext context) {
- Resources fixedResources = production.getFixedResources();
- Set<Set<ResourceType>> choices = production.getAlternativeResources();
- if (fixedResources.isEmpty()) {
- return serializeAsChoice(choices, context);
- } else if (choices.isEmpty()) {
- return serializeAsResources(fixedResources, context);
- } else {
- throw new IllegalArgumentException("Cannot serialize a production with mixed fixed resources and choices");
- }
- }
-
- private static JsonElement serializeAsChoice(Set<Set<ResourceType>> choices, JsonSerializationContext context) {
- if (choices.isEmpty()) {
- return JsonNull.INSTANCE;
- }
- if (choices.size() > 1) {
- throw new IllegalArgumentException("Cannot serialize a production with more than one choice");
- }
- String str = choices.stream()
- .flatMap(Set::stream)
- .map(ResourceType::getSymbol)
- .map(Object::toString)
- .collect(Collectors.joining("/"));
- return context.serialize(str);
- }
-
- private static JsonElement serializeAsResources(Resources fixedResources, JsonSerializationContext context) {
- return context.serialize(fixedResources);
- }
-
- @Override
- public Production deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- String resourcesStr = json.getAsString();
- Production production = new Production();
- if (resourcesStr.contains("/")) {
- production.addChoice(createChoice(resourcesStr));
- } else {
- Resources fixedResources = context.deserialize(json, Resources.class);
- production.addAll(fixedResources);
- }
- return production;
- }
-
- private ResourceType[] createChoice(String choiceStr) {
- String[] symbols = choiceStr.split("/");
- ResourceType[] choice = new ResourceType[symbols.length];
- for (int i = 0; i < symbols.length; i++) {
- if (symbols[i].length() != 1) {
- throw new IllegalArgumentException("Choice elements must be resource types, got " + symbols[i]);
- }
- choice[i] = ResourceType.fromSymbol(symbols[i].charAt(0));
- }
- return choice;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.java
deleted file mode 100644
index d2a49180..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.java
+++ /dev/null
@@ -1,31 +0,0 @@
-package org.luxons.sevenwonders.game.data.serializers;
-
-import java.lang.reflect.Type;
-
-import org.luxons.sevenwonders.game.resources.ResourceType;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-public class ResourceTypeSerializer implements JsonSerializer<ResourceType>, JsonDeserializer<ResourceType> {
-
- @Override
- public JsonElement serialize(ResourceType type, Type typeOfSrc, JsonSerializationContext context) {
- return new JsonPrimitive(type.getSymbol());
- }
-
- @Override
- public ResourceType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- String str = json.getAsString();
- if (str.isEmpty()) {
- throw new IllegalArgumentException("Empty string is not a valid resource type");
- }
- return ResourceType.fromSymbol(str.charAt(0));
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializer.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializer.java
deleted file mode 100644
index 89d3e723..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializer.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.luxons.sevenwonders.game.data.serializers;
-
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.stream.Collectors;
-
-import org.luxons.sevenwonders.game.resources.ResourceType;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-public class ResourceTypesSerializer implements JsonSerializer<List<ResourceType>>,
- JsonDeserializer<List<ResourceType>> {
-
- @Override
- public JsonElement serialize(List<ResourceType> resources, Type typeOfSrc, JsonSerializationContext context) {
- String s = resources.stream().map(ResourceType::getSymbol).map(Object::toString).collect(Collectors.joining());
- return new JsonPrimitive(s);
- }
-
- @Override
- public List<ResourceType> deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- String s = json.getAsString();
- List<ResourceType> resources = new ArrayList<>();
- for (char c : s.toCharArray()) {
- resources.add(ResourceType.fromSymbol(c));
- }
- return resources;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.java
deleted file mode 100644
index 9c27b2a1..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package org.luxons.sevenwonders.game.data.serializers;
-
-import java.lang.reflect.Type;
-import java.util.stream.Collectors;
-
-import org.luxons.sevenwonders.game.resources.ResourceType;
-import org.luxons.sevenwonders.game.resources.Resources;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonNull;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-public class ResourcesSerializer implements JsonSerializer<Resources>, JsonDeserializer<Resources> {
-
- @Override
- public JsonElement serialize(Resources resources, Type typeOfSrc, JsonSerializationContext context) {
- String s = resources.asList()
- .stream()
- .map(ResourceType::getSymbol)
- .map(Object::toString)
- .collect(Collectors.joining());
- return s.isEmpty() ? JsonNull.INSTANCE : new JsonPrimitive(s);
- }
-
- @Override
- public Resources deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- String s = json.getAsString();
- Resources resources = new Resources();
- for (char c : s.toCharArray()) {
- resources.add(ResourceType.fromSymbol(c), 1);
- }
- return resources;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.java
deleted file mode 100644
index cecad405..00000000
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package org.luxons.sevenwonders.game.data.serializers;
-
-import java.lang.reflect.Type;
-
-import org.luxons.sevenwonders.game.boards.Science;
-import org.luxons.sevenwonders.game.boards.ScienceType;
-import org.luxons.sevenwonders.game.effects.ScienceProgress;
-
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonNull;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonPrimitive;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-
-public class ScienceProgressSerializer implements JsonSerializer<ScienceProgress>, JsonDeserializer<ScienceProgress> {
-
- @Override
- public JsonElement serialize(ScienceProgress scienceProgress, Type typeOfSrc, JsonSerializationContext context) {
- Science science = scienceProgress.getScience();
-
- if (science.size() > 1) {
- throw new UnsupportedOperationException("Cannot serialize science containing more than one element");
- }
-
- for (ScienceType type : ScienceType.values()) {
- int quantity = science.getQuantity(type);
- if (quantity == 1) {
- return context.serialize(type);
- }
- }
-
- if (science.getJokers() == 1) {
- return new JsonPrimitive("any");
- }
-
- return JsonNull.INSTANCE;
- }
-
- @Override
- public ScienceProgress deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
- throws JsonParseException {
- String s = json.getAsString();
- ScienceProgress scienceProgress = new ScienceProgress();
- Science science = new Science();
- if ("any".equals(s)) {
- science.addJoker(1);
- } else {
- science.add(deserializeScienceType(json, context), 1);
- }
- scienceProgress.setScience(science);
- return scienceProgress;
- }
-
- private ScienceType deserializeScienceType(JsonElement json, JsonDeserializationContext context) {
- ScienceType type = context.deserialize(json, ScienceType.class);
- if (type == null) {
- throw new IllegalArgumentException("Invalid science type " + json.getAsString());
- }
- return type;
- }
-}
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourceType.java b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourceType.java
index baad8451..644437df 100644
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourceType.java
+++ b/game-engine/src/main/java/org/luxons/sevenwonders/game/resources/ResourceType.java
@@ -26,6 +26,14 @@ public enum ResourceType {
this.symbol = symbol;
}
+ public static ResourceType fromSymbol(String symbol) {
+ if (symbol.length() != 1) {
+ throw new IllegalArgumentException("The given symbol must be a valid single-char resource type, got "
+ + symbol);
+ }
+ return fromSymbol(symbol.charAt(0));
+ }
+
public static ResourceType fromSymbol(Character symbol) {
ResourceType type = typesPerSymbol.get(symbol);
if (type == null) {
diff --git a/game-engine/src/main/java/org/luxons/sevenwonders/game/Settings.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/Settings.kt
index 70dddccf..fac3979e 100644
--- a/game-engine/src/main/java/org/luxons/sevenwonders/game/Settings.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/Settings.kt
@@ -1,8 +1,8 @@
package org.luxons.sevenwonders.game
import org.luxons.sevenwonders.game.api.CustomizableSettings
-import org.luxons.sevenwonders.game.data.definitions.WonderSide
-import org.luxons.sevenwonders.game.data.definitions.WonderSidePickMethod
+import org.luxons.sevenwonders.game.data.WonderSide
+import org.luxons.sevenwonders.game.data.WonderSidePickMethod
import java.util.Random
class Settings @JvmOverloads constructor(
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/CustomizableSettings.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/CustomizableSettings.kt
index 354aecb6..e4efb9e3 100644
--- a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/CustomizableSettings.kt
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/api/CustomizableSettings.kt
@@ -1,6 +1,6 @@
package org.luxons.sevenwonders.game.api
-import org.luxons.sevenwonders.game.data.definitions.WonderSidePickMethod
+import org.luxons.sevenwonders.game.data.WonderSidePickMethod
data class CustomizableSettings(
val randomSeedForTests: Long? = null,
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt
new file mode 100644
index 00000000..b7a859eb
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinition.kt
@@ -0,0 +1,30 @@
+package org.luxons.sevenwonders.game.data
+
+import org.luxons.sevenwonders.game.Game
+import org.luxons.sevenwonders.game.Settings
+import org.luxons.sevenwonders.game.api.CustomizableSettings
+import org.luxons.sevenwonders.game.boards.Board
+import org.luxons.sevenwonders.game.data.definitions.DecksDefinition
+import org.luxons.sevenwonders.game.data.definitions.WonderDefinition
+
+class GameDefinition internal constructor(
+ rules: GlobalRules,
+ private val wonders: Array<WonderDefinition>,
+ private val decksDefinition: DecksDefinition
+) {
+ val minPlayers: Int = rules.minPlayers
+ val maxPlayers: Int = rules.maxPlayers
+
+ fun initGame(id: Long, customSettings: CustomizableSettings, nbPlayers: Int): Game {
+ val settings = Settings(nbPlayers, customSettings)
+ val boards = assignBoards(settings, nbPlayers)
+ val decks = decksDefinition.prepareDecks(settings)
+ return Game(id, settings, nbPlayers, boards, decks)
+ }
+
+ private fun assignBoards(settings: Settings, nbPlayers: Int): List<Board> {
+ val randomizedWonders = wonders.toMutableList()
+ randomizedWonders.shuffle(settings.random)
+ return randomizedWonders.take(nbPlayers).mapIndexed { i, wDef -> Board(wDef.create(settings), i, settings) }
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinitionLoader.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinitionLoader.kt
new file mode 100644
index 00000000..22ecc488
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GameDefinitionLoader.kt
@@ -0,0 +1,63 @@
+package org.luxons.sevenwonders.game.data
+
+import com.github.salomonbrys.kotson.registerTypeAdapter
+import com.github.salomonbrys.kotson.typeToken
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonNull
+import com.google.gson.JsonPrimitive
+import org.luxons.sevenwonders.game.data.definitions.DecksDefinition
+import org.luxons.sevenwonders.game.data.definitions.WonderDefinition
+import org.luxons.sevenwonders.game.data.serializers.NumericEffectSerializer
+import org.luxons.sevenwonders.game.data.serializers.ProductionIncreaseSerializer
+import org.luxons.sevenwonders.game.data.serializers.ProductionSerializer
+import org.luxons.sevenwonders.game.data.serializers.ResourceTypeSerializer
+import org.luxons.sevenwonders.game.data.serializers.ResourceTypesSerializer
+import org.luxons.sevenwonders.game.data.serializers.ResourcesSerializer
+import org.luxons.sevenwonders.game.data.serializers.ScienceProgressSerializer
+import org.luxons.sevenwonders.game.effects.GoldIncrease
+import org.luxons.sevenwonders.game.effects.MilitaryReinforcements
+import org.luxons.sevenwonders.game.effects.ProductionIncrease
+import org.luxons.sevenwonders.game.effects.RawPointsIncrease
+import org.luxons.sevenwonders.game.effects.ScienceProgress
+import org.luxons.sevenwonders.game.resources.Production
+import org.luxons.sevenwonders.game.resources.ResourceType
+import org.luxons.sevenwonders.game.resources.Resources
+
+class GameDefinitionLoader {
+
+ val gameDefinition: GameDefinition by lazy { load() }
+
+ private fun load(): GameDefinition {
+ val gson: Gson = createGson()
+ val rules = loadJson("global_rules.json", GlobalRules::class.java, gson)
+ val wonders = loadJson("wonders.json", Array<WonderDefinition>::class.java, gson)
+ val decksDefinition = loadJson("cards.json", DecksDefinition::class.java, gson)
+ return GameDefinition(rules, wonders, decksDefinition)
+ }
+
+ private fun <T> loadJson(filename: String, clazz: Class<T>, gson: Gson): T {
+ val packageAsPath = GameDefinitionLoader::class.java.`package`.name.replace('.', '/')
+ val resourcePath = "/$packageAsPath/$filename"
+ val resource = GameDefinitionLoader::class.java.getResource(resourcePath)
+ val json = resource.readText()
+ return gson.fromJson(json, clazz)
+ }
+
+ private fun createGson(): Gson {
+ return GsonBuilder().disableHtmlEscaping()
+ .registerTypeAdapter<Resources>(ResourcesSerializer())
+ .registerTypeAdapter<ResourceType>(ResourceTypeSerializer())
+ .registerTypeAdapter<List<ResourceType>>(ResourceTypesSerializer())
+ .registerTypeAdapter<Production>(ProductionSerializer())
+ .registerTypeAdapter<ProductionIncrease>(ProductionIncreaseSerializer())
+ .registerTypeAdapter<MilitaryReinforcements>(NumericEffectSerializer())
+ .registerTypeAdapter<RawPointsIncrease>(NumericEffectSerializer())
+ .registerTypeAdapter<GoldIncrease>(NumericEffectSerializer())
+ .registerTypeAdapter<ScienceProgress>(ScienceProgressSerializer())
+ .create()
+ }
+
+ private inline fun <reified T: Any> GsonBuilder.registerTypeAdapter(typeAdapter: Any): GsonBuilder
+ = this.registerTypeAdapter(typeToken<T>(), typeAdapter)
+}
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
new file mode 100644
index 00000000..f472cc2a
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/GlobalRules.kt
@@ -0,0 +1,6 @@
+package org.luxons.sevenwonders.game.data
+
+internal data class GlobalRules(
+ val minPlayers: Int,
+ val maxPlayers: Int
+)
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/WonderSide.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/WonderSide.kt
new file mode 100644
index 00000000..4a818fc4
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/WonderSide.kt
@@ -0,0 +1,6 @@
+package org.luxons.sevenwonders.game.data
+
+enum class WonderSide {
+ A,
+ B
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/WonderSidePickMethod.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/WonderSidePickMethod.kt
new file mode 100644
index 00000000..29fe2126
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/WonderSidePickMethod.kt
@@ -0,0 +1,28 @@
+package org.luxons.sevenwonders.game.data
+
+import java.util.Random
+
+enum class WonderSidePickMethod {
+ ALL_A {
+ override fun pickSide(random: Random, lastPickedSide: WonderSide?): WonderSide {
+ return WonderSide.A
+ }
+ },
+ ALL_B {
+ override fun pickSide(random: Random, lastPickedSide: WonderSide?): WonderSide {
+ return WonderSide.B
+ }
+ },
+ EACH_RANDOM {
+ override fun pickSide(random: Random, lastPickedSide: WonderSide?): WonderSide {
+ return if (random.nextBoolean()) WonderSide.A else WonderSide.B
+ }
+ },
+ SAME_RANDOM_FOR_ALL {
+ override fun pickSide(random: Random, lastPickedSide: WonderSide?): WonderSide {
+ return lastPickedSide ?: if (random.nextBoolean()) WonderSide.A else WonderSide.B
+ }
+ };
+
+ abstract fun pickSide(random: Random, lastPickedSide: WonderSide?): WonderSide
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt
new file mode 100644
index 00000000..2827564b
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/CardDefinition.kt
@@ -0,0 +1,20 @@
+package org.luxons.sevenwonders.game.data.definitions
+
+import org.luxons.sevenwonders.game.cards.Card
+import org.luxons.sevenwonders.game.cards.Color
+import org.luxons.sevenwonders.game.cards.Requirements
+
+internal class CardDefinition(
+ private val name: String,
+ private val color: Color,
+ private val requirements: Requirements? = null,
+ private val effect: EffectsDefinition,
+ private val chainParent: String? = null,
+ private val chainChildren: List<String> = emptyList(),
+ private val image: String? = null,
+ private val countPerNbPlayer: Map<Int, Int>
+) {
+ fun create(nbPlayers: Int): List<Card> = List( countPerNbPlayer[nbPlayers]!!) { create() }
+
+ fun create(): Card = Card(name, color, requirements, effect.create(), chainParent, chainChildren, image)
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt
new file mode 100644
index 00000000..a09f0642
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/DecksDefinition.kt
@@ -0,0 +1,53 @@
+package org.luxons.sevenwonders.game.data.definitions
+
+import org.luxons.sevenwonders.game.Settings
+import org.luxons.sevenwonders.game.cards.Card
+import org.luxons.sevenwonders.game.cards.CardBack
+import org.luxons.sevenwonders.game.cards.Decks
+
+internal class DecksDefinition(
+ private val age1: List<CardDefinition>,
+ private val age2: List<CardDefinition>,
+ private val age3: List<CardDefinition>,
+ private val age1Back: String,
+ private val age2Back: String,
+ private val age3Back: String,
+ private val guildCards: List<CardDefinition>
+) {
+ fun prepareDecks(settings: Settings): Decks {
+ val cardsPerAge = mapOf(
+ 1 to prepareStandardDeck(age1, settings, age1Back),
+ 2 to prepareStandardDeck(age2, settings, age2Back),
+ 3 to prepareAge3Deck(settings)
+ )
+ return Decks(cardsPerAge)
+ }
+
+ private fun prepareStandardDeck(defs: List<CardDefinition>, settings: Settings, backImage: String): List<Card> {
+ val back = CardBack(backImage)
+ val cards = createDeck(defs, settings, back).toMutableList()
+ cards.shuffle(settings.random)
+ return cards
+ }
+
+ private fun prepareAge3Deck(settings: Settings): List<Card> {
+ val back = CardBack(age3Back)
+ val age3deck = createDeck(age3, settings, back).toMutableList()
+ age3deck.addAll(createGuildCards(settings, back))
+ age3deck.shuffle(settings.random)
+ return age3deck
+ }
+
+ private fun createDeck(defs: List<CardDefinition>, settings: Settings, back: CardBack): List<Card> {
+ return defs.flatMap { it.create(settings.nbPlayers) }.onEach { it.back = back }
+ }
+
+ private fun createGuildCards(settings: Settings, back: CardBack): List<Card> {
+ val guild = guildCards
+ .map { it.create() }
+ .map { c -> c.back = back; c }
+ .toMutableList()
+ guild.shuffle(settings.random)
+ return guild.subList(0, settings.nbPlayers + 2)
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/EffectsDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/EffectsDefinition.kt
new file mode 100644
index 00000000..978ec4a6
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/EffectsDefinition.kt
@@ -0,0 +1,53 @@
+package org.luxons.sevenwonders.game.data.definitions
+
+import org.luxons.sevenwonders.game.effects.BonusPerBoardElement
+import org.luxons.sevenwonders.game.effects.Discount
+import org.luxons.sevenwonders.game.effects.Effect
+import org.luxons.sevenwonders.game.effects.GoldIncrease
+import org.luxons.sevenwonders.game.effects.MilitaryReinforcements
+import org.luxons.sevenwonders.game.effects.ProductionIncrease
+import org.luxons.sevenwonders.game.effects.RawPointsIncrease
+import org.luxons.sevenwonders.game.effects.ScienceProgress
+import org.luxons.sevenwonders.game.effects.SpecialAbility
+import org.luxons.sevenwonders.game.effects.SpecialAbilityActivation
+import java.util.ArrayList
+
+internal data class EffectsDefinition(
+ private val gold: GoldIncrease? = null,
+ private val military: MilitaryReinforcements? = null,
+ private val science: ScienceProgress? = null,
+ private val discount: Discount? = null,
+ private val perBoardElement: BonusPerBoardElement? = null,
+ private val production: ProductionIncrease? = null,
+ private val points: RawPointsIncrease? = null,
+ private val action: SpecialAbility? = null
+) {
+ fun create(): List<Effect> {
+ val effects = ArrayList<Effect>()
+ if (gold != null) {
+ effects.add(gold)
+ }
+ if (military != null) {
+ effects.add(military)
+ }
+ if (science != null) {
+ effects.add(science)
+ }
+ if (discount != null) {
+ effects.add(discount)
+ }
+ if (perBoardElement != null) {
+ effects.add(perBoardElement)
+ }
+ if (production != null) {
+ effects.add(production)
+ }
+ if (points != null) {
+ effects.add(points)
+ }
+ if (action != null) {
+ effects.add(SpecialAbilityActivation(action))
+ }
+ return effects
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderDefinition.kt
new file mode 100644
index 00000000..558f16e8
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderDefinition.kt
@@ -0,0 +1,21 @@
+package org.luxons.sevenwonders.game.data.definitions
+
+import org.luxons.sevenwonders.game.Settings
+import org.luxons.sevenwonders.game.data.WonderSide
+import org.luxons.sevenwonders.game.wonders.Wonder
+
+internal data class WonderDefinition(
+ private val name: String,
+ private val sides: Map<WonderSide, WonderSideDefinition>
+) {
+ fun create(settings: Settings): Wonder {
+ val wonder = Wonder()
+ wonder.name = name
+
+ val wonderSideDef = sides[settings.pickWonderSide()]!!
+ wonder.initialResource = wonderSideDef.initialResource
+ wonder.stages = wonderSideDef.createStages()
+ wonder.image = wonderSideDef.image
+ return wonder
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderSideDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderSideDefinition.kt
new file mode 100644
index 00000000..247432e3
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderSideDefinition.kt
@@ -0,0 +1,14 @@
+package org.luxons.sevenwonders.game.data.definitions
+
+import org.luxons.sevenwonders.game.resources.ResourceType
+import org.luxons.sevenwonders.game.wonders.WonderStage
+
+internal class WonderSideDefinition(
+ val initialResource: ResourceType,
+ private val stages: List<WonderStageDefinition>,
+ val image: String
+) {
+ fun createStages(): List<WonderStage> {
+ return stages.map { def -> def.create() }
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderStageDefinition.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderStageDefinition.kt
new file mode 100644
index 00000000..3d2872d0
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/definitions/WonderStageDefinition.kt
@@ -0,0 +1,13 @@
+package org.luxons.sevenwonders.game.data.definitions
+
+import org.luxons.sevenwonders.game.cards.Requirements
+import org.luxons.sevenwonders.game.wonders.WonderStage
+
+internal class WonderStageDefinition(
+ private val requirements: Requirements,
+ private val effects: EffectsDefinition
+) {
+ fun create(): WonderStage {
+ return WonderStage(requirements, effects.create())
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializer.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializer.kt
new file mode 100644
index 00000000..510a511c
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/NumericEffectSerializer.kt
@@ -0,0 +1,40 @@
+package org.luxons.sevenwonders.game.data.serializers
+
+import java.lang.reflect.Type
+
+import org.luxons.sevenwonders.game.effects.Effect
+import org.luxons.sevenwonders.game.effects.GoldIncrease
+import org.luxons.sevenwonders.game.effects.MilitaryReinforcements
+import org.luxons.sevenwonders.game.effects.RawPointsIncrease
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonParseException
+import com.google.gson.JsonPrimitive
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+
+class NumericEffectSerializer : JsonSerializer<Effect>, JsonDeserializer<Effect> {
+
+ override fun serialize(effect: Effect, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
+ val value: Int = when (effect) {
+ is MilitaryReinforcements -> effect.count
+ is GoldIncrease -> effect.amount
+ is RawPointsIncrease -> effect.points
+ else -> throw IllegalArgumentException("Unknown numeric effect " + effect.javaClass.name)
+ }
+ return JsonPrimitive(value)
+ }
+
+ @Throws(JsonParseException::class)
+ override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Effect {
+ val value = json.asInt
+ return when (typeOfT) {
+ MilitaryReinforcements::class.java -> MilitaryReinforcements(value)
+ GoldIncrease::class.java -> GoldIncrease(value)
+ RawPointsIncrease::class.java -> RawPointsIncrease(value)
+ else -> throw IllegalArgumentException("Unknown numeric effet " + typeOfT.typeName)
+ }
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializer.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializer.kt
new file mode 100644
index 00000000..578f816b
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionIncreaseSerializer.kt
@@ -0,0 +1,51 @@
+package org.luxons.sevenwonders.game.data.serializers
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonParseException
+import com.google.gson.JsonPrimitive
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+import org.luxons.sevenwonders.game.effects.ProductionIncrease
+import org.luxons.sevenwonders.game.resources.Production
+import java.lang.reflect.Type
+
+class ProductionIncreaseSerializer : JsonSerializer<ProductionIncrease>, JsonDeserializer<ProductionIncrease> {
+
+ override fun serialize(
+ productionIncrease: ProductionIncrease,
+ typeOfSrc: Type,
+ context: JsonSerializationContext
+ ): JsonElement {
+ val production = productionIncrease.production
+ val json = context.serialize(production)
+ return if (!json.isJsonNull && !productionIncrease.isSellable) { JsonPrimitive("(${json.asString})")} else json
+ }
+
+ @Throws(JsonParseException::class)
+ override fun deserialize(
+ json: JsonElement,
+ typeOfT: Type,
+ context: JsonDeserializationContext
+ ): ProductionIncrease {
+ var json = json
+ val productionIncrease = ProductionIncrease()
+
+ var resourcesStr = json.asString
+ val isSellable = !resourcesStr.startsWith("(")
+ if (!isSellable) {
+ resourcesStr = unwrapBrackets(resourcesStr)
+ json = JsonPrimitive(resourcesStr)
+ }
+ productionIncrease.isSellable = isSellable
+
+ val production = context.deserialize<Production>(json, Production::class.java)
+ productionIncrease.production = production
+ return productionIncrease
+ }
+
+ private fun unwrapBrackets(str: String): String {
+ return str.substring(1, str.length - 1)
+ }
+}
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
new file mode 100644
index 00000000..538fdbb4
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ProductionSerializer.kt
@@ -0,0 +1,58 @@
+package org.luxons.sevenwonders.game.data.serializers
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonNull
+import com.google.gson.JsonParseException
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+import org.luxons.sevenwonders.game.resources.Production
+import org.luxons.sevenwonders.game.resources.ResourceType
+import org.luxons.sevenwonders.game.resources.Resources
+import java.lang.reflect.Type
+
+class ProductionSerializer : JsonSerializer<Production>, JsonDeserializer<Production> {
+
+ override fun serialize(production: Production, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
+ val fixedResources = production.fixedResources
+ val choices = production.alternativeResources
+ return when {
+ fixedResources.isEmpty -> serializeAsChoice(choices, context)
+ choices.isEmpty() -> serializeAsResources(fixedResources, context)
+ else -> throw IllegalArgumentException("Cannot serialize a production with mixed fixed resources and choices")
+ }
+ }
+
+ private fun serializeAsChoice(choices: Set<Set<ResourceType>>, context: JsonSerializationContext): JsonElement {
+ if (choices.isEmpty()) {
+ return JsonNull.INSTANCE
+ }
+ if (choices.size > 1) {
+ throw IllegalArgumentException("Cannot serialize a production with more than one choice")
+ }
+ val str = choices.flatMap { it }.map { it.symbol }.joinToString("/")
+ return context.serialize(str)
+ }
+
+ private fun serializeAsResources(fixedResources: Resources, context: JsonSerializationContext): JsonElement {
+ return context.serialize(fixedResources)
+ }
+
+ @Throws(JsonParseException::class)
+ override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): Production {
+ val resourcesStr = json.asString
+ val production = Production()
+ if (resourcesStr.contains("/")) {
+ production.addChoice(*createChoice(resourcesStr))
+ } else {
+ val fixedResources = context.deserialize<Resources>(json, Resources::class.java)
+ production.addAll(fixedResources)
+ }
+ return production
+ }
+
+ private fun createChoice(choiceStr: String): Array<ResourceType> {
+ return choiceStr.split("/").map { ResourceType.fromSymbol(it) }.toTypedArray()
+ }
+}
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
new file mode 100644
index 00000000..1de9334a
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypeSerializer.kt
@@ -0,0 +1,29 @@
+package org.luxons.sevenwonders.game.data.serializers
+
+import java.lang.reflect.Type
+
+import org.luxons.sevenwonders.game.resources.ResourceType
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonParseException
+import com.google.gson.JsonPrimitive
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+
+internal class ResourceTypeSerializer : JsonSerializer<ResourceType>, JsonDeserializer<ResourceType> {
+
+ override fun serialize(type: ResourceType, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
+ return JsonPrimitive(type.symbol!!)
+ }
+
+ @Throws(JsonParseException::class)
+ 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")
+ }
+ return ResourceType.fromSymbol(str[0])
+ }
+}
diff --git a/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializer.kt b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializer.kt
new file mode 100644
index 00000000..9ba21043
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourceTypesSerializer.kt
@@ -0,0 +1,39 @@
+package org.luxons.sevenwonders.game.data.serializers
+
+import java.lang.reflect.Type
+
+import org.luxons.sevenwonders.game.resources.ResourceType
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonParseException
+import com.google.gson.JsonPrimitive
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+
+internal class ResourceTypesSerializer : JsonSerializer<List<ResourceType>>, JsonDeserializer<List<ResourceType>> {
+
+ override fun serialize(
+ resources: List<ResourceType>,
+ typeOfSrc: Type,
+ context: JsonSerializationContext
+ ): JsonElement {
+ val s = resources.map { it.symbol }.joinToString("")
+ return JsonPrimitive(s)
+ }
+
+ @Throws(JsonParseException::class)
+ override fun deserialize(
+ json: JsonElement,
+ typeOfT: Type,
+ context: JsonDeserializationContext
+ ): List<ResourceType> {
+ val s = json.asString
+ val resources = ArrayList<ResourceType>()
+ for (c in s.toCharArray()) {
+ resources.add(ResourceType.fromSymbol(c))
+ }
+ return resources
+ }
+}
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
new file mode 100644
index 00000000..2baf7a35
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ResourcesSerializer.kt
@@ -0,0 +1,34 @@
+package org.luxons.sevenwonders.game.data.serializers
+
+import java.lang.reflect.Type
+import java.util.stream.Collectors
+
+import org.luxons.sevenwonders.game.resources.ResourceType
+import org.luxons.sevenwonders.game.resources.Resources
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonNull
+import com.google.gson.JsonParseException
+import com.google.gson.JsonPrimitive
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+
+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("")
+ 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
+ }
+}
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
new file mode 100644
index 00000000..ed383d63
--- /dev/null
+++ b/game-engine/src/main/kotlin/org/luxons/sevenwonders/game/data/serializers/ScienceProgressSerializer.kt
@@ -0,0 +1,57 @@
+package org.luxons.sevenwonders.game.data.serializers
+
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonNull
+import com.google.gson.JsonParseException
+import com.google.gson.JsonPrimitive
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+import org.luxons.sevenwonders.game.boards.Science
+import org.luxons.sevenwonders.game.boards.ScienceType
+import org.luxons.sevenwonders.game.effects.ScienceProgress
+import java.lang.reflect.Type
+
+internal class ScienceProgressSerializer : JsonSerializer<ScienceProgress>, JsonDeserializer<ScienceProgress> {
+
+ override fun serialize(
+ scienceProgress: ScienceProgress,
+ typeOfSrc: Type,
+ context: JsonSerializationContext
+ ): JsonElement {
+ val science = scienceProgress.science
+
+ if (science.size() > 1) {
+ throw UnsupportedOperationException("Cannot serialize science containing more than one element")
+ }
+
+ for (type in ScienceType.values()) {
+ val quantity = science.getQuantity(type)
+ if (quantity == 1) {
+ return context.serialize(type)
+ }
+ }
+
+ return if (science.jokers == 1) JsonPrimitive("any") else JsonNull.INSTANCE
+ }
+
+ @Throws(JsonParseException::class)
+ override fun deserialize(json: JsonElement, typeOfT: Type, context: JsonDeserializationContext): ScienceProgress {
+ val s = json.asString
+ val scienceProgress = ScienceProgress()
+ val science = Science()
+ if ("any" == s) {
+ science.addJoker(1)
+ } else {
+ science.add(deserializeScienceType(json, context), 1)
+ }
+ scienceProgress.science = science
+ return scienceProgress
+ }
+
+ private fun deserializeScienceType(json: JsonElement, context: JsonDeserializationContext): ScienceType {
+ return context.deserialize<ScienceType>(json, ScienceType::class.java)
+ ?: throw IllegalArgumentException("Invalid science type " + json.asString)
+ }
+}
diff --git a/game-engine/src/test/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethodTest.java b/game-engine/src/test/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethodTest.java
index 0b7de3d6..f45ad4ea 100644
--- a/game-engine/src/test/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethodTest.java
+++ b/game-engine/src/test/java/org/luxons/sevenwonders/game/data/definitions/WonderSidePickMethodTest.java
@@ -8,6 +8,8 @@ import org.junit.experimental.theories.DataPoints;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
+import org.luxons.sevenwonders.game.data.WonderSide;
+import org.luxons.sevenwonders.game.data.WonderSidePickMethod;
import static org.junit.Assert.assertEquals;
bgstack15