Esempio n. 1
0
        /// <summary>
        /// Adds a new connection between 2 randomly chosen nodes.
        /// The first node can not be an output.
        /// The second node cannot be an input node if the first node is an input node, and the layer cannot be the same as the first node.
        ///
        /// If the connection that is going to be created already exists, there will be a maximum of 5 attempts to create a new one.
        /// </summary>
        private void AddConnectionMutation()
        {
            int attemptsDone = 0;

            while (true)
            {
                // NOTE: Rebuild structure so we get layer information to make sure we don't get circle dependencies
                RebuildStructure();

                Node startNode;
                Node endNode;
                do
                {
                    startNode = _nodes.ElementAt(TrainingRoom.Random.Next(_nodes.Count)).Value;
                } while (startNode.NodeType == NodeType.Output);

                do
                {
                    endNode = _nodes.ElementAt(TrainingRoom.Random.Next(_nodes.Count)).Value;
                } while (endNode.Layer == startNode.Layer || (startNode.NodeType == NodeType.Input && endNode.NodeType == NodeType.Input));

                if (endNode.Layer > startNode.Layer)
                {
                    Node temp = endNode;
                    endNode   = startNode;
                    startNode = temp;
                }

                if (ConnectionExists(startNode, endNode))
                {
                    if (attemptsDone >= 5)
                    {
                        return;
                    }
                    attemptsDone += 1;
                    continue;
                }

                ConnectionGene connection = new ConnectionGene(Id, startNode.Id, endNode.Id, TrainingRoom.Random.NextDouble() * 2 - 1, TrainingRoom.GetInnovationNumber(startNode.Id, endNode.Id));
                ConnectionGenes.Add(connection);
                _maxInnovation = Math.Max(connection.InnovationNumber, _maxInnovation);
                break;
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Checks if the other brain is the same species as this brain.
        /// </summary>
        /// <param name="other">The other brain.</param>
        /// <returns>Returns <c>true</c> if the other brain is the same species as this brain; otherwise, <c>false</c>.</returns>
        public bool IsSameSpecies(Brain other)
        {
            // TODO: Redo this method and optimize it
            int excess = 0;

            if (other.ConnectionGenes.Any())
            {
                excess += other.ConnectionGenes.Count(gene => gene.InnovationNumber > _maxInnovation);
            }

            double weightDiff      = 0;
            int    disjoint        = 0;
            int    weightDiffCount = 0;

            foreach (ConnectionGene gene in ConnectionGenes)
            {
                ConnectionGene otherGene = other.ConnectionGenes.SingleOrDefault(gen => gen.InnovationNumber == gene.InnovationNumber);
                if (otherGene != default)
                {
                    weightDiff += Math.Abs(gene.Weight - otherGene.Weight);
                    weightDiffCount++;
                }
                else
                {
                    disjoint++;
                }
            }

            weightDiff = weightDiffCount == 0 ? 0 : weightDiff / weightDiffCount;

            int genomeCount = Math.Max(ConnectionGenes.Count, other.ConnectionGenes.Count);

            if (genomeCount < 20)
            {
                genomeCount = 1;
            }

            return(((TrainingRoom.TrainingRoomSettings.SpeciesExcessGeneWeight * excess) / genomeCount +
                    (TrainingRoom.TrainingRoomSettings.SpeciesDisjointGeneWeight * disjoint) / genomeCount +
                    TrainingRoom.TrainingRoomSettings.SpeciesAverageWeightDiffWeight * weightDiff) <
                   TrainingRoom.TrainingRoomSettings.Threshold);
        }
Esempio n. 3
0
        /// <summary>
        /// Adds a new connection between 2 randomly chosen nodes.
        /// The first node can not be an output.
        /// The second node cannot be an input node if the first node is an input node, and the layer cannot be the same as the first node.
        ///
        /// If the connection that is going to be created already exists, there will be a maximum of 5 attempts to create a new one.
        /// </summary>
        /// <param name="trainingRoomSettings">The training room settings.</param>
        /// <param name="innovationFunction">The innovation function.</param>
        private void AddConnectionMutation(TrainingRoomSettings trainingRoomSettings, Func <uint, uint, uint> innovationFunction)
        {
            // Set initial attempt counter to 0.
            int attemptsDone = 0;

            // Rebuild structure so we get layer information to make sure we don't get circular dependencies.
            RebuildStructure();

            while (true)
            {
                // Prepare start and end node.
                Node startNode;
                Node endNode;

                // Set start node to a random node while start note is of type OutputNode.
                do
                {
                    startNode = GetRandomNode();
                } while (startNode is OutputNode);


                // Set end node to a random node while the layer of the start and end node are the same or
                // start node is of type InputNode and end node is of type InputNode.
                do
                {
                    endNode = GetRandomNode();
                } while (endNode.Layer == startNode.Layer || (startNode is InputNode && endNode is InputNode));

                // If end node layer is higher than start node layer swap them.
                if (endNode.Layer > startNode.Layer)
                {
                    Node temp = endNode;
                    endNode   = startNode;
                    startNode = temp;
                }

                // If the connection between the selected start and end node already exists continue the loop, until 5 attempts are done,
                // then return early.
                if (ConnectionExists(startNode, endNode))
                {
                    if (attemptsDone >= 5)
                    {
                        return;
                    }
                    attemptsDone += 1;
                    continue;
                }

                // Create a new connection gene from the start and end nodes.
                ConnectionGene connection = new ConnectionGene(Id, innovationFunction(startNode.NodeIdentifier, endNode.NodeIdentifier), startNode.NodeIdentifier, endNode.NodeIdentifier, trainingRoomSettings.Random.NextDouble() * 2 - 1);

                // Add the connection gene.
                ConnectionGenes.Add(connection);

                // Update the max innovation.
                _maxInnovation = Math.Max(connection.InnovationNumber, _maxInnovation);

                // Break the loop.
                break;
            }

            Node GetRandomNode()
            {
                //TODO: Why not use the hidden nodes list instead of the connection genes?

                List <Node> nodes = ConnectionGenes.SelectMany(p => new[] { p.InNode, p.OutNode }).Distinct().ToList();

                nodes.AddRange(Inputs.Select(p => p.InputNode));
                nodes.AddRange(Outputs.Select(p => p.OutputNode));

                return(nodes[trainingRoomSettings.Random.Next(nodes.Count)]);
            }
        }