package io.vivarium.ga;
import java.util.ArrayList;
import java.util.Collections;
import org.javatuples.Pair;
import io.vivarium.core.Creature;
import io.vivarium.core.CreatureBlueprint;
import io.vivarium.core.GridWorldBlueprint;
import io.vivarium.core.processor.ProcessorBlueprint;
import io.vivarium.util.Rand;
/**
* A class for running a standard genetic algorithm across a population of creatures testing them with one or more
* {@code FitnessFunction}.
*/
public class GeneticAlgorithmRunner
{
CreatureBlueprint _creatureBlueprint;
int _currentGeneration = 1;
int _generations = 100;
int _populationSize = 100;
ArrayList<Pair<Double, Creature>> _population = new ArrayList<>(_populationSize);
private FitnessFunction _fitnessFunction;
public GeneticAlgorithmRunner(CreatureBlueprint creatureBlueprint, FitnessFunction fitnessFunction)
{
this._fitnessFunction = fitnessFunction;
this._creatureBlueprint = creatureBlueprint;
buildInitialPopulation();
}
private void buildInitialPopulation()
{
for (int i = 0; i < _populationSize; i++)
{
Creature c = new Creature(_creatureBlueprint);
// TODO: Re-add support for rendering processors
_population.add(new Pair<>(0.0, c));
}
}
private void run()
{
while (_currentGeneration <= _generations)
{
evaluatePopulation();
updatePopulation();
// Finally finish and increment the generation counters
_currentGeneration++;
}
}
private void evaluatePopulation()
{
for (int i = 0; i < _population.size(); i++)
{
evaluatePopulationMember(i);
}
}
private void evaluatePopulationMember(int i)
{
Pair<Double, Creature> populationMember = _population.get(i);
Creature c = populationMember.getValue1();
double fitness = _fitnessFunction.evaluate(c);
Pair<Double, Creature> updatedPopulationMember = new Pair<>(fitness, c);
_population.set(i, updatedPopulationMember);
}
private void updatePopulation()
{
Collections.sort(_population);
System.out.println("Gen... " + this._currentGeneration);
System.out.println("Worst member " + _population.get(0).getValue0());
System.out.println("Median member " + _population.get(_population.size() / 2).getValue0());
System.out.println("3rd Best member " + _population.get(_population.size() - 3).getValue0());
System.out.println("2nd Best member " + _population.get(_population.size() - 2).getValue0());
System.out.println("1st Best member " + _population.get(_population.size() - 1).getValue0());
ArrayList<Pair<Double, Creature>> newPopulation = new ArrayList<>(_populationSize);
double fitnessSum = 0;
for (int i = 0; i < _populationSize; i++)
{
fitnessSum += _population.get(i).getValue0();
}
System.out.println("Total fitness is " + fitnessSum);
for (int i = 0; i < _populationSize; i++)
{
int parent1Index = Math.max(Rand.getInstance().getRandomInt(_populationSize - i) + i,
Rand.getInstance().getRandomInt(_populationSize - i) + i);
int parent2Index = Math.max(Rand.getInstance().getRandomInt(_populationSize - i) + i,
Rand.getInstance().getRandomInt(_populationSize - i) + i);
Creature parent1 = _population.get(parent1Index).getValue1();
Creature parent2 = _population.get(parent2Index).getValue1();
Creature child = new Creature(parent1, parent2);
newPopulation.add(new Pair<>(0.0, child));
}
_population = newPopulation;
}
public static void main(String[] args)
{
CreatureBlueprint creatureBlueprint = CreatureBlueprint.makeDefault();
for (ProcessorBlueprint processorBlueprint : creatureBlueprint.getProcessorBlueprints())
{
processorBlueprint.setRandomInitializationProportion(1);
}
creatureBlueprint.setInitialGenerationProbability(0);
creatureBlueprint.setMaximumFood(200);
System.out.println("Creature Blueprint: " + creatureBlueprint);
GridWorldBlueprint worldBlueprint = GridWorldBlueprint.makeDefault();
worldBlueprint.setSize(30);
ArrayList<CreatureBlueprint> creatureBlueprints = new ArrayList<>();
worldBlueprint.setCreatureBlueprints(creatureBlueprints);
worldBlueprint.setInitialFoodGenerationProbability(0);
int tenLifespans = creatureBlueprint.getMaximumAge() * 10;
GeneticAlgorithmRunner runner = new GeneticAlgorithmRunner(creatureBlueprint,
new TimeToExtinctionFF(worldBlueprint, 100, tenLifespans));
runner.run();
}
}