using System.Collections.Generic;
using UnityEngine;
namespace NanoBrain {
///
/// The Unity ScriptableObject to implement re-usable Cluster Prefabs
///
[CreateAssetMenu(menuName = "Passer/Cluster")]
public class ClusterPrefab : ScriptableObject {
/// The nuclei in this cluster
[SerializeReference]
public List nuclei = new();
///
/// The output of this cluster
///
/// This only returens the first(default) nucleus. Use outputs[0] instead
public virtual Nucleus output => this.nuclei[0] as Nucleus;
///
/// The nuclei in this cluster which are meant for receiving signals from outside the cluster
///
/// This is currently the nuclei which do not have any incoming synapse
public List _inputs = null;
public virtual List inputs {
get {
if (this._inputs == null) {
this._inputs = new();
foreach (Nucleus receptor in this.nuclei) {
if (receptor is Nucleus nucleus) {
// inputs have no incoming synapses yet.
if (nucleus.synapses.Count == 0)
this._inputs.Add(nucleus);
}
}
}
return this._inputs;
}
}
///
/// The nuclei in this cluster which are meant for sending signals onward
///
private List _outputs = null;
public List outputs {
get {
if (this._outputs == null)
RefreshOutputs();
return this._outputs;
}
}
///
/// Redetermine the outpus in the cluster
///
public void RefreshOutputs() {
this._outputs = new();
foreach (Nucleus nucleus in this.nuclei) {
if (nucleus is Neuron neuron && neuron.receivers.Count == 0)
this._outputs.Add(nucleus);
}
}
///
/// Retrieve a nucleus in this cluster
///
/// The name of the nucleus
/// The Nucleus with the given name or null if no such Nucleus could be found
public Nucleus GetNucleus(string nucleusName) {
foreach (Nucleus nucleus in this.nuclei) {
if (nucleus.name == nucleusName)
return nucleus;
}
return null;
}
// Call this function to ensure that there is at least one nucleus
// This is an invariant and should be ensured before the nucleus is used
// because output requires it.
public void EnsureInitialization() {
nuclei ??= new List();
if (nuclei.Count == 0)
new Neuron(this, "Output"); // Every cluster should have at least 1 neuron
}
public void GarbageCollection() {
HashSet visitedNuclei = new();
foreach (Nucleus output in this.outputs)
MarkNuclei(visitedNuclei, output);
//Debug.Log($"Garbage collection found {visitedNuclei.Count} Nuclei");
this.nuclei.RemoveAll(nucleus => visitedNuclei.Contains(nucleus) == false);
}
public void MarkNuclei(HashSet visitedNuclei, Nucleus nucleus) {
if (nucleus is null)
return;
if (nucleus.parent != null && nucleus.parent.prefab != this)
visitedNuclei.Add(nucleus.parent);
else
visitedNuclei.Add(nucleus);
if (nucleus.synapses != null) {
HashSet visitedSynapses = new();
foreach (Synapse synapse in nucleus.synapses) {
if (synapse != null && synapse.neuron != null) {
visitedSynapses.Add(synapse);
if (synapse.neuron is Nucleus synapse_nucleus)
MarkNuclei(visitedNuclei, synapse_nucleus);
}
}
nucleus.synapses.RemoveAll(synapse => visitedSynapses.Contains(synapse) == false);
}
if (nucleus is Neuron neuron && neuron.receivers != null) {
HashSet visitedReceivers = new();
foreach (Nucleus receiver in neuron.receivers) {
if (receiver != null && receiver != null) {
visitedReceivers.Add(receiver);
visitedNuclei.Add(receiver);
}
}
neuron.receivers.RemoveAll(receiver => visitedReceivers.Contains(receiver) == false);
}
}
public virtual void UpdateNuclei() {
foreach (Nucleus nucleus in this.nuclei)
nucleus.UpdateNuclei();
}
public int GetNucleusIndex(Nucleus receiver) {
int ix = 0;
foreach (Nucleus nucleus in this.nuclei) {
if (receiver == nucleus)
return ix;
ix++;
}
return -1;
}
}
}