/// <summary> /// Randomly place a node in the middle of an existing gene /// </summary> private void AddMutatedNode() { if (genes.Count == 0) { return; } // 30 attempts to create a node for (int i = 0; i < 30; ++i) { int geneId = Random.Range(0, genes.Count); NeatGene gene = genes[geneId]; // Only add node in enabled genes if (!gene.isEnabled) { continue; } // Add the node NeatNode newNode = new NeatNode(nodes.Count, NeatNode.NodeType.Hidden, this); nodes.Add(newNode); // Disable old gene and add connected genes to new node gene.isEnabled = false; CreateGene(gene.fromNodeId, newNode.ID).weight = 1.0f; // Use 1.0 to avoid initial impact of adding the node CreateGene(newNode.ID, gene.toNodeId).weight = gene.weight; return; } }
/// <summary> /// Update this visualisation /// </summary> /// <param name="from">Visual node connecting from</param> /// <param name="to">Visual node connecting to</param> /// <param name="gene">The gene this is a visualisation for</param> public void SetVisualisation(NetworkNode from, NetworkNode to, NeatGene gene) { visual = GetComponent <RawImage>(); fromNode = from; toNode = to; netGene = gene; visual.enabled = gene.isEnabled; }
/// <summary> /// Create a gene between 2 nodes /// </summary> /// <param name="inNodeId">The index of the node for the gene to leave from</param> /// <param name="outNode">The index of the node for the gene to enter</param> /// <returns>The newly created gene object</returns> private NeatGene CreateGene(int fromNodeId, int toNodeId) { Vector2Int key = new Vector2Int(fromNodeId, toNodeId); // Check gene doesn't alreay exist (DEBUG) if (geneTable.ContainsKey(key)) { Debug.LogError("Gene from (" + fromNodeId + "->" + toNodeId + ") already exists in this genome"); } NeatGene gene = new NeatGene(controller.FetchInnovationId(fromNodeId, toNodeId), fromNodeId, toNodeId, Random.Range(-1.0f, 1.0f)); genes.Add(gene); geneTable[key] = gene; nodes[fromNodeId].outputGenes.Add(gene); nodes[toNodeId].inputGenes.Add(gene); return(gene); }
/// <summary> /// Read in the xml for a specfic generation /// </summary> /// <param name="writer"></param> public void ReadXML(XmlElement entry) { fitness = float.Parse(entry.GetAttribute("fitness")); previousFitness = float.Parse(entry.GetAttribute("previousFitness")); age = int.Parse(entry.GetAttribute("age")); foreach (XmlElement child in entry.ChildNodes) { // Parse nodes if (child.Name == "Nodes") { nodes = new List <NeatNode>(); inputCount = int.Parse(child.GetAttribute("inputCount")); outputCount = int.Parse(child.GetAttribute("outputCount")); int totalCount = int.Parse(child.GetAttribute("totalCount")); // Create nodes for (int i = 0; i < totalCount; ++i) { if (i < inputCount) { nodes.Add(new NeatNode(nodes.Count, NeatNode.NodeType.Input, this)); } else if (i < inputCount + outputCount) { nodes.Add(new NeatNode(nodes.Count, NeatNode.NodeType.Output, this)); } else { nodes.Add(new NeatNode(nodes.Count, NeatNode.NodeType.Hidden, this)); } } } // Parse genes else if (child.Name == "Genes") { genes = new List <NeatGene>(); geneTable = new Dictionary <Vector2Int, NeatGene>(); foreach (XmlElement gene in child.ChildNodes) { if (gene.Name != "Gene") { continue; } int toId = int.Parse(gene.GetAttribute("to")); int fromId = int.Parse(gene.GetAttribute("from")); NeatGene newGene = CreateGene(fromId, toId); newGene.isEnabled = bool.Parse(gene.GetAttribute("isEnabled")); newGene.weight = float.Parse(gene.GetAttribute("weight")); } } } // Fetch assigned species from this if (entry.HasAttribute("speciesGuid")) { System.Guid guid = new System.Guid(entry.GetAttribute("speciesGuid")); // Fetch species foreach (var species in controller.activeSpecies) { if (species.guid == guid) { if (!species.AttemptAdd(this)) { Debug.LogWarning("Couldn't assign network desired species"); } break; } } } }
/// <summary> /// Perform matching on 2 networks to breed them /// </summary> /// <returns>The new child network</returns> public static NeatNetwork Breed(NeatNetwork networkA, NeatNetwork networkB) { // Construct gene table (A genes in index 0 B genes in index 1) Dictionary <int, NeatGene[]> geneTable = new Dictionary <int, NeatGene[]>(); foreach (NeatGene gene in networkA.genes) { geneTable.Add(gene.innovationId, new NeatGene[] { gene, null }); } foreach (NeatGene gene in networkB.genes) { if (geneTable.ContainsKey(gene.innovationId)) { geneTable[gene.innovationId][1] = gene; } else { geneTable.Add(gene.innovationId, new NeatGene[] { null, gene }); } } // Add new genes List <NeatGene> childGenes = new List <NeatGene>(); foreach (var pair in geneTable) { NeatGene[] genes = pair.Value; // Inherit equally if (networkA.fitness == networkB.fitness) { // Only A posseses the gene if (genes[0] != null && genes[1] == null) { childGenes.Add(genes[0]); } // Only B posseses the gene else if (genes[0] == null && genes[1] != null) { childGenes.Add(genes[1]); } // Both posses the gene, so take random else { childGenes.Add(genes[Random.Range(0, 2)]); } } // One nework is better than the other else { // Both posses the gene, so take random if (genes[0] != null && genes[1] != null) { childGenes.Add(genes[Random.Range(0, 2)]); } // Take gene from A else if (networkA.fitness > networkB.fitness && genes[0] != null) { childGenes.Add(genes[0]); } // Take gene from B else if (networkB.fitness > networkA.fitness && genes[1] != null) { childGenes.Add(genes[1]); } } } // Construct child network NeatNetwork childNetwork = new NeatNetwork(networkA.controller, networkA.inputCount, networkB.outputCount); foreach (NeatGene gene in childGenes) { int maxId = System.Math.Max(gene.fromNodeId, gene.toNodeId); // Ensure required genes exist (Must initilise in index order) while (childNetwork.nodes.Count <= maxId) { childNetwork.nodes.Add(new NeatNode(childNetwork.nodes.Count, NeatNode.NodeType.Hidden, childNetwork)); } // Add gene NeatGene newGene = childNetwork.CreateGene(gene.fromNodeId, gene.toNodeId); newGene.isEnabled = gene.isEnabled; newGene.weight = gene.weight; } return(childNetwork); }