commit 345891d6a859a544da6afdbb883e68abeab403c4 parent aa16a8ce16058a3eb79c10bb23a09b571d24b632 Author: Kevin Corvisier <git@kevincorvisier.fr> Date: Sat, 28 Sep 2024 23:55:38 +0900 Use the Spring Framework to load configuration files and inject dependencies Diffstat:
19 files changed, 215 insertions(+), 204 deletions(-)
diff --git a/lombok.config b/lombok.config @@ -0,0 +1 @@ +lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Value +\ No newline at end of file diff --git a/pom.xml b/pom.xml @@ -19,6 +19,7 @@ <logback.version>1.2.10</logback.version> <lombok.version>1.18.22</lombok.version> <slf4j.version>1.7.33</slf4j.version> + <spring.version>6.1.13</spring.version> </properties> <repositories> @@ -28,6 +29,18 @@ </repository> </repositories> + <dependencyManagement> + <dependencies> + <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-framework-bom</artifactId> + <version>${spring.version}</version> + <type>pom</type> + <scope>import</scope> + </dependency> + </dependencies> + </dependencyManagement> + <dependencies> <dependency> <groupId>forge</groupId> @@ -36,6 +49,11 @@ </dependency> <dependency> + <groupId>org.springframework</groupId> + <artifactId>spring-context</artifactId> + </dependency> + + <dependency> <groupId>dev.dirs</groupId> <artifactId>directories</artifactId> <version>26</version> diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/ApplicationConfig.java b/src/main/java/fr/kevincorvisier/mtg/gdb/ApplicationConfig.java @@ -0,0 +1,16 @@ +package fr.kevincorvisier.mtg.gdb; + +import org.springframework.context.annotation.ComponentScan; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.PropertySource; + +@Configuration +@PropertySource("application.properties") +@PropertySource("crossover.properties") +@PropertySource("evaluation.properties") +@PropertySource("mutation.properties") +@PropertySource("population.properties") +@ComponentScan("fr.kevincorvisier.mtg.gdb") +public class ApplicationConfig +{ +} diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/Main.java b/src/main/java/fr/kevincorvisier/mtg/gdb/Main.java @@ -17,6 +17,7 @@ import org.apache.commons.compress.archivers.tar.TarArchiveEntry; import org.apache.commons.compress.archivers.tar.TarArchiveInputStream; import org.apache.commons.compress.compressors.bzip2.BZip2CompressorInputStream; import org.apache.commons.lang3.StringUtils; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; import com.google.common.io.ByteStreams; @@ -128,8 +129,11 @@ public class Main else ForgeUtils.setGameFormat(FModel.getFormats().get(formatName)); - final GeneticAlgorithm ga = new GeneticAlgorithm(properties); - ga.run(); + try (final AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class)) + { + final GeneticAlgorithm ga = context.getBean(GeneticAlgorithm.class); // 2 + ga.run(); + } } catch (final Throwable e) { diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/ai/ChildValidator.java b/src/main/java/fr/kevincorvisier/mtg/gdb/ai/ChildValidator.java @@ -2,27 +2,29 @@ package fr.kevincorvisier.mtg.gdb.ai; import java.util.Collection; import java.util.HashSet; -import java.util.Properties; import javax.annotation.Nullable; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; import forge.deck.Deck; import fr.kevincorvisier.mtg.gdb.utils.ForgeUtils; -import fr.kevincorvisier.mtg.gdb.utils.PropertiesUtils; import lombok.Data; +import lombok.RequiredArgsConstructor; +@Service +@RequiredArgsConstructor public class ChildValidator { private final Collection<Condition> conditions = new HashSet<>(); + @Value("${validation.child.max-unique-cards}") private final int maxUniqueCards; - public ChildValidator(final Properties properties) + @Value("${validation.child.conditions}") + public void setConditions(final String conditions) { - maxUniqueCards = PropertiesUtils.getInt(properties, "validation.child.max-unique-cards"); - final String conditions = PropertiesUtils.getStringOrDefault(properties, "validation.child.conditions", null); - if (conditions != null) { for (final String condition : StringUtils.split(conditions, ';')) diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/ai/GeneticAlgorithm.java b/src/main/java/fr/kevincorvisier/mtg/gdb/ai/GeneticAlgorithm.java @@ -5,27 +5,27 @@ import java.io.IOException; import java.util.Collection; import java.util.HashSet; import java.util.List; -import java.util.Properties; import java.util.Random; import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; import fr.kevincorvisier.mtg.gdb.Reloadable; import fr.kevincorvisier.mtg.gdb.evaluation.Evaluation; -import fr.kevincorvisier.mtg.gdb.evaluation.EvaluationFactory; import fr.kevincorvisier.mtg.gdb.operators.crossover.Crossover; -import fr.kevincorvisier.mtg.gdb.operators.crossover.CrossoverFactory; import fr.kevincorvisier.mtg.gdb.operators.mutation.Mutation; -import fr.kevincorvisier.mtg.gdb.operators.mutation.MutationFactory; import fr.kevincorvisier.mtg.gdb.operators.selection.HybridSelection; import fr.kevincorvisier.mtg.gdb.operators.selection.Selection; import fr.kevincorvisier.mtg.gdb.population.Individual; import fr.kevincorvisier.mtg.gdb.population.Population; import fr.kevincorvisier.mtg.gdb.population.PopulationFactory; -import fr.kevincorvisier.mtg.gdb.utils.PropertiesUtils; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j +@Service +@RequiredArgsConstructor public class GeneticAlgorithm implements Reloadable { private final Collection<Long> alreadyGenerated = new HashSet<>(); @@ -33,10 +33,10 @@ public class GeneticAlgorithm implements Reloadable protected final Random rnd = new Random(); // Operators - private final Crossover crossover = CrossoverFactory.create(); - private final Mutation mutation = MutationFactory.create(); + private final Crossover crossover; + private final Mutation mutation; private final Selection selection = new HybridSelection(); - private final List<Evaluation> evaluators = EvaluationFactory.create(); + private final List<Evaluation> evaluators; private final ChildValidator childValidator; private Population population = null; @@ -47,18 +47,13 @@ public class GeneticAlgorithm implements Reloadable private int noImprovementCount = 0; private long previousBestFingerprint = 0; + @Value("${max.generations}") private final int maxGenerations; + @Value("${max.no-improvement-count}") private final int maxNoImprovementCount; + @Value("${output.directory}") private final File outputDirectory; - public GeneticAlgorithm(final Properties properties) - { - maxGenerations = PropertiesUtils.getInt(properties, "max.generations"); - maxNoImprovementCount = PropertiesUtils.getInt(properties, "max.no-improvement-count"); - outputDirectory = PropertiesUtils.getFile(properties, "output.directory"); - childValidator = new ChildValidator(properties); - } - private boolean shouldContinue() { if (generationCount >= maxGenerations) diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/EvaluationFactory.java b/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/EvaluationFactory.java @@ -1,34 +0,0 @@ -package fr.kevincorvisier.mtg.gdb.evaluation; - -import java.util.List; -import java.util.Properties; - -import fr.kevincorvisier.mtg.gdb.population.PopulationFactory; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@NoArgsConstructor(access = AccessLevel.PRIVATE) // static class -public class EvaluationFactory -{ - public static List<Evaluation> create() - { - try - { - final Properties properties = new Properties(); - properties.load(PopulationFactory.class.getClassLoader().getResourceAsStream("evaluation.properties")); - - return List.of( // - // new GoldfishEvaluation(properties), // - new WinRatioEvaluation(properties)); - - } - catch (final Exception e) - { - log.error("Unable to load population configuration", e); - System.exit(0); - return null; // Makes the compiler happy - } - } -} diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/GoldfishEvaluation.java b/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/GoldfishEvaluation.java @@ -1,7 +1,10 @@ package fr.kevincorvisier.mtg.gdb.evaluation; import java.util.Arrays; -import java.util.Properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Service; import forge.deck.Deck; import forge.game.GameRules; @@ -11,59 +14,58 @@ import forge.game.player.RegisteredPlayer; import forge.player.GamePlayerUtil; import fr.kevincorvisier.mtg.gdb.population.Individual; import fr.kevincorvisier.mtg.gdb.population.Population; -import fr.kevincorvisier.mtg.gdb.utils.PropertiesUtils; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j +@Service +@RequiredArgsConstructor +@Conditional(GoldfishEvaluationEnabledCondition.class) public class GoldfishEvaluation implements Evaluation { + private static final Deck DECK_GOLDFISH = new Deck("Goldfish"); + static + { + DECK_GOLDFISH.getMain().add("Plains", 12); + DECK_GOLDFISH.getMain().add("Forest", 12); + DECK_GOLDFISH.getMain().add("Mountain", 12); + DECK_GOLDFISH.getMain().add("Swamp", 12); + DECK_GOLDFISH.getMain().add("Island", 12); + } + + @Value("${evaluation.goldfish.initial.min-games}") private final int initialMinGames; + @Value("${evaluation.goldfish.subsequent.min-games}") private final int subsequentMinGames; + @Value("${evaluation.goldfish.player.ai-profile}") private final String playerAiProfile; + @Value("${evaluation.goldfish.opponent.ai-profile}") private final String opponentAiProfile; - private final GameSimulator simulator; + @Value("${evaluation.goldfish.timeout.player-turns}") private final int gameTimeoutTurns; - - private final Deck goldfishDeck; - - /* package */ GoldfishEvaluation(final Properties properties) - { - initialMinGames = PropertiesUtils.getInt(properties, "evaluation.goldfish.initial.min-games"); - subsequentMinGames = PropertiesUtils.getInt(properties, "evaluation.goldfish.subsequent.min-games"); - playerAiProfile = PropertiesUtils.getString(properties, "evaluation.goldfish.player.ai-profile"); - opponentAiProfile = PropertiesUtils.getString(properties, "evaluation.goldfish.opponent.ai-profile"); - - gameTimeoutTurns = PropertiesUtils.getInt(properties, "evaluation.goldfish.timeout.player-turns"); - final int gameTimeoutSeconds = PropertiesUtils.getInt(properties, "evaluation.goldfish.timeout.seconds"); - simulator = new GameSimulator(gameTimeoutTurns, gameTimeoutSeconds); - - goldfishDeck = new Deck("Goldfish"); - goldfishDeck.getMain().add("Plains", 12); - goldfishDeck.getMain().add("Forest", 12); - goldfishDeck.getMain().add("Mountain", 12); - goldfishDeck.getMain().add("Swamp", 12); - goldfishDeck.getMain().add("Island", 12); - } + @Value("${evaluation.goldfish.timeout.seconds}") + private final int gameTimeoutSeconds; @Override public void reload() { - // TODO Auto-generated method stub - + // Nothing to reload } @Override public void evaluateFitness(final Population population) { - population.parallelStream() // - .forEach(individual -> calculationWinRatio(individual)); + population.parallelStream().forEach(this::calculationWinRatio); } private void calculationWinRatio(final Individual individual) { GoldfishEvaluationContext context = individual.getGoldfishContext(); if (context == null) - individual.setGoldfishContext(context = new GoldfishEvaluationContext(gameTimeoutTurns)); + { + context = new GoldfishEvaluationContext(gameTimeoutTurns); + individual.setGoldfishContext(context); + } final int minGames = context.getEndTurn().isEmpty() ? initialMinGames : subsequentMinGames; @@ -74,11 +76,13 @@ public class GoldfishEvaluation implements Evaluation final RegisteredPlayer player = new RegisteredPlayer(individual.getDeck()) .setPlayer(GamePlayerUtil.createAiPlayer(individual.getName(), playerAiProfile)); - final RegisteredPlayer opponent = new RegisteredPlayer(goldfishDeck) - .setPlayer(GamePlayerUtil.createAiPlayer(goldfishDeck.getName(), opponentAiProfile)); + final RegisteredPlayer opponent = new RegisteredPlayer(DECK_GOLDFISH) + .setPlayer(GamePlayerUtil.createAiPlayer(DECK_GOLDFISH.getName(), opponentAiProfile)); final Match mc = new Match(rules, Arrays.asList(player, opponent), "Test"); + final GameSimulator simulator = new GameSimulator(gameTimeoutTurns, gameTimeoutSeconds); + do { final GameSimulationResult result = simulator.simulateGame(player, mc); diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/GoldfishEvaluationEnabledCondition.java b/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/GoldfishEvaluationEnabledCondition.java @@ -0,0 +1,14 @@ +package fr.kevincorvisier.mtg.gdb.evaluation; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class GoldfishEvaluationEnabledCondition implements Condition +{ + @Override + public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) + { + return Boolean.TRUE.equals(context.getEnvironment().getProperty("evaluation.goldfish.enabled", Boolean.class)); + } +} diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioEvaluation.java b/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioEvaluation.java @@ -4,7 +4,10 @@ import java.io.File; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.Properties; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Service; import forge.deck.Deck; import forge.game.GameRules; @@ -15,33 +18,31 @@ import forge.player.GamePlayerUtil; import fr.kevincorvisier.mtg.gdb.population.Individual; import fr.kevincorvisier.mtg.gdb.population.Population; import fr.kevincorvisier.mtg.gdb.utils.ForgeUtils; -import fr.kevincorvisier.mtg.gdb.utils.PropertiesUtils; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @Slf4j +@Service +@RequiredArgsConstructor +@Conditional(WinRatioEvaluationEnabledCondition.class) public class WinRatioEvaluation implements Evaluation { private Collection<Deck> opponents = Collections.emptySet(); + @Value("${evaluation.win-ratio.opponents-directory}") private final File opponentsDirectory; + @Value("${evaluation.win-ratio.initial.min-games}") private final int initialMinGames; + @Value("${evaluation.win-ratio.subsequent.min-games}") private final int subsequentMinGames; + @Value("${evaluation.win-ratio.player.ai-profile}") private final String playerAiProfile; + @Value("${evaluation.win-ratio.opponent.ai-profile}") private final String opponentAiProfile; - private final GameSimulator simulator; - - public WinRatioEvaluation(final Properties properties) - { - opponentsDirectory = PropertiesUtils.getFile(properties, "evaluation.win-ratio.opponents-directory"); - initialMinGames = PropertiesUtils.getInt(properties, "evaluation.win-ratio.initial.min-games"); - subsequentMinGames = PropertiesUtils.getInt(properties, "evaluation.win-ratio.subsequent.min-games"); - playerAiProfile = PropertiesUtils.getString(properties, "evaluation.win-ratio.player.ai-profile"); - opponentAiProfile = PropertiesUtils.getString(properties, "evaluation.win-ratio.opponent.ai-profile"); - - final int gameTimeoutTurns = PropertiesUtils.getInt(properties, "evaluation.win-ratio.timeout.player-turns"); - final int gameTimeoutSeconds = PropertiesUtils.getInt(properties, "evaluation.win-ratio.timeout.seconds"); - simulator = new GameSimulator(gameTimeoutTurns, gameTimeoutSeconds); - } + @Value("${evaluation.win-ratio.timeout.player-turns}") + private final int gameTimeoutTurns; + @Value("${evaluation.win-ratio.timeout.seconds}") + private final int gameTimeoutSeconds; @Override public void reload() @@ -70,7 +71,10 @@ public class WinRatioEvaluation implements Evaluation { WinRatioEvaluationContext context = individual.getWinRatioContext(); if (context == null) - individual.setWinRatioContext(context = new WinRatioEvaluationContext()); + { + context = new WinRatioEvaluationContext(); + individual.setWinRatioContext(context); + } final int minGames = context.getGamePlayed().intValue() == 0 ? initialMinGames : subsequentMinGames; final int gamesPerMatch = (int) Math.ceil((double) (minGames) / (double) (opponents.size())); @@ -88,6 +92,8 @@ public class WinRatioEvaluation implements Evaluation final Match mc = new Match(rules, Arrays.asList(player, opponent), "Test"); + final GameSimulator simulator = new GameSimulator(gameTimeoutTurns, gameTimeoutSeconds); + do { final GameSimulationResult result = simulator.simulateGame(player, mc); diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioEvaluationEnabledCondition.java b/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioEvaluationEnabledCondition.java @@ -0,0 +1,14 @@ +package fr.kevincorvisier.mtg.gdb.evaluation; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class WinRatioEvaluationEnabledCondition implements Condition +{ + @Override + public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) + { + return Boolean.TRUE.equals(context.getEnvironment().getProperty("evaluation.win-ratio.enabled", Boolean.class)); + } +} diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/operators/crossover/CrossoverFactory.java b/src/main/java/fr/kevincorvisier/mtg/gdb/operators/crossover/CrossoverFactory.java @@ -1,30 +0,0 @@ -package fr.kevincorvisier.mtg.gdb.operators.crossover; - -import java.util.Properties; - -import fr.kevincorvisier.mtg.gdb.population.PopulationFactory; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@NoArgsConstructor(access = AccessLevel.PRIVATE) // static class -public class CrossoverFactory -{ - public static Crossover create() - { - try - { - final Properties properties = new Properties(); - properties.load(PopulationFactory.class.getClassLoader().getResourceAsStream("crossover.properties")); - - return new UniformCrossover(properties); - } - catch (final Exception e) - { - log.error("Unable to load crossover configuration", e); - System.exit(0); - return null; // Makes the compiler happy - } - } -} diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/operators/crossover/UniformCrossover.java b/src/main/java/fr/kevincorvisier/mtg/gdb/operators/crossover/UniformCrossover.java @@ -1,17 +1,19 @@ package fr.kevincorvisier.mtg.gdb.operators.crossover; import java.util.List; -import java.util.Properties; import java.util.Random; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckSection; import forge.item.PaperCard; import fr.kevincorvisier.mtg.gdb.population.Individual; -import fr.kevincorvisier.mtg.gdb.utils.PropertiesUtils; import lombok.RequiredArgsConstructor; +@Service @RequiredArgsConstructor public class UniformCrossover implements Crossover { @@ -22,13 +24,9 @@ public class UniformCrossover implements Crossover protected int generationCount = 0; private int individualIncrementCounter = 0; + @Value("${crossover.no-crossover-chance}") private final double noCrossoverChance; - /* package */ UniformCrossover(final Properties props) - { - noCrossoverChance = PropertiesUtils.getDouble(props, "crossover.no-crossover-chance"); - } - @Override public List<Individual> crossover(final Individual parent1, final Individual parent2) { diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/MutationFactory.java b/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/MutationFactory.java @@ -1,40 +0,0 @@ -package fr.kevincorvisier.mtg.gdb.operators.mutation; - -import java.util.Properties; - -import fr.kevincorvisier.mtg.gdb.population.PopulationFactory; -import fr.kevincorvisier.mtg.gdb.utils.PropertiesUtils; -import lombok.AccessLevel; -import lombok.NoArgsConstructor; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -@NoArgsConstructor(access = AccessLevel.PRIVATE) // static class -public class MutationFactory -{ - public static Mutation create() - { - try - { - final Properties properties = new Properties(); - properties.load(PopulationFactory.class.getClassLoader().getResourceAsStream("mutation.properties")); - - final String type = PropertiesUtils.getString(properties, "mutation.type"); - switch (type) - { - case "RANDOM": - return new RandomCardMutation(properties); - case "SWAP_SIDEBOARD": - return new SwapSideboardMutation(); - default: - throw new RuntimeException("Invalid mutation type: " + type + ", expected: RANDOM, SWAP_SIDEBOARD"); - } - } - catch (final Exception e) - { - log.error("Unable to load mutation configuration", e); - System.exit(0); - return null; // Makes the compiler happy - } - } -} diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/RandomCardMutation.java b/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/RandomCardMutation.java @@ -4,36 +4,39 @@ import java.io.File; import java.util.Collection; import java.util.Collections; import java.util.HashSet; -import java.util.Properties; import java.util.Random; import java.util.Set; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Service; + import forge.deck.CardPool; import forge.item.PaperCard; import forge.util.Aggregates; import fr.kevincorvisier.mtg.gdb.population.Individual; import fr.kevincorvisier.mtg.gdb.utils.ForgeUtils; -import fr.kevincorvisier.mtg.gdb.utils.PropertiesUtils; +import lombok.RequiredArgsConstructor; +@Service +@RequiredArgsConstructor +@Conditional(RandomCardMutationEnabledCondition.class) /* package */ class RandomCardMutation implements Mutation { private final Random rnd = new Random(); + + @Value("${mutation.random.card-pool}") private final File cardPoolFile; private Collection<String> cards; - public RandomCardMutation(final Properties properties) - { - cardPoolFile = PropertiesUtils.getFile(properties, "mutation.random.card-pool"); - } - @Override public void reload() { - final Set<String> cards = new HashSet<>(); + final Set<String> newCards = new HashSet<>(); for (final PaperCard card : ForgeUtils.loadCardsList(cardPoolFile)) - cards.add(card.getName()); - this.cards = Collections.unmodifiableSet(cards); + newCards.add(card.getName()); + this.cards = Collections.unmodifiableSet(newCards); } @Override diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/RandomCardMutationEnabledCondition.java b/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/RandomCardMutationEnabledCondition.java @@ -0,0 +1,14 @@ +package fr.kevincorvisier.mtg.gdb.operators.mutation; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class RandomCardMutationEnabledCondition implements Condition +{ + @Override + public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) + { + return "RANDOM".equalsIgnoreCase(context.getEnvironment().getProperty("mutation.type")); + } +} diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/SwapSideboardMutation.java b/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/SwapSideboardMutation.java @@ -1,14 +1,21 @@ package fr.kevincorvisier.mtg.gdb.operators.mutation; import java.util.Collection; +import java.util.Map.Entry; import java.util.Random; -import java.util.stream.Collectors; + +import org.springframework.context.annotation.Conditional; +import org.springframework.stereotype.Service; import forge.deck.DeckSection; import forge.item.PaperCard; import forge.util.Aggregates; import fr.kevincorvisier.mtg.gdb.population.Individual; +import lombok.RequiredArgsConstructor; +@Service +@RequiredArgsConstructor +@Conditional(SwapSideboardMutationEnabledCondition.class) /* package */ class SwapSideboardMutation implements Mutation { private final Random rnd = new Random(); @@ -31,11 +38,11 @@ import fr.kevincorvisier.mtg.gdb.population.Individual; return; final Collection<PaperCard> fromMainToSideboard = Aggregates.random(individual.getDeck().getMain(), nbCardsToSwap).stream() // - .map(entry -> entry.getKey()) // - .collect(Collectors.toUnmodifiableList()); + .map(Entry::getKey) // + .toList(); final Collection<PaperCard> fromSideboardToMain = Aggregates.random(individual.getDeck().getOrCreate(DeckSection.Sideboard), nbCardsToSwap).stream() // - .map(entry -> entry.getKey()) // - .collect(Collectors.toUnmodifiableList()); + .map(Entry::getKey) // + .toList(); for (final PaperCard card : fromMainToSideboard) { diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/SwapSideboardMutationEnabledCondition.java b/src/main/java/fr/kevincorvisier/mtg/gdb/operators/mutation/SwapSideboardMutationEnabledCondition.java @@ -0,0 +1,14 @@ +package fr.kevincorvisier.mtg.gdb.operators.mutation; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; + +public class SwapSideboardMutationEnabledCondition implements Condition +{ + @Override + public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) + { + return "SWAP_SIDEBOARD".equalsIgnoreCase(context.getEnvironment().getProperty("mutation.type")); + } +} diff --git a/src/main/packaged-resources/cfg/evaluation.properties b/src/main/packaged-resources/cfg/evaluation.properties @@ -5,23 +5,27 @@ # Goldfish evaluation # +evaluation.goldfish.enabled=false + # Set the minimum number of games to perform when first evaluating a deck against the opponents -# evaluation.goldfish.initial.min-games=100 +evaluation.goldfish.initial.min-games=100 # Set the minimum number of games to perform when evaluating a deck against the opponents after the first evaluation -# evaluation.goldfish.subsequent.min-games=10 +evaluation.goldfish.subsequent.min-games=10 # Game timeout: after this number of seconds/turns, the game is stopped as draw -# evaluation.goldfish.timeout.seconds=120 -# evaluation.goldfish.timeout.player-turns=40 +evaluation.goldfish.timeout.seconds=120 +evaluation.goldfish.timeout.player-turns=40 -# evaluation.goldfish.player.ai-profile=Reckless -# evaluation.goldfish.opponent.ai-profile=Default +evaluation.goldfish.player.ai-profile=Reckless +evaluation.goldfish.opponent.ai-profile=Default # # Win-ratio evaluation # +evaluation.win-ratio.enabled=true + # Set the minimum number of games to perform when first evaluating a deck against the opponents evaluation.win-ratio.initial.min-games=930