commit 55dbaeb796d71170bc7bbbc8bc8da84d829626d0
parent dbd1a610fcfc46a891f6a6e9aef61ca791ff28e4
Author: Kevin Corvisier <git@kevincorvisier.fr>
Date: Fri, 20 Dec 2024 18:53:52 +0900
Win evaluation: add possibility to have a different number of games
performed depending on the current win-ratio of an individual
Diffstat:
7 files changed, 99 insertions(+), 21 deletions(-)
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/ApplicationConfig.java b/src/main/java/fr/kevincorvisier/mtg/gdb/ApplicationConfig.java
@@ -20,6 +20,7 @@ import forge.game.GameFormat.FormatType;
import forge.model.FModel;
import fr.kevincorvisier.mtg.gdb.spring.converters.StringToCardListFileConverter;
import fr.kevincorvisier.mtg.gdb.spring.converters.StringToFileConverter;
+import fr.kevincorvisier.mtg.gdb.spring.converters.StringToWinRatioContinueConditionConverter;
@Configuration
@PropertySource("application.properties")
@@ -73,6 +74,7 @@ public class ApplicationConfig
final DefaultConversionService conversionService = new DefaultConversionService();
conversionService.addConverter(new StringToCardListFileConverter(conversionService));
conversionService.addConverter(new StringToFileConverter());
+ conversionService.addConverter(new StringToWinRatioContinueConditionConverter());
return conversionService;
}
}
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioContinueCondition.java b/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioContinueCondition.java
@@ -0,0 +1,23 @@
+package fr.kevincorvisier.mtg.gdb.evaluation;
+
+import javax.validation.constraints.NotNull;
+
+import lombok.Data;
+import lombok.NonNull;
+
+@Data
+public class WinRatioContinueCondition
+{
+ private final int minPlayedGames;
+ private final double requiredWinRate;
+
+ public boolean accept(@NonNull @NotNull final WinRatioEvaluationContext context)
+ {
+ return context.getGamesPlayed() >= minPlayedGames;
+ }
+
+ public boolean canContinue(@NonNull @NotNull final WinRatioEvaluationContext context)
+ {
+ return context.getWinRatio() >= requiredWinRate;
+ }
+}
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioEvaluation.java b/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioEvaluation.java
@@ -6,6 +6,8 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import javax.validation.constraints.NotNull;
+
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Service;
@@ -48,6 +50,8 @@ public class WinRatioEvaluation implements Evaluation
private final int gameTimeoutTurns;
@Value("${evaluation.win-ratio.timeout.seconds}")
private final int gameTimeoutSeconds;
+ @Value("${evaluation.win-ratio.continue-conditions}")
+ private final Collection<WinRatioContinueCondition> gameThreshold;
@Override
public void reload()
@@ -84,29 +88,50 @@ public class WinRatioEvaluation implements Evaluation
individual.setWinRatioContext(context);
}
- final int minGames = context.getGamePlayed().intValue() == 0 ? initialMinGames : subsequentMinGames;
- final int gamesPerMatch = (int) Math.ceil((double) (minGames) / (double) (opponents.size()));
+ final int minGames = context.getGamesPlayed() == 0 ? initialMinGames : subsequentMinGames;
+ final int gamesPerOpponent = (int) Math.ceil((double) (minGames) / (double) (opponents.size()));
final GameRules rules = new GameRules(GameType.Constructed);
- rules.setGamesPerMatch(gamesPerMatch);
+ rules.setGamesPerMatch(1);
+
+ log.debug("individual={}, minGames={}, opponents={}, gamesPerMatch={}", individual.getName(), minGames, opponents.size(), gamesPerOpponent);
+
+ for (int i = 0; i != gamesPerOpponent; i++)
+ {
+ if (skipEvaluation(individual, context))
+ return;
- log.debug("individual={}, minGames={}, opponents={}, gamesPerMatch={}", individual.getName(), minGames, opponents.size(), gamesPerMatch);
+ opponents.parallelStream().forEach(opponentDeck -> {
+ final RegisteredPlayer player = new RegisteredPlayer(individual.getDeck())
+ .setPlayer(GamePlayerUtil.createAiPlayer(individual.getName(), playerAiProfile));
+ final RegisteredPlayer opponent = new RegisteredPlayer(opponentDeck)
+ .setPlayer(GamePlayerUtil.createAiPlayer(opponentDeck.getName(), opponentAiProfile));
- opponents.parallelStream().forEach(opponentDeck -> {
- final RegisteredPlayer player = new RegisteredPlayer(individual.getDeck())
- .setPlayer(GamePlayerUtil.createAiPlayer(individual.getName(), playerAiProfile));
- final RegisteredPlayer opponent = new RegisteredPlayer(opponentDeck)
- .setPlayer(GamePlayerUtil.createAiPlayer(opponentDeck.getName(), opponentAiProfile));
+ final Match mc = new Match(rules, Arrays.asList(player, opponent), "Test");
- final Match mc = new Match(rules, Arrays.asList(player, opponent), "Test");
+ final GameSimulator simulator = new GameSimulator(gameTimeoutTurns, gameTimeoutSeconds, errorProneCards);
- final GameSimulator simulator = new GameSimulator(gameTimeoutTurns, gameTimeoutSeconds, errorProneCards);
+ do
+ {
+ final GameSimulationResult result = simulator.simulateGame(player, mc);
+ individual.getWinRatioContext().onGameOver(result);
+ } while (mc.getOutcomes().size() < mc.getRules().getGamesPerMatch());
+ });
+ }
+ }
- do
+ private boolean skipEvaluation(@NotNull final Individual individual, @NotNull final WinRatioEvaluationContext context)
+ {
+ for (final WinRatioContinueCondition condition : gameThreshold)
+ {
+ if (condition.accept(context) && !condition.canContinue(context))
{
- final GameSimulationResult result = simulator.simulateGame(player, mc);
- individual.getWinRatioContext().onGameOver(result);
- } while (mc.getOutcomes().size() < mc.getRules().getGamesPerMatch());
- });
+ log.info("Skip evaluation: of {}: not machting requirement of condition {}, context={}", individual.getName(), condition, context);
+ return true;
+ }
+ }
+
+ return false;
}
+
}
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioEvaluationContext.java b/src/main/java/fr/kevincorvisier/mtg/gdb/evaluation/WinRatioEvaluationContext.java
@@ -3,9 +3,7 @@ package fr.kevincorvisier.mtg.gdb.evaluation;
import java.util.concurrent.atomic.AtomicInteger;
import fr.kevincorvisier.mtg.gdb.evaluation.GameSimulationResult.GameSimulationResultValue;
-import lombok.Data;
-@Data
public class WinRatioEvaluationContext
{
private final AtomicInteger gamePlayed = new AtomicInteger();
@@ -27,10 +25,15 @@ public class WinRatioEvaluationContext
return gamePlayed.intValue() != 0;
}
+ public int getGamesPlayed()
+ {
+ return gamePlayed.get();
+ }
+
/**
* Win ratio
*/
- public double getFitness()
+ public double getWinRatio()
{
return gameWon.doubleValue() / gamePlayed.doubleValue();
}
@@ -38,6 +41,6 @@ public class WinRatioEvaluationContext
@Override
public String toString()
{
- return getFitness() + ": " + gameWon.doubleValue() + "/" + gamePlayed.doubleValue();
+ return getWinRatio() + ": " + gameWon.doubleValue() + "/" + gamePlayed.doubleValue();
}
}
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/population/Individual.java b/src/main/java/fr/kevincorvisier/mtg/gdb/population/Individual.java
@@ -45,7 +45,7 @@ public class Individual
final Collection<Double> fitnesses = new ArrayList<>();
if (winRatioContext != null && winRatioContext.hasData())
- fitnesses.add(winRatioContext.getFitness());
+ fitnesses.add(winRatioContext.getWinRatio());
if (goldfishContext != null && goldfishContext.hasData())
fitnesses.add(goldfishContext.getFitness());
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/spring/converters/StringToWinRatioContinueConditionConverter.java b/src/main/java/fr/kevincorvisier/mtg/gdb/spring/converters/StringToWinRatioContinueConditionConverter.java
@@ -0,0 +1,20 @@
+package fr.kevincorvisier.mtg.gdb.spring.converters;
+
+import org.springframework.core.convert.converter.Converter;
+
+import fr.kevincorvisier.mtg.gdb.evaluation.WinRatioContinueCondition;
+
+public class StringToWinRatioContinueConditionConverter implements Converter<String, WinRatioContinueCondition>
+{
+ @Override
+ public WinRatioContinueCondition convert(final String source)
+ {
+ final String[] fields = source.split("\\|");
+ if (fields.length != 2)
+ throw new IllegalArgumentException(source);
+
+ final int minPlayedGames = Integer.parseInt(fields[0]);
+ final double requiredWinRate = Double.parseDouble(fields[1]);
+ return new WinRatioContinueCondition(minPlayedGames, requiredWinRate);
+ }
+}
diff --git a/src/main/packaged-resources/cfg/evaluation.properties b/src/main/packaged-resources/cfg/evaluation.properties
@@ -52,3 +52,8 @@ evaluation.win-ratio.opponents-directories=example1/ms-opponents,example1/pm-opp
evaluation.win-ratio.player.ai-profile=Default
evaluation.win-ratio.opponent.ai-profile=Default
+
+# When a individual has played at least X games, continue evaluation only if the win ratio is at least Y
+# format: X|Y,X|Y
+evaluation.win-ratio.continue-conditions=10|0.2,20|0.4,30|0.6,40|0.8
+