public MemoryGraphNode AddNamedNodeToGraph(MemoryNode memNode, bool copyOverload = false) { MemoryGraphNode mgn = new MemoryGraphNode(memNode); MemoryNodes.Add(mgn); //Debug.Log(mgn.UID); MemoryHash.Add(mgn.UID, mgn); if (memNode.MemoryType == MemoryType.Cue)// || (copyOverload && memNode.MemoryType != MemoryType.Location)) //Cues cannot have parent nodes. return mgn; //We can access related memories from high-level cues (e.g. Hammers, Villagers, Family etc.) //Here we either add a reference from the related cue to the object. //Or if this is the first object to have this high-level cue, we create the cue. for (int i = 0; i < memNode.RelatedCues.Length; i++) { MemoryGraphNode cueNode; //Debug.Log("Creating cue node: " + memNode.RelatedCues[i]); if (Contains(memNode.RelatedCues[i]) == false) cueNode = AddNamedNodeToGraph(new MemoryNode(memNode.RelatedCues[i], MemoryType.Cue)); else cueNode = GetNamedNodeFromGraph(memNode.RelatedCues[i]); AddDirectedEdge(cueNode, mgn, 11.0f, mgn.MemoryNode.GetInitialOpinion()); } return mgn; }
private void WriteMemoryGraphNode(Utf8JsonWriter writer, MemoryGraphNode node) { writer.WriteStartObject(); writer.WriteNumber(Id, node.Id); if (node.StackOffset != null) { writer.WriteNumber(Offset, node.StackOffset.Value); writer.WriteNumber(Size, node.StackSize !.Value); } writer.WriteString(Title, node.Title); if (node.Value is string s) { writer.WriteString(Value, s); } else { writer.WriteString(Value, ((StringBuilder)node.Value !).ToString()); } if (node.NestedNodes.Count > 0) { writer.WriteStartArray(NestedNodes); foreach (var nested in node.NestedNodes) { WriteMemoryGraphNode(writer, nested); } writer.WriteEndArray(); if (node.NestedNodesLimitReached) { writer.WriteBoolean(NestedNodesLimit, true); } } writer.WriteEndObject(); }
public bool RemoveNamedNodeFromGraph(MemoryGraphNode memNode) { if (memNode.MemoryType != MemoryType.Cue) { //First we check the cues that this memory belongs to. //If the cue is only pointing to this memory, we will remove the cue too. for (int i = 0; i < memNode.RelatedCues.Length; i++) { MemoryGraphNode cueNode; if (Contains(memNode.RelatedCues[i]) == false) cueNode = AddNamedNodeToGraph(new MemoryNode(memNode.RelatedCues[i], MemoryType.Cue)); else cueNode = GetNamedNodeFromGraph(memNode.RelatedCues[i]); if (cueNode.Neighbours.Count == 0) RemoveNamedNodeFromGraph(cueNode); } //Next we must check any neighbours which are pointing to it, and remove those. for (int i = 0; i < memNode.Neighbours.Count; i++) { int index = memNode.Neighbours.IndexOf(memNode); if (index != -1) { memNode.RemoveNeighbour(index); } } } //Finally, we can remove the node itself from our overall list as well as the hash table. bool retVal = true; retVal &= MemoryNodes.Remove(memNode); retVal &= MemoryHash.Remove(memNode.UID); return retVal; //We return a value which will be true if successfully removed from both, or false otherwise. }
private void SerializeMemoryGraphNode(IFastJsonWriter writer, MemoryGraphNode node) { writer.WriteStartObject(); writer.WriteProperty("id", node.Id); if (node.StackOffset != null) { writer.WriteProperty("offset", node.StackOffset.Value); writer.WriteProperty("size", node.StackSize !.Value); } writer.WriteProperty("title", node.Title); writer.WritePropertyName("value"); if (node.Value is StringBuilder builder) { writer.WriteValue(builder); } else { writer.WriteValue((string)node.Value); } if (node.NestedNodes.Count > 0) { writer.WritePropertyStartArray("nestedNodes"); foreach (var nested in node.NestedNodes) { SerializeMemoryGraphNode(writer, nested); } writer.WriteEndArray(); if (node.NestedNodesLimitReached) { writer.WriteProperty("nestedNodesLimit", true); } } writer.WriteEndObject(); }
public MemoryType MemoryType; //This tells us what type of memory this is about. public virtual void UpdateMemory(MemoryGraph memoryGraph, MemoryGraphNode retainedMemory, MemoryCue memCue, float UpdateFrequency) { //LastLocation = memCue.CurrentLocation; //if(memCue.CachedTransform != null) // LastPosition = memCue.CachedTransform.position; retainedMemory.LastLocation = memCue.CurrentLocation; if (memCue.CachedTransform != null) retainedMemory.LastPosition = memCue.CachedTransform.position; retainedMemory.LastUpdated = Time.time; //Debug.Log("Making a memory connection between " + memCue.UniqueNodeID + " and " + LastLocation.ToString() + " - " + UpdateFrequency); //if (UpdateFrequency <= 0.0f) //We want to strengthen the memory between the specific item and the place it has been observed. // return; MemoryGraphNode locNode; string locString = retainedMemory.LastLocation.ToString(); if (!memoryGraph.Contains(locString)) { locNode = memoryGraph.AddNamedNodeToGraph(new LocationNode(retainedMemory.LastLocation)); memoryGraph.AddUndirectedEdge(locNode, retainedMemory); } if (UpdateFrequency <= 0.0f) //We want to strengthen the memory between the specific item and the place it has been observed. return; if (memoryGraph.Contains(locString)) locNode = memoryGraph.GetNamedNodeFromGraph(locString); else locNode = memoryGraph.AddNamedNodeToGraph(new LocationNode(retainedMemory.LastLocation)); //First make a connection between the place and the item. int index = locNode.Neighbours.IndexOf(retainedMemory); if (index == -1) { //Somehow the cue relationship has been broken!? //Rebuild it! memoryGraph.AddDirectedEdge(locNode, retainedMemory, UpdateFrequency, UpdateFrequency); } else { //Strengthing the memory based on the length of time it was active in the working set. locNode.StrengthenMemory(index, UpdateFrequency); //locNode.StrengthenOpinion(index, UpdateFrequency); //We keep opinion strength updating here because it represents how strongly the agent believes the item / character etc. is connected to this location. //Debug.Log(string.Format("Node: {0}, NS: {1}, ES: {2}", retainedMemory.UID, locNode.NodeStrengths[index], locNode.EdgeStrengths[index])); //Debug.Log(string.Format("NS: {0}, ES: {1}", locNode.NodeStrengths[index], cueNode.EdgeStrengths[index])); } //Then between the item and the place. index = retainedMemory.Neighbours.IndexOf(locNode); if (index == -1) { //Somehow the cue relationship has been broken!? //Rebuild it! memoryGraph.AddDirectedEdge(retainedMemory, locNode, UpdateFrequency, UpdateFrequency); } else { //Strengthing the memory based on the length of time it was active in the working set. retainedMemory.StrengthenMemory(index, UpdateFrequency); //retainedMemory.StrengthenOpinion(index, UpdateFrequency); //We keep opinion strength updating here because it represents how strongly the agent believes the item / character etc. is connected to this location. //Debug.Log(string.Format("Node: {0}, NS: {1}, ES: {2}", locNode.UID, retainedMemory.NodeStrengths[index], retainedMemory.EdgeStrengths[index])); //Debug.Log(string.Format("NS: {0}, ES: {1}", locNode.NodeStrengths[index], cueNode.EdgeStrengths[index])); } }
internal void AddNeighbour(MemoryGraphNode toNode, float initialMemoryStrength, float initialOpinionStrength, float initialPrimaryStrength, float initialSecondaryStrength) { Neighbours.Add(toNode); MemStrengths.Add(initialMemoryStrength); PrimaryStrengths.Add(initialPrimaryStrength); SecondaryStrengths.Add(initialSecondaryStrength); OpinionStrengths.Add(initialOpinionStrength); //OpinionStrengths.Add(initialPrimaryStrength + initialSecondaryStrength); AscensionSortNeighbours(OpinionStrengths.Count - 1); //Sorts the Neighbours by affinity (e.g. opinion strength) }
internal void ForceDebugAllocation(MemoryNode memoryNode) { MemoryGraphNode mgn = new MemoryGraphNode(memoryNode); MemoryNodes.Add(mgn); }
void PrintNodeAndNeighbours(MemoryGraphNode mgn, int loopCount, Dictionary<string, bool> memHash) { if (loopCount > 5) return; if (mgn != null && memHash.ContainsKey(mgn.UID) == false) { Debug.Log(mgn.UID); memHash.Add(mgn.UID, true); for (int i = 0; i < mgn.Neighbours.Count; i++) Debug.Log(string.Format("{0}. {1} - {2}", i + 1, mgn.Neighbours[i].UID, mgn.OpinionStrengths[i])); for (int i = 0; i < mgn.Neighbours.Count; i++) PrintNodeAndNeighbours(mgn.Neighbours[i], loopCount + 1, memHash); } }
public override void UpdateMemory(MemoryGraph memoryGraph, MemoryGraphNode retainedMemory, MemoryCue memCue, float UpdateFrequency) { LocationCue locInfo = (LocationCue)memCue; base.UpdateMemory(memoryGraph, retainedMemory, memCue, UpdateFrequency); }
public void AddUndirectedEdge(MemoryGraphNode fromNode, MemoryGraphNode toNode, float initialMemoryStrength = 1.0f, float initialOpinionStrength = 0.0f, float initialPrimaryStrength = 0.0f, float initialSecondaryStrength = 0.0f) { fromNode.AddNeighbour(toNode, initialMemoryStrength, initialOpinionStrength, initialPrimaryStrength, initialSecondaryStrength); toNode.AddNeighbour(fromNode, initialMemoryStrength, initialOpinionStrength, initialPrimaryStrength, initialSecondaryStrength); }
public void RemoveEdge(MemoryGraphNode fromNode, MemoryGraphNode toNode) { //Debug.Log("Removing an edge: " + fromNode.UID + " to " + toNode.UID); for (int i = 0; i < fromNode.Neighbours.Count; i++) { if (fromNode.Neighbours[i] == toNode) { fromNode.RemoveNeighbour(i); break; } } for (int i = 0; i < toNode.Neighbours.Count; i++) { if (toNode.Neighbours[i] == fromNode) { toNode.RemoveNeighbour(i); break; } } //This will need to be watched, to see if any loose memories leak into this system. //As is, I don't believe it is possible. We only need to outright remove a node if the cue reference breaks down. if(fromNode.MemoryType == MemoryType.Cue) { //Debug.Log("Removing the entire node: " + toNode.UID); deadNodes.Add(toNode.UID); //We can't remove a node in the middle of the update, so we store it until the end of the frame. } }
private int GetNeighbourCount(MemoryGraphNode memNode, MemoryType fuzzyType) { int count = 0; for (int i = 0; i < memNode.Neighbours.Count; i++) { if (memNode.Neighbours[i].MemoryType == fuzzyType) ++count; } return count; }
/// <summary> /// Takes a memory and checks if it is remembered correctly or not. /// </summary> /// <returns>Returns the memory, the agent will have no idea if it is genuine or fuzzy.</returns> private int PerformFuzzyCheck(MemoryGraphNode memNode, int strongestMem, string cueUID) { if (memNode.MemStrengths[strongestMem] >= memNode.NodeThreshold) return strongestMem; //It's a strong stage memory, so is remembered correctly! float fuzzyProbability = 1.0f - (memNode.MemStrengths[strongestMem] / memNode.NodeThreshold); if (Random.Range(0.0f, 1.0f) > fuzzyProbability) return strongestMem; //Although a weak stage memory, the probability roll has rememebred it correctly in this instance! MemoryType fuzzyType = memNode.Neighbours[strongestMem].MemoryType; int fuzzyNeighbourCount = GetNeighbourCount(memNode, fuzzyType); if (fuzzyNeighbourCount <= 1) return strongestMem; //Although we are meant to return a false memory, we've only one related memory so it can't really be fuzzified! int fuzzyMem = Random.Range(0, fuzzyNeighbourCount - 2); //We then get the element that will be return in its place. for(int i = 0; i < memNode.Neighbours.Count; i++) { if(memNode.Neighbours[i].MemoryType == fuzzyType && i != strongestMem) { --fuzzyMem; if (fuzzyMem <= 0) { //Debug.Log(string.Format("Agent wanted " + strongestMem + " but it was fuzzied into " + i)); return i; } } } return strongestMem; //We should never get here, but if we do we'll just return the original memory to ensure no crashes. }
public bool GetConceptLocation(string cueUID, out MemoryGraphNode strongestMemoryConcept) { strongestMemoryConcept = null; // Debug.Log("Looking for: " + cueUID); if(!MemoryGraph.Contains(cueUID)) { //Debug.Log("Memory Graph does not contain - " + cueUID); return false; } MemoryGraphNode memNode = MemoryGraph.GetNamedNodeFromGraph(cueUID); //Debug.Log("This node has neighbours - " + memNode.Neighbours.Count); if (memNode.Neighbours == null || memNode.Neighbours.Count == 0) return false; for (int i = 0; i < memNode.Neighbours.Count; i++) { if (memNode.Neighbours[i].MemoryType == MemoryType.Item && memNode.Neighbours[i].LastLocation != Locations.Unknown) { strongestMemoryConcept = memNode.Neighbours[PerformFuzzyCheck(memNode, i, cueUID)]; //We know the list is sorted, so the first valid memory is the one with the greatest affinity. We then fuzzify it, and return the "remembered" concept (either real or fuzzied) return true; } } return false; }
public override void UpdateMemory(MemoryGraph memoryGraph, MemoryGraphNode retainedMemory, MemoryCue memCue, float UpdateFrequency) { ItemCue itemInfo = (ItemCue)memCue; status = itemInfo.Status; owner = itemInfo.Owner; durability = itemInfo.Durability; if (durability < 0.0f) { //Once durability starts to fall, a agent's affinity with an item will also drop. memoryGraph.UpdateCueOpinion(retainedMemory.MemoryNode, -UpdateFrequency); } base.UpdateMemory(memoryGraph, retainedMemory, memCue, UpdateFrequency); if (UpdateFrequency <= 0.0f || itemInfo.Owner == null) //We want to strengthen the memory between the specific item and the place it has been observed. return; MemoryGraphNode charNode; if (memoryGraph.Contains(itemInfo.Owner.UniqueNodeID)) charNode = memoryGraph.GetNamedNodeFromGraph(itemInfo.Owner.UniqueNodeID); else charNode = memoryGraph.AddNamedNodeToGraph(new CharacterNode(itemInfo.Owner)); //First make a connection between the character and the item. int index = charNode.Neighbours.IndexOf(retainedMemory); if (index == -1) memoryGraph.AddDirectedEdge(charNode, retainedMemory, UpdateFrequency, UpdateFrequency); else { //Strengthing the memory based on the length of time it was active in the working set. charNode.StrengthenMemory(index, UpdateFrequency); //charNode.OpinionStrengths[index] += UpdateFrequency; } //Then between the item and the character. index = retainedMemory.Neighbours.IndexOf(charNode); if (index == -1) memoryGraph.AddDirectedEdge(retainedMemory, charNode, UpdateFrequency, UpdateFrequency); else { //Strengthing the memory based on the length of time it was active in the working set. retainedMemory.StrengthenMemory(index, UpdateFrequency); //retainedMemory.OpinionStrengths[index] += UpdateFrequency; } }
public void PrintGraph(MemoryGraphNode node, int depth) { Debug.Log(string.Format("Node: {0}, Neighbours: {1}", node.UID, node.Neighbours.Count)); for (int i = 0; i < node.Neighbours.Count; i++) Debug.Log(string.Format("\tNeighbour: {0}, NS: {1}, ES:{2}", node.Neighbours[i].UID, node.MemStrengths[i], node.OpinionStrengths[i])); Debug.Log("\t-----------"); Debug.Log("-----------"); if (depth > 0) for (int i = 0; i < node.Neighbours.Count; i++) PrintGraph(node.Neighbours[i], depth - 1); }
public override void UpdateMemory(MemoryGraph memoryGraph, MemoryGraphNode retainedMemory, MemoryCue memCue, float UpdateFrequency) { if (IsMonster) return; //Current, we've no need to update monsters. CharacterCue charInfo = (CharacterCue)memCue; base.UpdateMemory(memoryGraph, retainedMemory, memCue, UpdateFrequency); }
private void CopyFromOtherGraph(MemoryGraphNode ourMGN, MemoryGraphNode otherMGN, int loopCount) { //if(loopCount >= 30) //Debug.Log("We've hit an infinite loop!"); RecursiveHash.Add(ourMGN.UID, true); if(ourMGN.MemoryType == MemoryType.Location) return; //We go no deeper than location children. for (int i = 0; i < otherMGN.Neighbours.Count; i++) { for (int j = 0; j < ourMGN.Neighbours.Count; j++) { if (otherMGN.Neighbours[i].MemoryNode == ourMGN.Neighbours[j].MemoryNode) { //Debug.Log("Found a match for " + otherMGN.Neighbours[i].MemoryNode.UID); if (!RecursiveHash.ContainsKey(otherMGN.Neighbours[i].MemoryNode.UID)) CopyFromOtherGraph(ourMGN.Neighbours[j], otherMGN.Neighbours[i], loopCount + 1); //We will see if any of their children also need updated. //else //Debug.Log("Infinite Loop Prevented!"); continue; } } //Debug.Log("Found no match for " + otherMGN.Neighbours[i].MemoryNode.UID + " so I will create it."); //We need to create it as an entirely fresh node. MemoryGraphNode newNode; if(Contains(otherMGN.Neighbours[i].MemoryNode.UID)) newNode = GetNamedNodeFromGraph(otherMGN.Neighbours[i].MemoryNode.UID); else newNode = AddNamedNodeToGraph(otherMGN.Neighbours[i].MemoryNode, true); if (ourMGN.MemoryType == MemoryType.Cue) AddDirectedEdge(ourMGN, newNode, otherMGN.MemStrengths[i], otherMGN.OpinionStrengths[i] * InfluenceRate); else AddUndirectedEdge(ourMGN, newNode, otherMGN.MemStrengths[i], otherMGN.OpinionStrengths[i] * InfluenceRate); //For now, we'll even copy the strength of the other character's memory. newNode.LastLocation = otherMGN.Neighbours[i].LastLocation; newNode.LastPosition = otherMGN.Neighbours[i].LastPosition; // Debug.Log("Copying from " + otherMGN.UID + " to " + otherMGN.Neighbours[i].UID); if (!RecursiveHash.ContainsKey(otherMGN.Neighbours[i].MemoryNode.UID)) CopyFromOtherGraph(newNode, otherMGN.Neighbours[i], loopCount + 1); //else //Debug.Log("Infinite Loop Prevented!"); } }
public override void UpdateMemory(MemoryGraph memoryGraph, MemoryGraphNode retainedMemory, MemoryCue memCue, float UpdateFrequency) { EventCue eventInfo = (EventCue)memCue; base.UpdateMemory(memoryGraph, retainedMemory, memCue, UpdateFrequency); }
public MemoryGraphReference(MemoryGraphNode from, MemoryGraphNode to) { From = from; To = to; }