private void NextGeneration() { if (population.Count >= 2) { //Go through and populate most of our next generation with new children while (nextPopulation.Count() < (populationSize - 2)) { RCT2RideData parent1 = SelectCandidate(); RCT2RideData parent2; //Make sure the parents aren't the same do { parent2 = SelectCandidate(); } while (parent2 == parent1); //Crossover to create children List <RCT2RideData> children = Crossover(parent1, parent2); //Mutate the children children[0] = Mutation(children[0]); children[1] = Mutation(children[1]); //Add them to the next population nextPopulation.AddRange(children); } //Now add the best from this population as a form of Elitism RCT2RideData elitism1 = population[0]; int elitism1Fitness = CalculateFitness(elitism1); RCT2RideData elitism2 = population[1]; int elitism2Fitness = CalculateFitness(elitism2); for (int i = 0; i < population.Count; i++) { int curFitness = CalculateFitness(population[i]); if (curFitness > elitism1Fitness) { elitism1 = population[i]; elitism1Fitness = curFitness; } else if (curFitness > elitism2Fitness) { elitism2 = population[i]; elitism2Fitness = curFitness; } } nextPopulation.Add(elitism1); nextPopulation.Add(elitism2); } else { Console.WriteLine("ERROR: Population Size must be at least 2"); } }
private int CalculateFitness(RCT2RideData candidate) { int fitness = 0; //Get Displacement to End & Max Y Displacement Vector3 prevWorldPos = new Vector3(0.0f, 0.0f, 0.0f); int worldDirectionChange = 0; double maxVarianceFromStart = 0; int maxYVariance = 0; for (int i = 0; i < candidate.TrackData.TrackData.Count; i++) { RCT2TrackElements.RCT2TrackElement currentElement = candidate.TrackData.TrackData[i].TrackElement; RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[currentElement]; Vector3 worldDisplacement = candidate.TrackData.LocalDisplacementToWorld(property.Displacement, worldDirectionChange); //Update World Position Changes prevWorldPos += worldDisplacement; if (prevWorldPos.Y >= maxYVariance) { maxYVariance = (int)prevWorldPos.Y; } if (prevWorldPos.Length() >= maxVarianceFromStart) { maxVarianceFromStart = prevWorldPos.Length(); } //Update World Direction Changes worldDirectionChange = candidate.TrackData.UpdateRotation(worldDirectionChange, property.DirectionChange); } maxYVariance -= (int)prevWorldPos.Y; maxVarianceFromStart -= prevWorldPos.Length(); Vector3 displacementToEnd = new Vector3(0, 0, 0); displacementToEnd.X = Math.Abs(prevWorldPos.X); displacementToEnd.Y = Math.Abs(prevWorldPos.Y); displacementToEnd.Z = Math.Abs(prevWorldPos.Z); //Console.WriteLine(maxYDisplacement); double displacementToEndLength = displacementToEnd.Length(); fitness = (int)(((candidate.ExcitementTimesTen / (1 + candidate.NauseaTimesTen)) + /*maxVarianceFromStart*/ +maxYVariance) * 1000); return(fitness); }
public void PerformAlgorithm() { InitialiseParameters(); Logger.Log("=================="); Logger.Log("Initial Population"); Logger.Log("=================="); InitialPopulationGeneration(); Logger.Log("=================="); Logger.Log("Generation Start"); Logger.Log("=================="); for (int i = 0; i < generationCount; i++) { Logger.Log($"Generation Number {i}"); //Generate the next generation NextGeneration(); //Swap over the populations population.Clear(); population.AddRange(nextPopulation); nextPopulation.Clear(); //Find the best fitness from this new generation and display it //Now add the best from this population as a form of Elitism RCT2RideData bestCoaster = population[0]; int bestFitness = CalculateFitness(bestCoaster); int totalFitness = 0; for (int j = 0; j < population.Count; j++) { int curFitness = CalculateFitness(population[j]); totalFitness += curFitness; if (curFitness > bestFitness) { bestCoaster = population[j]; bestFitness = curFitness; } } Logger.Log($"\tSuccessful Mutations: {successfulMutations}"); Logger.Log($"\tSuccessful Crossovers: {successfulCrossovers}"); Logger.Log($"\tBest Fitness: {bestFitness}"); Logger.Log($"\tAverage Fitness: {totalFitness / populationSize}"); totalSuccessfulMutations += successfulMutations; successfulMutations = 0; totalSuccessfulCrossovers += successfulCrossovers; successfulCrossovers = 0; } Logger.Log("=================="); Logger.Log("Evolution Complete"); Logger.Log("=================="); //Find the best one of the final generation RCT2RideData bestCoasterMyDude = population[0]; int bestFitnessMyDude = CalculateFitness(bestCoasterMyDude); for (int i = 0; i < populationSize; i++) { int currentFitness = CalculateFitness(population[i]); if (currentFitness > bestFitnessMyDude) { bestCoasterMyDude = population[i]; bestFitnessMyDude = currentFitness; } } Logger.Log("=================="); Logger.Log($"Best Member - Fitness: {bestFitnessMyDude}"); Logger.Log("=================="); //Print the best candidate for (int i = 0; i < bestCoasterMyDude.TrackData.TrackData.Count; i++) { Logger.Log(bestCoasterMyDude.TrackData.TrackData[i].TrackElement.ToString()); } }
private RCT2RideData Mutation(RCT2RideData candidate) { //Console.WriteLine("------------"); //foreach (var track in candidate.TrackData.TrackData) //{ // Console.WriteLine(track.TrackElement.ToString()); //} bool hasExtraPiece = false; for (int i = 2; i < candidate.TrackData.TrackData.Count(); i++) { if (random.NextDouble() <= mutationRate) { //Get possible candidates RCT2RideData candidateCopy = new RCT2RideData(candidate); List <RCT2TrackElements.RCT2TrackElement> candidateReplacements = RCT2TrackElements.FindValidSuccessors(whitelistedTracks, candidate.TrackData.TrackData[i - 1].TrackElement); bool redo = false; do { //If we're out of possible replacements if (candidateReplacements.Count <= 0) { //Console.WriteLine("/////////////"); //foreach (var track in candidate.TrackData.TrackData) //{ // Console.WriteLine(track.TrackElement.ToString()); //} return(candidate); } //Construct our random element RCT2TrackPiece randomElement = new RCT2TrackPiece(); randomElement.TrackElement = candidateReplacements[random.Next(candidateReplacements.Count)]; candidateReplacements.Remove(randomElement.TrackElement); RCT2TrackElementProperty property = RCT2TrackElements.TrackElementPropertyMap[randomElement.TrackElement]; if (property.InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up25 || property.InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up60 || property.InputTrackDegree == RCT2TrackElementProperty.RCT2TrackDegree.Up90) { randomElement.Qualifier = new RCT2Qualifier() { IsChainLift = true, TrackColourSchemeNumber = 0, TrackRotation = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero, AtTerminalStation = false, StationNumber = 0 }; } else { randomElement.Qualifier = new RCT2Qualifier() { IsChainLift = false, TrackColourSchemeNumber = 0, TrackRotation = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero, AtTerminalStation = false, StationNumber = 0 }; } //Replace the existing element at that location with this if (hasExtraPiece) { candidateCopy.TrackData.TrackData.RemoveAt(i + 1); hasExtraPiece = false; } candidateCopy.TrackData.TrackData.RemoveAt(i); if (randomElement.TrackElement == RCT2TrackElements.RCT2TrackElement.FlatToIncline25 || randomElement.TrackElement == RCT2TrackElements.RCT2TrackElement.Incline25) { candidateCopy.TrackData.TrackData.Insert(i, randomElement); RCT2TrackPiece bridgeElement = new RCT2TrackPiece(); bridgeElement.TrackElement = RCT2TrackElements.RCT2TrackElement.Incline25ToFlat; bridgeElement.Qualifier = new RCT2Qualifier() { IsChainLift = true, TrackColourSchemeNumber = 0, TrackRotation = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero, AtTerminalStation = false, StationNumber = 0 }; candidateCopy.TrackData.TrackData.Insert(i + 1, bridgeElement); //Console.WriteLine($"\tAttempted to Mutate into {randomElement.TrackElement.ToString()}"); hasExtraPiece = true; } else if (randomElement.TrackElement == RCT2TrackElements.RCT2TrackElement.FlatToDecline25 || randomElement.TrackElement == RCT2TrackElements.RCT2TrackElement.Decline25) { candidateCopy.TrackData.TrackData.Insert(i, randomElement); RCT2TrackPiece bridgeElement = new RCT2TrackPiece(); bridgeElement.TrackElement = RCT2TrackElements.RCT2TrackElement.Decline25ToFlat; bridgeElement.Qualifier = new RCT2Qualifier() { IsChainLift = false, TrackColourSchemeNumber = 0, TrackRotation = RideData.RCT2Qualifier.RCT2QualifierRotation.Zero, AtTerminalStation = false, StationNumber = 0 }; candidateCopy.TrackData.TrackData.Insert(i + 1, bridgeElement); //Console.WriteLine($"\tAttempted to Mutate into {randomElement.TrackElement.ToString()}"); hasExtraPiece = true; } else { candidateCopy.TrackData.TrackData.Insert(i, randomElement); } //If it makes the track invalid, try again if (candidateCopy.TrackData.CheckValidity() != RCT2TrackData.InvalidityCode.Valid) { redo = true; } else { redo = false; successfulMutations++; hasExtraPiece = false; } } while (redo); } } return(candidate); }
private List <RCT2RideData> Crossover(RCT2RideData parent1, RCT2RideData parent2) { List <RCT2RideData> children = new List <RCT2RideData>(); int crossoverPoint = length; int redoCount = 0; bool redo = false; RCT2TrackData.InvalidityCode parent1Invalidity = parent1.TrackData.CheckValidity(); RCT2TrackData.InvalidityCode parent2Invalidity = parent2.TrackData.CheckValidity(); //Create crossover point if (random.NextDouble() <= crossoverRate) { crossoverPoint = random.Next(length); } do { if (redoCount >= crossoverAttempts) { crossoverPoint = length; } //Add first halves to each child RCT2TrackData child1Track = new RCT2TrackData(); RCT2TrackData child2Track = new RCT2TrackData(); for (int i = 0; i < crossoverPoint; i++) { child1Track.TrackData.Add(parent1.TrackData.TrackData[i]); child2Track.TrackData.Add(parent2.TrackData.TrackData[i]); } //Add second halves to each child for (int i = crossoverPoint; i < parent2.TrackData.TrackData.Count(); i++) { child1Track.TrackData.Add(parent2.TrackData.TrackData[i]); } for (int i = crossoverPoint; i < parent1.TrackData.TrackData.Count(); i++) { child2Track.TrackData.Add(parent1.TrackData.TrackData[i]); } RCT2RideData child1 = new RCT2RideData(parent1); RCT2RideData child2 = new RCT2RideData(parent2); child1.TrackData = child1Track; child2.TrackData = child2Track; //If the created children are invalid, keep trying //Wont cause an infinite loop as we will eventually crossover at point 0 //Which acts as if we never crossed over at all RCT2TrackData.InvalidityCode child1Invalidity = child1.TrackData.CheckValidity(); RCT2TrackData.InvalidityCode child2Invalidity = child2.TrackData.CheckValidity(); if (child1Invalidity != RCT2TrackData.InvalidityCode.Valid || child2Invalidity != RCT2TrackData.InvalidityCode.Valid) { redo = true; redoCount++; crossoverPoint = random.Next(length); } else { redo = false; children.Add(child1); children.Add(child2); if (crossoverPoint != length) { successfulCrossovers++; } } } while (redo); return(children); }