private void SpawnCreatures(BitArray newGenome) { GameObject creaturesParent = new GameObject("creatures"); _parents.Add(creaturesParent); for (int i = 0; i < CreatureAmount; i++) { CreatureBehaviour creature = Instantiate(Resources.Load("Prefabs/creature") as GameObject).GetComponent <CreatureBehaviour>(); if (newGenome != null) { newGenome = GeneticAlgorithm.Mutation(newGenome); creature.Genome.SetGenome(newGenome); } else { creature.Genome.BaseMass = Random.Range(1f, 10f); creature.Genome.VisionLength = 2f; } Vector2 screenBounds = new Vector2(Width, Height); creature.transform.position = Camera.main.ScreenToWorldPoint(new Vector2(Random.Range(0f, screenBounds.x), Random.Range(0f, screenBounds.y))); creature.transform.Rotate(0, 0, Random.Range(0, 360)); creature.transform.SetParent(creaturesParent.transform, true); _creatures.Add(creature); } }
private static CreatureBehaviour ChooseWinner(CreatureBehaviour[] candidates) { float random = Random.value; CreatureBehaviour winner = null; candidates = candidates.OrderBy(x => x.AccumulatedNormalizedFitness).ToArray(); // Make sure the list is ordered right (high norm fitness = better score) //foreach (CreatureBehaviour candidate in candidates.Reverse()) // Wikipedia doesn't mention reversing the list, but if we don't then we will always pick the latest one in the list (with the highest accNormFitness) // if (candidate.AccumulatedNormalizedFitness >= random) // winner = candidate; foreach (CreatureBehaviour candidate in candidates) // Isn't this a better option? This way if random = 0.9, the closest one below 0.9 would be taken instead of the ones above 0.9 (higher accumulated fitness, the worse you are) { if (candidate.AccumulatedNormalizedFitness <= random) { winner = candidate; } } // If we have no winner, take the best one (fallback) // TODO: I don't know why we won't always take the best one? Is it to introduce more diversity? Is mutation not enough for this? if (!winner) { winner = candidates.First(); } Debug.Log($"Winner fitness: {winner.Fitness}, Best fitness: {candidates.Max(x => x.Fitness)}"); return(winner); }
public void OnCollisionEnter2D(Collision2D col) { CreatureBehaviour creature = col.collider.gameObject.GetComponent <CreatureBehaviour>(); if (creature != null) { creature.Die(); } }
public static CreatureBehaviour[] GetParents(List <CreatureBehaviour> candidates) { CreatureBehaviour parent1 = CalculateWinner(candidates); candidates.Remove(parent1); CreatureBehaviour parent2 = CalculateWinner(candidates); return(new CreatureBehaviour[] { parent1, parent2 }); }
// Selection // 1. Normalize each fitness (normFitness = fitness / sum(allFitnesses)), so that the sum of all fitnesses is 1 // 2. Sort population desc fitness // 3. Accumulate normalized fitnesses (accFitness = normFitness + allPreviousNormFitnesses) // ex: accFitness1 would be (normFitness1), accFitness2 would be (normFitness2 + normFitness1) // accFitness3 would be (normFitness3 + normFitness2 + normFitness1) ... // 4. Choose random number R between 0 and 1 // 5. Winner is the last one whose accFitness >= R // https://en.wikipedia.org/wiki/Selection_(genetic_algorithm) public static CreatureBehaviour CalculateWinner(List <CreatureBehaviour> candidates) { // Calculate total fitness of all candidates summed together float totalFitnesses = candidates.Select(x => x.Fitness).Sum(); // Normalize fitnesses candidates.ForEach(x => x.NormalizedFitness = NormalizeFitness(x.Fitness, totalFitnesses)); // Sort by descending fitness candidates = candidates.OrderByDescending(x => x.Fitness).ToList(); // Accumulate normalized fitnesses foreach (CreatureBehaviour candidate in candidates) { int index = candidates.IndexOf(candidate); float[] previousFitnesses = candidates.GetRange(0, index).Select(x => x.NormalizedFitness).ToArray(); candidate.AccumulatedNormalizedFitness = AccumulateNormalizedFitness(candidate.NormalizedFitness, previousFitnesses); } // Choose winner CreatureBehaviour winner = ChooseWinner(candidates.ToArray()); return(winner); }