示例#1
0
        public void Mutate()
        {
            // When no genes, add gene mutation
            if (Connections.Count == 0)
            {
                AddConnection();
            }

            double chance = MyRand.UniformRnd(0, 1);

            // 80% chance to modify weight or bias
            if (chance < 0.8)
            {
                ModifyConnection();
            }

            chance = MyRand.UniformRnd(0, 1);
            // 8% chance to add new connection
            if (chance < 0.08)
            {
                AddConnection();
            }

            chance = MyRand.UniformRnd(0, 1);
            // 2% chance to add new node
            if (chance < 0.02)
            {
                AddNode();
            }

            // No mutations today
        }
示例#2
0
        // Add new connection to the network
        public Result AddConnection()
        {
            // Select a random node which will have an extra connections
            // Do not select nodes in the output layer

            // Check if network is fully connected
            if (IsNetworkFullyConnected())
            {
                return(Result.fail);
            }

            Node fromNode = null;

            do
            {
                // Select a random node
                int rndNodeNumb = MyRand.Next(0, Nodes.Count);
                fromNode = Nodes[rndNodeNumb];

                //Find new node if this node is fully connected, OR in the output layer
            } while(IsNodeInOutputLayer(fromNode) ||
                    IsNodeFullyConnected(fromNode)
                    );

            // Find a node to connect to
            Node toNode = null;

            do
            {
                // Select a random node
                int rndNodeNumb = MyRand.Next(0, Nodes.Count);
                toNode = Nodes[rndNodeNumb];

                // Find new node if this node is in the same layer, OR both already connected
            } while((toNode.Layer == fromNode.Layer) ||
                    (IsConnected(fromNode, toNode))
                    );

            //If the tonode is in a lower layer than the fromnode, change positon
            if (toNode.Layer < fromNode.Layer)
            {
                Node tmp = toNode;
                toNode   = fromNode;
                fromNode = tmp;
            }

            // Add connections between the two nodes with random weight
            Connection newConn = new Connection(fromNode);

            newConn.ONode = toNode;
            fromNode.Connections.Add(newConn);
            Connections.Add(newConn);

            // Assign innovation ID for new connection
            InnovationHandler.GetInnovationID(newConn);

            return(Result.success);
        }
 /// <summary>
 /// Random uniform weight, and enabled
 /// </summary>
 /// <param name="node">The input node</param>
 public Connection(Node node)
 {
     INode   = node;
     Weight  = MyRand.UniformRnd(-1, 1);
     Bias    = MyRand.UniformRnd(-1, 1);
     Enabled = true;
     ONode   = null; // Output nodes are not necessary defined immediately
     InnovID = 0;    // It will be overwritten
 }
示例#4
0
        /// <summary>
        /// Adds a new player to the niche by crossover or cloning, could return null!
        /// </summary>
        public Player GetBaby()
        {
            // Need at least one player in this species
            if (Members.Count < 1)
            {
                return(null);
            }

            Player newPlayer = null;

            double chance = MyRand.UniformRnd(0, 1);

            // TODO opt. In this case it is unnecessary to decide which species should this belong
            // with 25% chance just clone a player || When we only have one member
            if (chance < 0.25 || Members.Count == 1)
            {
                int rndPlayer = MyRand.Next(0, Members.Count);
                newPlayer = Members[rndPlayer].DeepCopy();
                newPlayer.SetFitness(0);
                newPlayer.LifeSpawn = 0;
            }
            // Crossover 75% chance
            else
            {
                // We cant do crossover if there are less than 2 players
                if (Members.Count < 2)
                {
                    return(null);
                }

                Player parentA = null;
                Player parentB = null;
                do
                {
                    int rndPlayerNumb = MyRand.Next(0, Members.Count);
                    parentA       = Members[rndPlayerNumb];
                    rndPlayerNumb = MyRand.Next(0, Members.Count);
                    parentB       = Members[rndPlayerNumb];
                } while(parentA.ID == parentB.ID);

                if (parentA.Fitness > parentB.Fitness)
                {
                    newPlayer = parentA.Crossover(parentB);
                }
                else
                {
                    newPlayer = parentB.Crossover(parentA);
                }
            }

            return(newPlayer);
        }
示例#5
0
        // Toggle connection
        public Result ToggleConnection()
        {
            // Check if we have connections or not
            if (Connections.Count == 0)
            {
                //Cannot Add new node
                return(Result.fail);
            }

            int        rndConNumb = MyRand.Next(0, Connections.Count);
            Connection conn       = Connections[rndConNumb];

            conn.Enabled = !conn.Enabled;

            return(Result.success);
        }
示例#6
0
        // Crossover this and NetworkB where this is the better parent
        public Network Crossover(Network networkB)
        {
            // Create a child network, which is very similar to this network
            Network child = new Network(InputNodes,
                                        OutputNodes,
                                        OutputLayer,
                                        NodeCnt);

            // At first we should Add the nodes, but how to choose which nodes to add?
            // Chose the better parent which is this
            foreach (var nodeA in Nodes)
            {
                Node childN = nodeA.DeepCopy(nodeA.ID);
                // we will add the connections later
                childN.Connections.Clear();
                child.Nodes.Add(childN);
            }

            //Go through the genes, and decide how to use them
            foreach (var conA in Connections)
            {
                bool       enableGene = true;
                Connection gene       = null;

                //Check if both parents have this gene
                Connection conB = networkB.FindConnection(conA.InnovID);
                if (conB != null)
                {
                    //Both have this gene

                    //Check if it is disabled in any of the parent and with 75% chance
                    if ((!conA.Enabled || !conB.Enabled) &&
                        (MyRand.UniformRnd(0, 1) < Cfg.DisableUnnecessaryGene)
                        )
                    {
                        // These gene doesnt look necessary
                        enableGene = false;
                    }

                    //Decide who will give the gene
                    if (MyRand.UniformRnd(0, 1) < 0.50)
                    {
                        // Parent A
                        Node iNode = child.getNodeByID(conA.INode.ID);
                        Node oNode = child.getNodeByID(conA.ONode.ID);
                        gene = conA.DeepCopy(iNode, oNode);
                        iNode.Connections.Add(gene);
                    }
                    else
                    {
                        // Parent B
                        Node iNode = child.getNodeByID(conB.INode.ID);
                        Node oNode = child.getNodeByID(conB.ONode.ID);
                        gene = conB.DeepCopy(iNode, oNode);
                        iNode.Connections.Add(gene);
                    }
                }
                else
                {
                    //Only parentA has this gene, disjoint or excess gene
                    Node iNode = child.getNodeByID(conA.INode.ID);
                    Node oNode = child.getNodeByID(conA.ONode.ID);
                    gene = conA.DeepCopy(iNode, oNode);
                    iNode.Connections.Add(gene);
                }

                gene.Enabled = enableGene;
                child.Connections.Add(gene);
            }

            return(child);
        }
示例#7
0
        // Modify an existing connection, can also be turned Off or On
        public Result ModifyConnection()
        {
            // Check if we have connections or not
            if (Connections.Count == 0)
            {
                //Cannot Add new node
                return(Result.fail);
            }

            // Check if every connection is disabled, like in LoL
            bool allDisabled = true;

            foreach (var c in Connections)
            {
                if (c.Enabled)
                {
                    allDisabled = false;
                    break;
                }
            }
            if (allDisabled)
            {
                return(Result.fail);
            }

            // Select a connection which is not disabled
            Connection conn = null;

            do
            {
                int rndConNumb = MyRand.Next(0, Connections.Count);
                conn = Connections[rndConNumb];
            } while(!conn.Enabled);

            // Slightly modify this connection's weight or bias
            if (MyRand.UniformRnd(0, 1) > 0.3)
            {
                // Weight change
                conn.Weight = MyRand.NormalDist(conn.Weight, 0.1);
                // Not sure if limiting the weight is a good idea or not
                if (conn.Weight > 1.0)
                {
                    conn.Weight = 1.0;
                }
                if (conn.Weight < -1.0)
                {
                    conn.Weight = -1.0;
                }
            }
            else
            {
                // Bias change
                conn.Bias = MyRand.NormalDist(conn.Bias, 0.1);
                // Not sure if limiting the bias is a good idea or not
                if (conn.Bias > 5.0)
                {
                    conn.Bias = 5.0;
                }
                if (conn.Bias < -5.0)
                {
                    conn.Bias = -5.0;
                }
            }

            return(Result.success);
        }
示例#8
0
        // Add new node on an existing connection
        public Result AddNode()
        {
            if (Connections.Count == 0)
            {
                //Cannot Add new node
                return(Result.fail);
            }

            //Get a random connection, it can be disabled too
            int        rndConnNumb = MyRand.Next(0, Connections.Count);
            Connection rndConn     = Connections[rndConnNumb];

            //Deactivate this connection
            rndConn.Enabled = false;

            //Add a new node on it
            Node newNode = new Node(NodeCnt);

            NodeCnt++;
            newNode.Layer = rndConn.INode.Layer + 1;

            //Connect INode and new node
            Connection conn1 = new Connection(rndConn.INode);

            conn1.Weight  = 1.0;
            conn1.Bias    = 0.0;
            conn1.Enabled = true;
            conn1.ONode   = newNode;
            InnovationHandler.GetInnovationID(conn1);
            rndConn.INode.Connections.Add(conn1);

            //TODO i could consider if the connection was disabled to create two "low" effect connections

            //Connect new node and Onode
            Connection conn2 = new Connection(newNode);

            conn2.Weight  = rndConn.Weight;
            conn2.Bias    = rndConn.Bias;
            conn2.Enabled = true;
            conn2.ONode   = rndConn.ONode;
            InnovationHandler.GetInnovationID(conn2);
            newNode.Connections.Add(conn2);

            //If the connection we are breaking is between layers that are next to eachother
            //it means we need to add a new layer
            //else we need to increase the first intermediate layer
            if ((rndConn.INode.Layer + 1) == rndConn.ONode.Layer)
            {
                //New layer is needed

                //Shift every layer by one after the new layer
                foreach (var node in Nodes)
                {
                    if (node.Layer > rndConn.INode.Layer)
                    {
                        node.Layer++;
                    }
                }

                OutputLayer++;
            }
            else if ((rndConn.INode.Layer + 1) < rndConn.ONode.Layer)
            {
                //Layer should already exist
            }
            else
            {
                throw new Exception("Error: The connection is wrong!");
            }

            //Finally add new object to the lists
            Nodes.Add(newNode);
            Connections.Add(conn1);
            Connections.Add(conn2);

            return(Result.success);
        }
示例#9
0
        // Kills the badly graded player/species and creates new ones
        public void UnNaturalSelection()
        {
            // Kill player and species
            for (int i = Species.Count - 1; i >= 0; i--)
            {
                // Determine the share of this species from the population
                int share = (int)Math.Floor((Species[i].AvgFitness / SumAvgFitness) * Size);

                // We should delete species with less members
                if (share < Math.Ceiling(2 / (1 - Cfg.PlayersLeftAlive)))
                {
                    Species.RemoveAt(i);
                }
                else
                {
                    // Kill the halflings
                    Species[i].Bloodlust(share);
                }
                // DONT put anything after this, i-th element could be removed by now
            }

            // Iterate backwards so i can remove elements meanwhile
            for (int i = 0; i < Species.Count; i++)
            {
                // Determine the share of this species from the population
                int share = (int)Math.Floor((Species[i].AvgFitness / SumAvgFitness) * Size);

                // Create new players replacing the dead
                int newToCreate = share - Species[i].Members.Count;
                for (int j = 0; j < newToCreate; j++)
                {
                    Player newPlayer = Species[i].GetBaby();
                    // This shouldn't happen as we took precoutions just before
                    if (newPlayer == null)
                    {
                        throw new Exception($"Cannot generate a new player in this niche!");
                    }

                    // Mutate the new player
                    newPlayer.Mutate();

                    // Add this new player to a species,
                    // it could happen that it will have his own,
                    // or a different from where it was born
                    AddToSpecies(newPlayer);
                }
            }

            // Checking number of players
            int playerNum = 0;

            foreach (var s in Species)
            {
                playerNum += s.Members.Count;
            }
            if (playerNum > Size)
            {
                throw new Exception("Too many players were assigned!");
            }

            // Because of rounding errors, some spaces are not filled it, fill them in
            for (int space = 0; space < Size - playerNum; space++)
            {
                // Select a random species
                int i = MyRand.Next(Species.Count);
                // Get baby from the species
                Player newPlayer = Species[i].GetBaby();
                // Mutate the new player
                newPlayer.Mutate();
                // Add this new player to a species,
                AddToSpecies(newPlayer);
            }
        }