commit 0fa17222c7a0370dc2ea87aea1db8d625735ea55
parent 62f5f0a3fdec531acab281180a79e9f0fa9add89
Author: Kevin Corvisier <git@kevincorvisier.fr>
Date: Thu, 19 Dec 2024 12:31:51 +0900
Make Crossover.crossover return a Pair instead of a List, move crossover rate to NextGenerationService
Diffstat:
6 files changed, 43 insertions(+), 42 deletions(-)
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/ApplicationConfig.java b/src/main/java/fr/kevincorvisier/mtg/gdb/ApplicationConfig.java
@@ -23,7 +23,6 @@ import fr.kevincorvisier.mtg.gdb.spring.converters.StringToFileConverter;
@Configuration
@PropertySource("application.properties")
-@PropertySource("crossover.properties")
@PropertySource("evaluation.properties")
@PropertySource("mutation.properties")
@PropertySource("population.properties")
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/operators/crossover/Crossover.java b/src/main/java/fr/kevincorvisier/mtg/gdb/operators/crossover/Crossover.java
@@ -1,12 +1,12 @@
package fr.kevincorvisier.mtg.gdb.operators.crossover;
-import java.util.List;
+import org.apache.commons.math3.util.Pair;
import fr.kevincorvisier.mtg.gdb.population.Individual;
public interface Crossover
{
- List<Individual> crossover(final Individual parent1, final Individual parent2);
+ Pair<Individual, Individual> crossover(final Individual parent1, final Individual parent2);
void setGenerationCount(final int generationCount);
}
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
@@ -3,7 +3,7 @@ package fr.kevincorvisier.mtg.gdb.operators.crossover;
import java.util.List;
import java.util.Random;
-import org.springframework.beans.factory.annotation.Value;
+import org.apache.commons.math3.util.Pair;
import org.springframework.stereotype.Service;
import forge.deck.CardPool;
@@ -24,47 +24,33 @@ public class UniformCrossover implements Crossover
protected int generationCount = 0;
private int individualIncrementCounter = 0;
- @Value("${crossover.no-crossover-chance}")
- private final double noCrossoverChance;
-
@Override
- public List<Individual> crossover(final Individual parent1, final Individual parent2)
+ public Pair<Individual, Individual> crossover(final Individual parent1, final Individual parent2)
{
- final Deck first;
- final Deck second;
+ final Deck first = new Deck();
+ final Deck second = new Deck();
- if (rnd.nextDouble() <= noCrossoverChance)
- {
- first = new Deck(parent1.getDeck());
- second = new Deck(parent2.getDeck());
- }
- else
+ for (final DeckSection section : SECTIONS)
{
- first = new Deck();
- second = new Deck();
+ final List<PaperCard> list1 = parent1.getDeck().getOrCreate(section).toFlatList();
+ final List<PaperCard> list2 = parent2.getDeck().getOrCreate(section).toFlatList();
+ final int size = Math.max(list1.size(), list2.size());
- for (final DeckSection section : SECTIONS)
- {
- final List<PaperCard> list1 = parent1.getDeck().getOrCreate(section).toFlatList();
- final List<PaperCard> list2 = parent2.getDeck().getOrCreate(section).toFlatList();
- final int size = Math.max(list1.size(), list2.size());
-
- final CardPool child1Section = first.getOrCreate(section);
- final CardPool child2Section = second.getOrCreate(section);
+ final CardPool child1Section = first.getOrCreate(section);
+ final CardPool child2Section = second.getOrCreate(section);
- for (int i = 0; i != size; i++)
- {
- final PaperCard card1 = i < list1.size() ? list1.get(i) : null;
- final PaperCard card2 = i < list2.size() ? list2.get(i) : null;
+ for (int i = 0; i != size; i++)
+ {
+ final PaperCard card1 = i < list1.size() ? list1.get(i) : null;
+ final PaperCard card2 = i < list2.size() ? list2.get(i) : null;
- final boolean firstCardToFirstChild = rnd.nextBoolean();
- child1Section.add(firstCardToFirstChild ? card1 : card2);
- child2Section.add(firstCardToFirstChild ? card2 : card1);
- }
+ final boolean firstCardToFirstChild = rnd.nextBoolean();
+ child1Section.add(firstCardToFirstChild ? card1 : card2);
+ child2Section.add(firstCardToFirstChild ? card2 : card1);
}
}
- return List.of(new Individual(uniqName(), first), new Individual(uniqName(), second));
+ return Pair.create(new Individual(uniqName(), first), new Individual(uniqName(), second));
}
private String uniqName()
diff --git a/src/main/java/fr/kevincorvisier/mtg/gdb/population/NextGenerationService.java b/src/main/java/fr/kevincorvisier/mtg/gdb/population/NextGenerationService.java
@@ -2,6 +2,7 @@ package fr.kevincorvisier.mtg.gdb.population;
import java.util.Collection;
import java.util.HashSet;
+import java.util.Random;
import org.apache.commons.math3.util.Pair;
import org.springframework.beans.factory.annotation.Value;
@@ -29,6 +30,8 @@ public class NextGenerationService
private final float newRate;
@Value("${population.maximum-size}")
private final int maximumSize;
+ @Value("${crossover.rate}")
+ private final double crossoverRate;
private final Crossover crossover;
private final Mutation mutation;
@@ -37,6 +40,8 @@ public class NextGenerationService
private final RandomDeckGenerator deckGenerator;
private final IndividualValidationService validationService;
+ private final Random rnd = new Random();
+
public Population toNextGeneration(final Population population)
{
final DefaultPopulation next = new DefaultPopulation();
@@ -79,20 +84,25 @@ public class NextGenerationService
while (next.getSize() < maximumSize)
{
// Selection
- final Pair<Individual, Individual> parents = selection.select(population);
- final double estimatedFitness = (parents.getFirst().getFitness() + parents.getSecond().getFitness()) / 2d;
+ Pair<Individual, Individual> pair = selection.select(population);
+ final double estimatedFitness = (pair.getFirst().getFitness() + pair.getSecond().getFitness()) / 2d;
// Crossover
- for (final Individual child : crossover.crossover(parents.getFirst(), parents.getSecond()))
+ if (rnd.nextDouble() < crossoverRate)
+ {
+ pair = crossover.crossover(pair.getFirst(), pair.getSecond());
+ }
+
+ for (final Individual individual : new Individual[] { pair.getFirst(), pair.getSecond() })
{
if (result.size() >= maximumSize)
continue; // Ensure second child will not breach the population limit
// Mutation
- mutation.mutate(child, estimatedFitness);
+ mutation.mutate(individual, estimatedFitness);
- if (validationService.validate(child, next))
- next.addIndividual(child);
+ if (validationService.validate(individual, next))
+ next.addIndividual(individual);
}
}
}
diff --git a/src/main/packaged-resources/cfg/application.properties b/src/main/packaged-resources/cfg/application.properties
@@ -10,3 +10,10 @@ cards.error-prone.file=error-prone-cards.txt
validation.child.conditions=
validation.child.max-unique-cards=24
+
+
+#
+# Crossover
+#
+
+crossover.rate=0.8
diff --git a/src/main/packaged-resources/cfg/crossover.properties b/src/main/packaged-resources/cfg/crossover.properties
@@ -1 +0,0 @@
-crossover.no-crossover-chance=0.333