Pascal Serrarens 3f8716794a Squashed 'NanoBrain/' changes from fbca658..3eb4ab7
3eb4ab7 Cleanup

git-subtree-dir: NanoBrain
git-subtree-split: 3eb4ab7993054a9ec75ba7cadede74bfb213e34f
2026-04-08 09:33:52 +02:00

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];
}
}
}
}