434 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using Unity.Mathematics;
using static Unity.Mathematics.math;
[System.Serializable]
public class Cluster : INucleus {
// The ScriptableObject asset from which the runtime object has been created
[SerializeField]
protected string _name;
public virtual string name {
get => _name;
set => _name = value;
}
public ClusterPrefab storedPrefab;
//public ClusterPrefab prefab;
// public Cluster() {
// }
public Cluster(Cluster parent, ClusterPrefab realPrefab) {
this.storedPrefab = realPrefab;
this.parent = parent;
this.parent?.nuclei.Add(this);
ClonePrefab();
}
public Cluster(ClusterPrefab realPrefab) {
this.storedPrefab = realPrefab;
//this.prefab = prefab.Clone();
this.name = realPrefab.name;
this.cluster = null;
if (this.cluster != null)
this.cluster.nuclei.Add(this);
ClonePrefab();
// IReceptor[] nucleiArray = this.storedPrefab.nuclei.ToArray();
// // first clone the nuclei without their connections
// foreach (IReceptor nucleus in this.storedPrefab.nuclei)
// nucleus.ShallowCloneTo(this);
// IReceptor[] clonedNuclei = this.nuclei.ToArray();
// // Now clone the connections
// for (int nucleusIx = 0; nucleusIx < nucleiArray.Length; nucleusIx++) {
// //IReceptor receptor = nucleiArray[nucleusIx];
// IReceptor clonedReceptor = clonedNuclei[nucleusIx];
// if (clonedReceptor == null)
// continue;
// // Copy the receivers, which will also create the synapses
// foreach (INucleus receiver in nucleiArray[nucleusIx].receivers) {
// int ix = GetNucleusIndex(nucleiArray, receiver);
// if (ix < 0)
// continue;
// if (clonedNuclei[ix] is not INucleus clonedReceiver)
// continue;
// clonedReceptor.AddReceiver(clonedReceiver);
// }
// }
}
public Cluster(ClusterPrefab parent, ClusterPrefab realPrefab) {
this.storedPrefab = realPrefab;
//this.prefab = realPrefab.Clone();
this.name = realPrefab.name;
this.cluster = parent;
if (this.cluster != null)
this.cluster.nuclei.Add(this);
ClonePrefab();
// IReceptor[] nucleiArray = this.storedPrefab.nuclei.ToArray();
// // first clone the nuclei without their connections
// foreach (IReceptor nucleus in this.storedPrefab.nuclei)
// nucleus.ShallowCloneTo(this);
// IReceptor[] clonedNuclei = this.nuclei.ToArray();
// // Now clone the connections
// for (int nucleusIx = 0; nucleusIx < nucleiArray.Length; nucleusIx++) {
// IReceptor receptor = nucleiArray[nucleusIx];
// IReceptor clonedReceptor = clonedNuclei[nucleusIx];
// if (clonedReceptor == null)
// continue;
// // Copy the receivers, which will also create the synapses
// foreach (INucleus receiver in nucleiArray[nucleusIx].receivers) {
// int ix = GetNucleusIndex(nucleiArray, receiver);
// if (ix < 0)
// continue;
// if (clonedNuclei[ix] is not INucleus clonedReceiver)
// continue;
// clonedReceptor.AddReceiver(clonedReceiver);
// }
// }
}
private void ClonePrefab() {
IReceptor[] nucleiArray = this.storedPrefab.nuclei.ToArray();
// first clone the nuclei without their connections
foreach (IReceptor nucleus in this.storedPrefab.nuclei)
nucleus.ShallowCloneTo(this);
IReceptor[] clonedNuclei = this.nuclei.ToArray();
// Now clone the connections
for (int nucleusIx = 0; nucleusIx < nucleiArray.Length; nucleusIx++) {
IReceptor receptor = nucleiArray[nucleusIx];
IReceptor clonedSender = clonedNuclei[nucleusIx];
if (clonedSender == null)
continue;
// Copy the receivers, which will also create the synapses
foreach (INucleus receiver in receptor.receivers) {
int ix = GetNucleusIndex(nucleiArray, receiver);
if (ix < 0)
continue;
if (clonedNuclei[ix] is not INucleus clonedReceiver)
continue;
// Find the synapse for the weight
float weight = 1;
foreach (Synapse synapse in receiver.synapses) {
if (synapse.nucleus == receptor) {
weight = synapse.weight;
break;
}
}
clonedSender.AddReceiver(clonedReceiver, weight);
}
}
}
public virtual IReceptor Clone() {
Neuron clone = new(this.cluster, this.name) {
array = this.array,
};
foreach (Synapse synapse in this.synapses) {
Synapse clonedSynapse = clone.AddSynapse(synapse.nucleus);
clonedSynapse.weight = synapse.weight;
}
foreach (INucleus receiver in this.receivers) {
clone.AddReceiver(receiver);
}
return clone;
}
// public IReceptor CloneTo(ClusterPrefab parent) {
// Cluster clone = new(parent, this.prefab);
// return clone;
// }
public IReceptor ShallowCloneTo(Cluster parent) {
Cluster clone = new(parent, this.storedPrefab) {
name = this.name,
};
return clone;
}
// public IReceptor ShallowCloneTo(ClusterPrefab parent) {
// Cluster clone = new(parent, this.prefab);
// return clone;
// }
// Deep clone a nucleus with its connections
// public virtual Cluster InstantiatePrefab(ClusterPrefab prefab) {
// Cluster clone = new Cluster {
// nuclei = new()
// };
// IReceptor[] nucleiArray = this.nuclei.ToArray();
// // first clone the nuclei without their connections
// foreach (IReceptor nucleus in this.nuclei)
// nucleus.ShallowCloneTo(clone);
// IReceptor[] clonedNuclei = clone.nuclei.ToArray();
// // Now clone the connections
// for (int nucleusIx = 0; nucleusIx < nucleiArray.Length; nucleusIx++) {
// IReceptor receptor = nucleiArray[nucleusIx];
// IReceptor clonedReceptor = clonedNuclei[nucleusIx];
// if (clonedReceptor == null)
// continue;
// // Copy the synapses
// if (receptor is INucleus nucleus) {
// foreach (Synapse synapse in nucleus.synapses) {
// if (clonedReceptor is not INucleus clonedNucleus)
// continue;
// int ix = GetNucleusIndex(nucleiArray, synapse.nucleus);
// if (ix < 0)
// continue;
// IReceptor clonedSynapseNucleus = clonedNuclei[ix];
// if (clonedSynapseNucleus == null)
// continue;
// clonedNucleus.AddSynapse(clonedSynapseNucleus, synapse.weight);
// }
// }
// // Copy the receivers
// foreach (INucleus receiver in nucleiArray[nucleusIx].receivers) {
// int ix = GetNucleusIndex(nucleiArray, receiver);
// if (ix < 0)
// continue;
// if (clonedNuclei[ix] is not INucleus clonedReceiver)
// continue;
// clonedReceptor.AddReceiver(clonedReceiver);
// }
// }
// return clone;
// }
private int GetNucleusIndex(IReceptor[] nucleiArray, IReceptor nucleus) {
for (int i = 0; i < nucleiArray.Length; i++) {
if (nucleus == nucleiArray[i])
return i;
}
return -1;
}
public ClusterPrefab cluster { get; set; }
public Cluster parent { get; set; }
[SerializeReference]
public List<IReceptor> nuclei = new();
public List<INucleus> _inputs = null;
public virtual List<INucleus> inputs {
get {
if (this._inputs == null) {
this._inputs = new();
foreach (IReceptor receptor in this.nuclei) {
if (receptor is INucleus nucleus) {
// inputs have no incoming synapses yet.
if (nucleus.synapses.Count == 0)
this._inputs.Add(nucleus);
}
}
}
return this._inputs;
}
}
//public INucleus output => prefab.output;
public virtual INucleus output {//=> this.nuclei[0] as INucleus;
get {
if (this.nuclei.Count > 0)
return this.nuclei[0] as INucleus;
return null;
}
}
// Not sure if this belongs here...
[SerializeReference]
private NucleusArray _array;
public NucleusArray array {
get { return _array; }
set { _array = value; }
}
#region Synapses
[SerializeField]
private List<Synapse> _synapses = new();
public List<Synapse> synapses => _synapses;
public Synapse AddSynapse(IReceptor sendingNucleus, float weight = 1.0f) {
Synapse synapse = new(sendingNucleus, weight); //, this.prefab.inputs[0]);
this._synapses.Add(synapse);
return synapse;
}
// Does this even exist already?
public void RemoveSynapse() {
}
#endregion Synapses
#region Receivers
[SerializeReference]
private List<INucleus> _receivers = new();
public List<INucleus> receivers {
get { return _receivers; }
set { _receivers = value; }
}
public virtual void AddReceiver(INucleus receivingNucleus, float weight = 1) {
this._receivers.Add(receivingNucleus);
receivingNucleus.AddSynapse(this, weight);
}
public void RemoveReceiver(INucleus receiverNucleus) {
this._receivers.RemoveAll(receiver => receiver == receiverNucleus);
receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
}
#endregion Receivers
#region Runtime
[NonSerialized]
private int stale = 1000;
public bool isSleeping => lengthsq(this.outputValue) == 0;
[NonSerialized]
protected float3 _outputValue;
public virtual float3 outputValue {
get { return _outputValue; }
set {
this.stale = 0;
_outputValue = value;
}
}
#region Update
public virtual void UpdateState() {
UpdateState(new float3(0, 0, 0));
}
public void UpdateState(float3 inputValue) {
float3 sum = inputValue; // new(0, 0, 0);
//Applying the weight factgors
foreach (Synapse synapse in this.synapses) {
sum += synapse.weight * synapse.nucleus.outputValue;
}
//this.prefab.inputs[0].UpdateState(sum);
this.inputs[0].UpdateState(sum);
UpdateResult(this.output.outputValue);
}
public virtual void UpdateResult(Vector3 result) {
// float d = Vector3.Distance(result, this.outputValue);
// if (d < 0.5f) {
// //Debug.Log($"insignificant update: {d}");
// return;
// }
this.outputValue = result;
foreach (INucleus receiver in this.receivers)
receiver.UpdateState();
}
public void UpdateNuclei() {
this.stale++;
if (this.stale > 2)
_outputValue = Vector3.zero;
//foreach (IReceptor nucleus in this.prefab.nuclei)
foreach (IReceptor nucleus in this.nuclei)
nucleus.UpdateNuclei();
}
#endregion Update
#endregion Runtime
/*
[SerializeField]
private List<IReceptor> _dynamicNuclei;
public List<IReceptor> dynamicNuclei {// = new();
get {
if (_dynamicNuclei == null) {
this._dynamicNuclei = new();
foreach (IReceptor nucleus in this.prefab.nuclei) {
IReceptor clone = nucleus.CloneTo(null);
this._dynamicNuclei.Add(clone);
}
}
return this._dynamicNuclei;
}
}
public List<INucleus> _inputs = null;
public List<INucleus> inputs {
get {
this._inputs = new();
if (this.dynamicNuclei != null) {
foreach (IReceptor receptor in this.dynamicNuclei) {
if (receptor is INucleus nucleus)
this._inputs.Add(nucleus);
}
}
return this._inputs;
}
}
public INucleus output => this.dynamicNuclei[0] as INucleus;
public float3 outputValue => this.output.outputValue;
public IReceptor CloneTo(ClusterPrefab parent) {
Cluster clone = new(parent, this.prefab);
return clone;
}
public IReceptor Clone() {
Cluster clone = new(this.cluster, this.prefab);
return clone;
}
#region Properties
public string name {
get { return prefab.name; }
set { prefab.name = value; }
}
public bool isSleeping => lengthsq(this.outputValue) == 0;
public NucleusArray array { get; set; }
#endregion Properties
*/
}