Пример #1
0
    // Select a genome using tournament.
    protected virtual Genome TournamentSelect(List <Genome> population)
    {
        List <Genome> contestents = new List <Genome>();

        // First, randomize number of contestents.
        for (int i = 0; i < tourneyContestents; i++)
        {
            int randomIndex = UnityEngine.Random.Range(0, population.Count);
            contestents.Add(population[randomIndex]);
        }

        Genome highestFit = contestents[0];

        // Find highest fitness among contestents
        foreach (Genome g in contestents)
        {
            if (g.Fitness > highestFit.Fitness)
            {
                highestFit = g;
            }
        }

        // Create a copy of the best genome and return it.
        Genome copy = highestFit.GenomeCopy();

        copy.RootNode = StaticMethods.DeepCopy <N_Root>(highestFit.RootNode);

        return(copy);
    }
Пример #2
0
    // Assign fitness values according to their performance in the simulation step
    protected void Evaluate()
    {
        bestGenome = m_population[0];
        // Assign fitness value for each genome based on its statistics.
        foreach (Genome g in m_population)
        {
            g.Fitness = 100;
            // If the genome won the match and it wasn't a draw (assuming 0 damage given is a draw)
            // set fitness to 150.
            if (g.WonLastMatch && g.DamageGiven > 0)
            {
                g.Fitness += 150;
            }

            g.Fitness += g.HealthRemaining;
            g.Fitness += g.DamageGiven;

            // Make the floor of all fitness values 0 to make the roulette selection valid.
            if (g.Fitness < 0)
            {
                g.Fitness = 0;
            }

            // Find the genome with highest fitness, making sure that the simulations are always
            // compared to the currently best genome.
            if (g.Fitness > bestGenome.Fitness)
            {
                bestGenome.RootNode = StaticMethods.DeepCopy <N_Root>(g.RootNode);
                bestGenome.Fitness  = g.Fitness;
            }
        }
    }
Пример #3
0
    private Genome BinaryCrowdedTournamentSelect(List <Genome> genomes)
    {
        List <Genome> contestents = new List <Genome>();

        // First, randomize number of contestents.
        for (int i = 0; i < 2; i++)
        {
            int randomIndex = UnityEngine.Random.Range(0, genomes.Count);
            contestents.Add(genomes[randomIndex]);
        }

        Genome highestFit = contestents[0];

        // Find highest fitness among contestents
        foreach (Genome g in contestents)
        {
            if (g.Fitness < highestFit.Fitness) // Lower rank/fitness is better in this case.
            {
                highestFit = g;
            }
            else if (g.Fitness == highestFit.Fitness) // If they belong to the same front
            {
                // In that case, choose based on crowding distance instead.
                if (g.CrowdingDistance > highestFit.CrowdingDistance)
                {
                    highestFit = g;
                }
            }
        }

        // Create a copy of the best genome and return it.
        Genome copy = highestFit.GenomeCopy();

        copy.RootNode = StaticMethods.DeepCopy <N_Root>(highestFit.RootNode);

        return(copy);
    }
Пример #4
0
    // Combine parents and create two new children based on them
    protected void Combine(out Genome child0, out Genome child1, Genome parent0, Genome parent1)
    {
        Node subtree0, subtree1 = null;
        // First, check if there'll be any combination/crossover.
        float random = UnityEngine.Random.Range(0.0f, 1.0f);

        if (random < combinationRate)
        {
            List <Node> subtrees0 = TreeOperations.RetrieveSubtreeNodes(parent0.RootNode);
            List <Node> subtrees1 = TreeOperations.RetrieveSubtreeNodes(parent1.RootNode);

            // Select to random subtrees...
            int randomIndex = UnityEngine.Random.Range(0, subtrees0.Count);
            subtree0    = subtrees0[randomIndex];
            randomIndex = UnityEngine.Random.Range(0, subtrees1.Count);
            subtree1    = subtrees1[randomIndex];

            // Swap subtrees between 0 and 1.
            if (subtree0.Parent.GetType().IsSubclassOf(typeof(N_CompositionNode)))
            {
                N_CompositionNode comp0 = subtree0.Parent as N_CompositionNode;
                comp0.ReplaceChild(subtree0, StaticMethods.DeepCopy <Node>(subtree1));
            }

            // Swap subtrees between 1 and 0.
            if (subtree1.Parent.GetType().IsSubclassOf(typeof(N_CompositionNode)))
            {
                N_CompositionNode comp1 = subtree1.Parent as N_CompositionNode;
                comp1.ReplaceChild(subtree1, StaticMethods.DeepCopy <Node>(subtree0));
            }

            #region Old Combination
            //// Get initial comp of parent 0
            //N_CompositionNode comp0 = parent0.RootNode.Child as N_CompositionNode;
            //// Fetch a random subtree from the parent's subtrees
            //int randomIndex = UnityEngine.Random.Range(0, comp0.GetChildren().Count);
            //subtree0 = comp0.GetChildren()[randomIndex];

            //// Get initial comp of parent 1
            //N_CompositionNode comp1 = parent1.RootNode.Child as N_CompositionNode;
            //// Fetch a random subtree from the parent's subtrees
            //randomIndex = UnityEngine.Random.Range(0, comp1.GetChildren().Count);
            //subtree1 = comp1.GetChildren()[randomIndex];

            //// Swap subtrees between 0 and 1.
            //if (subtree0.Parent.GetType().IsSubclassOf(typeof(N_CompositionNode)))
            //{
            //    comp0.ReplaceChild(subtree0, StaticMethods.DeepCopy<Node>(subtree1));
            //}

            //// Swap subtrees between 1 and 0.
            //if (subtree1.Parent.GetType().IsSubclassOf(typeof(N_CompositionNode)))
            //{
            //    comp1.ReplaceChild(subtree1, StaticMethods.DeepCopy<Node>(subtree0));
            //}
            #endregion
        }

        // Regardless of combination, assign the children to be the parents. (combined or not)
        child0 = parent0;
        child1 = parent1;
    }
Пример #5
0
    // Select two candidates for next generation based on their fitness values
    protected void RouletteSelect(out Genome parent0, out Genome parent1)
    {
        Dictionary <Genome, float> genomeToWeight = new Dictionary <Genome, float>();

        parent0 = parent1 = null;

        // Sum up the total weight of all fitness values.
        float totalFitness = 0.0f;

        foreach (Genome g in m_population)
        {
            totalFitness += (float)g.Fitness;
        }

        // Calculate the relative fotness for each genome
        float fitnessSum = 0.0f;

        foreach (Genome g in m_population)
        {
            float weight = fitnessSum + ((float)g.Fitness / totalFitness);
            genomeToWeight.Add(g, weight);
            fitnessSum += (g.Fitness / totalFitness);
        }

        // Select two parents using semi-randomized roulette selection.
        for (int i = 0; i < 2; i++)
        {
            float  random       = UnityEngine.Random.Range(0.0f, 1.0f);
            Genome selectedTree = null;
            for (int j = 0; j < genomeToWeight.Count; j++)
            {
                var current = genomeToWeight.ElementAt(j);
                if (j == 0)
                {
                    if (random < current.Value)
                    {
                        selectedTree = current.Key;
                    }
                }
                else
                {
                    var prev = genomeToWeight.ElementAt(j - 1);
                    if (random < current.Value && random > prev.Value)
                    {
                        selectedTree = current.Key;
                    }
                }
            }
            if (selectedTree == null)
            {
                Debug.LogError("Selected tree is null...");
            }

            Genome parentG = selectedTree.GenomeCopy();

            // Assign parents during different iterations.
            switch (i) // Make sure to also copy conditions and subroots
            {
            case 0:
                parent0          = parentG;
                parent0.RootNode = StaticMethods.DeepCopy <N_Root>(selectedTree.RootNode);
                break;

            case 1:
                parent1          = parentG;
                parent1.RootNode = StaticMethods.DeepCopy <N_Root>(selectedTree.RootNode);
                break;

            default:
                parent0 = new Genome();
                parent1 = new Genome();
                Debug.LogError("No parent was set in roulette");
                break;
            }
        }

        if (parent0 == null || parent1 == null)
        {
            Debug.LogError("Parents weren't assigned during roulette selection!");
        }
    }
Пример #6
0
    // Update is called once per frame
    public override IEnumerator Evolve()
    {
        // Start by randomly generating the initial population.
        for (int i = 0; i < populationSize; i++)
        {
            m_population.Add(RandomGenome());
        }
        // Randomly generate the first best genome.
        bestGenome = RandomGenome();

        // First, process the initial population differently.
        feedbackText.SetText("Generation " + 0 + " out of " + generations + "...");
        Debug.Log("Starting simulation step of initial population...");
        // Start simulation and wait until done.
        simulationDone = false;
        StartCoroutine(Simulate(m_population));
        yield return(new WaitUntil(() => simulationDone));

        Debug.Log("Simulation step of initial population complete!");

        // Assign fitness to each genome according to nondomination principle.
        SortFitnessByNondomination(m_population);
        CalculateCrowdingDistance(m_population);

        // Initial "next population" processing.
        while (m_childPop.Count < m_population.Count)
        {
            Genome parent0, parent1;
            parent0 = BinaryCrowdedTournamentSelect(m_population);
            parent1 = BinaryCrowdedTournamentSelect(m_population);

            // Combine parents to retrieve two children
            Genome child0, child1;
            Combine(out child0, out child1, parent0, parent1);

            Mutate(child0.RootNode);
            Mutate(child1.RootNode);
            m_childPop.Add(child0);
            m_childPop.Add(child1);
        }

        //Debug.Log("Evolving rest of generations...");
        // Run general algorithm for remaining -th generations. (index 1 and forward
        for (int i = 1; i < generations; i++)
        {
            feedbackText.SetText("Generation " + i + " out of " + generations + "...");

            //Debug.Log("Starting simulation step of generation " + i);
            // Start simulation and wait until done.
            simulationDone = false;
            StartCoroutine(Simulate(m_childPop));
            yield return(new WaitUntil(() => simulationDone));

            //Debug.Log("Simulation step complete!");

            // First, create new generation as a combination of the last and the one before that.
            List <Genome> combinedGenomes = new List <Genome>();
            combinedGenomes.AddRange(m_population);
            combinedGenomes.AddRange(m_childPop);

            List <Genome> nextPopCandidates = new List <Genome>();
            // Sort according to nondomination and return list of fronts
            List <List <Genome> > fronts = GetFrontsByNondomination(combinedGenomes);
            int frontIndex = 0;

            // Create set of potential genome candidats
            while (nextPopCandidates.Count + fronts[frontIndex].Count <= populationSize)
            {
                // Calculate crowding distance for the front and add it to the population.
                CalculateCrowdingDistance(fronts[frontIndex]);
                nextPopCandidates.AddRange(fronts[frontIndex]);

                frontIndex++;
            }

            //Debug.Log(fronts[0].Count);
            // Retrieve the best genome from front 0, used for simulation
            Genome bestBack = BestFromFront(fronts[0]);
            bestGenome          = bestBack.GenomeCopy();
            bestGenome.RootNode = StaticMethods.DeepCopy <N_Root>(bestBack.RootNode);

            // Sort the front that didn't fit
            SortCrowdedComparison(fronts[frontIndex]);
            int fillingIndex = 0;
            // Complete the population by adding from the sorted front
            while (nextPopCandidates.Count < populationSize)
            {
                nextPopCandidates.Add(fronts[frontIndex][fillingIndex]);
                fillingIndex++;
            }

            // Push child back to pop and create new child using nextpop made from child and pop
            m_population = m_childPop;
            m_childPop   = new List <Genome>();

            // Finally, create the childpop for next generation.
            while (m_childPop.Count < populationSize)
            {
                Genome parent0, parent1;
                parent0 = BinaryCrowdedTournamentSelect(nextPopCandidates);
                parent1 = BinaryCrowdedTournamentSelect(nextPopCandidates);

                // Combine parents to retrieve two children
                Genome child0, child1;
                Combine(out child0, out child1, parent0, parent1);

                Mutate(child0.RootNode);
                Mutate(child1.RootNode);
                m_childPop.Add(child0);
                m_childPop.Add(child1);
            }
        }
        // Save the final best tree.
        FileSaver.GetInstance().SaveTree(bestGenome.RootNode, "multiEvolved");
        feedbackText.SetText("Multi evolution complete!");
        buttonCanvas.SetActive(true);

        yield return(null);
    }