197 lines
7.6 KiB
C#
197 lines
7.6 KiB
C#
using System.Linq;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
#if UNITY_MATHEMATICS
|
|
using Unity.Mathematics;
|
|
using static Unity.Mathematics.math;
|
|
#endif
|
|
|
|
namespace NanoBrain {
|
|
|
|
/// <summary>
|
|
/// Class to manage an array of nuclei for an IReceptor
|
|
/// </summary>
|
|
/// Would love to get rid of this class.
|
|
[System.Serializable]
|
|
public class NucleusArray {
|
|
/// <summary>
|
|
/// The nuclei in this array
|
|
/// </summary>
|
|
[SerializeReference]
|
|
private Nucleus[] _nuclei;
|
|
public Nucleus[] nuclei {
|
|
get {
|
|
return _nuclei;
|
|
}
|
|
set {
|
|
_nuclei = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Create a new NucleusArray with the given nucleus
|
|
/// </summary>
|
|
/// <param name="nucleus">The Nucleus to put in the NucleusArray</param>
|
|
/// This results in an nucleus array of size 1
|
|
public NucleusArray(Nucleus nucleus) {
|
|
this._nuclei = new Nucleus[1];
|
|
this._nuclei[0] = nucleus;
|
|
}
|
|
/// <summary>
|
|
/// Create a new NucleusArray of the given size
|
|
/// </summary>
|
|
/// <param name="size">The size of the nucluesArray</param>
|
|
public NucleusArray(int size) {
|
|
this._nuclei = new Nucleus[size];
|
|
}
|
|
|
|
|
|
// public void AddNucleus(ClusterPrefab prefab) {
|
|
// if (this._nuclei.Length == 0) {
|
|
// Debug.LogError("Empty perceptoid array, cannot add");
|
|
// return;
|
|
// }
|
|
// int newLength = this._nuclei.Length + 1;
|
|
// Nucleus[] newArray = new Nucleus[newLength];
|
|
|
|
// for (int i = 0; i < this._nuclei.Length; i++)
|
|
// newArray[i] = this._nuclei[i];
|
|
// if (this._nuclei[0] is Nucleus nucleus) {
|
|
// newArray[newLength - 1] = nucleus.Clone(prefab);
|
|
// newArray[newLength - 1].name += $": {newLength - 1}";
|
|
// }
|
|
|
|
// this._nuclei = newArray;
|
|
// }
|
|
|
|
// public void RemoveNucleus() {
|
|
// int newLength = this._nuclei.Length - 1;
|
|
// if (newLength == 0) {
|
|
// Debug.LogWarning("Perceptoid array cannot be empty");
|
|
// return;
|
|
// }
|
|
// Nucleus[] newPerceptei = new Nucleus[newLength];
|
|
// for (int i = 0; i < newLength; i++)
|
|
// newPerceptei[i] = this._nuclei[i];
|
|
// // Delete the last perception
|
|
// if (this._nuclei[newLength] is Nucleus nucleus)
|
|
// Neuron.Delete(nucleus); //this._nuclei[newLength]);
|
|
|
|
// this._nuclei = newPerceptei;
|
|
// }
|
|
|
|
public Dictionary<int, Nucleus> thingReceivers = new();
|
|
|
|
#if UNITY_MATHEMATICS
|
|
|
|
private Nucleus FindReceiver(int thingId, float3 inputValue) {
|
|
float inputMagnitude = length(inputValue);
|
|
return FindReceiverMagnitude(thingId, inputMagnitude);
|
|
}
|
|
|
|
#else
|
|
|
|
private Nucleus FindReceiver(int thingId, Vector3 inputValue) {
|
|
float inputMagnitude = inputValue.magnitude;
|
|
return FindReceiverMagnitude(thingId, inputMagnitude);
|
|
}
|
|
|
|
#endif
|
|
|
|
private Nucleus FindReceiverMagnitude(int thingId, float inputMagnitude) {
|
|
Neuron selectedReceiver = null;
|
|
float selectedMagnitude = 0;
|
|
foreach (Nucleus nucleusReceiver in this._nuclei) {
|
|
if (nucleusReceiver is not Neuron receiver)
|
|
continue;
|
|
if (thingReceivers.ContainsValue(receiver) == false) {
|
|
// We found an unusued receiver
|
|
thingReceivers.Add(thingId, receiver);
|
|
return receiver;
|
|
}
|
|
else if (receiver.isSleeping) {
|
|
// A sleeping receiver is not active and can therefore always be used
|
|
thingReceivers.Add(thingId, receiver);
|
|
return receiver;
|
|
}
|
|
else if (selectedReceiver == null) {
|
|
// If we haven't found a receiver yet, just start by taking the first
|
|
selectedReceiver = receiver;
|
|
selectedMagnitude = selectedReceiver.outputMagnitude;
|
|
}
|
|
// Look for the receiver with the lowest magnitude
|
|
else {
|
|
float magnitude = receiver.outputMagnitude;
|
|
|
|
if (magnitude < inputMagnitude && receiver.outputMagnitude < selectedMagnitude) {
|
|
selectedReceiver = receiver;
|
|
selectedMagnitude = selectedReceiver.outputMagnitude;
|
|
}
|
|
}
|
|
}
|
|
if (selectedReceiver != null) {
|
|
// Replace the receiver
|
|
// Find the thingId current associated with the receiver
|
|
int keyToRemove = thingReceivers.FirstOrDefault(r => r.Value.Equals(selectedReceiver)).Key;
|
|
if (keyToRemove != 0 || thingReceivers.ContainsKey(keyToRemove))
|
|
thingReceivers.Remove(keyToRemove);
|
|
// And add the new association
|
|
thingReceivers.Add(thingId, selectedReceiver);
|
|
}
|
|
return selectedReceiver;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Process an external stimulus
|
|
/// </summary>
|
|
/// <param name="inputValue">The value of the stimulus</param>
|
|
/// <param name="thingId">The id of the thing causing the stimulus</param>
|
|
/// <param name="thingName">The name of the thing causing the stimulus</param>
|
|
public virtual void ProcessStimulus(int thingId, Vector3 inputValue, string thingName = null) {
|
|
CleanupReceivers();
|
|
|
|
if (this._nuclei[0] is Neuron neuron)
|
|
inputValue = neuron.Activator(inputValue);
|
|
|
|
if (!thingReceivers.TryGetValue(thingId, out Nucleus selectedReceiver)) {
|
|
// No existing nucleus for this thing
|
|
selectedReceiver = FindReceiver(thingId, inputValue);
|
|
}
|
|
if (selectedReceiver == null)
|
|
return;
|
|
|
|
if (thingName != null) {
|
|
string baseName = selectedReceiver.name;
|
|
int colonPos = selectedReceiver.name.IndexOf(":");
|
|
if (colonPos > 0)
|
|
baseName = selectedReceiver.name[..colonPos];
|
|
selectedReceiver.name = baseName + ": " + thingName;
|
|
}
|
|
|
|
if (selectedReceiver is Neuron selectedNucleus)
|
|
selectedNucleus.ProcessStimulusDirect(inputValue);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a thing-receiver connection when the nucleus is inactive
|
|
/// </summary>
|
|
private void CleanupReceivers() {
|
|
List<int> receiversToRemove = new();
|
|
foreach (KeyValuePair<int, Nucleus> item in thingReceivers) {
|
|
if (item.Value != null && item.Value is Neuron neuron && neuron.isSleeping)
|
|
receiversToRemove.Add(item.Key);
|
|
}
|
|
foreach (int thingId in receiversToRemove) {
|
|
Nucleus selectedReceiver = thingReceivers[thingId];
|
|
|
|
thingReceivers.Remove(thingId);
|
|
|
|
int colonPos = selectedReceiver.name.IndexOf(":");
|
|
if (colonPos > 0)
|
|
selectedReceiver.name = selectedReceiver.name[..colonPos];
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
} |