2026-01-08 14:09:31 +01:00

304 lines
9.2 KiB
C#

using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using LinearAlgebra;
[System.Serializable]
public class Nucleus {
public int id; // hash code
[SerializeField]
protected string _name;
public virtual string name {
get => _name;
set => _name = value;
}
[SerializeField]
public List<Synapse> synapses = new();
[SerializeField]
public List<Receiver> receivers = new();
#region Serialization
[SerializeField]
protected string nucleusType;
public virtual void Rebuild(NanoBrain brain) {
if (this.synapses != null) {
foreach (Synapse synapse in synapses)
synapse.Rebuild(brain);
}
if (this.receivers == null)
this.receivers = new();
else {
foreach (Receiver receiver in receivers.ToArray()) {
if (receiver.Rebuild(brain) == false) {
Debug.Log("Rebuilding failed, removing receiver.");
receivers.Remove(receiver);
}
}
}
}
public static Nucleus RebuildType(NanoBrain brain, Nucleus nucleus) {
if (string.IsNullOrEmpty(nucleus.nucleusType) == false) {
Type nucleusType = Type.GetType(nucleus.nucleusType);
if (nucleusType != null) {
object[] args = new object[] { brain, nucleus.name };
Nucleus rebuiltNucleus = (Nucleus)Activator.CreateInstance(nucleusType, args);
rebuiltNucleus.Deserialize(nucleus);
return rebuiltNucleus;
}
}
return nucleus;
}
public virtual void Deserialize(Nucleus nucleus) { }
#endregion Serialization
#region Runtime state (not serialized)
public NanoBrain brain { get; set; }
private Vector3 _outputValue;
public Vector3 outputValue
{
get { return _outputValue; }
set {
this.stale = 0;
this.isSleeping = false;
_outputValue = value;
}
}
[System.NonSerialized]
private int stale = 1000;
public bool isSleeping = false;
public void IncreaseAge() {
this.stale++;
this.isSleeping = this.stale > 2;
if (isSleeping)
_outputValue = Vector3.zero;
}
[System.NonSerialized]
public int layerIx;
#endregion Runtime state
public Nucleus(string name) {
this._name = name;
this.id = this.GetHashCode();
}
public virtual void AddReceiver(Nucleus receiver) {
this.receivers.Add(new Receiver(receiver));
receiver.SetWeight(this, 1.0f);
}
public void RemoveReceiver(Nucleus receiverNucleus) {
this.receivers.RemoveAll(receiver => receiver.nucleus == receiverNucleus);
receiverNucleus.synapses.RemoveAll(synapse => synapse.nucleus == this);
}
public static void Delete(Nucleus nucleus) {
foreach (Synapse synapse in nucleus.synapses) {
if (synapse.nucleus.receivers.Count > 1) {
// there is another nucleus feeding into this input nucleus
synapse.nucleus.receivers.RemoveAll(r => r.nucleus == nucleus);
}
else {
// No other links, delete it.
Nucleus.Delete(synapse.nucleus);
}
}
foreach (Receiver receiver in nucleus.receivers) {
if (receiver.nucleus != null && receiver.nucleus.synapses != null)
receiver.nucleus.synapses.RemoveAll(s => s.nucleus == nucleus);
}
if (nucleus.brain != null) {
nucleus.brain.nuclei.RemoveAll(n => n == nucleus);
nucleus.brain.GarbageCollection();
}
}
public void GetInputFrom(Nucleus input, float weight = 1.0f) {
input.AddReceiver(this);
this.SetWeight(input, weight);
}
public bool SynapseExists(Nucleus nucleus) {
foreach (Synapse synapse in synapses) {
if (synapse.nucleus == nucleus)
return true;
}
return false;
}
public void SetWeight(Nucleus nucleus, float weight) {
foreach (Synapse synapse in synapses) {
if (synapse.nucleus == nucleus) {
synapse.weight = weight;
return;
}
}
Synapse newSynapse = new(nucleus, weight);
synapses.Add(newSynapse);
}
public virtual void UpdateState() { }
public 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 (Receiver receiver in this.receivers)
receiver.nucleus.UpdateState();
}
}
[System.Serializable]
public class Synapse {
[System.NonSerialized]
public Nucleus nucleus;
public int nucleusId;
public float weight;
public enum CurvePresets {
Linear,
Power,
Sqrt,
Reciprocal,
Custom
}
// public CurvePresets curvePreset;
// public AnimationCurve curve;
public float curveMax = 1.0f;
public Synapse(Nucleus nucleus, float weight) {
this.nucleus = nucleus;
this.nucleusId = nucleus.id;
this.weight = weight;
}
public void Rebuild(NanoBrain brain) {
if (brain == null) {
return;
}
foreach (Nucleus nucleus in brain.nuclei) {
if (nucleus.id == this.nucleusId) {
this.nucleus = nucleus;
return;
}
}
foreach (Perceptoid perceptoid in brain.perceptei) {
if (perceptoid.id == this.nucleusId) {
this.nucleus = perceptoid;
return;
}
}
Debug.LogError($"Synapse deserialization error: could not find nucleus with id {this.nucleusId}");
}
// public AnimationCurve GenerateCurve() {
// switch (this.curvePreset) {
// case CurvePresets.Linear:
// this.curveMax = this.weight;
// return Presets.Linear(this.weight);
// case CurvePresets.Power:
// this.curveMax = this.weight;
// return Presets.Power(2.0f, this.weight);
// case CurvePresets.Sqrt:
// this.curveMax = this.weight;
// return Presets.Power(0.5f, this.weight);
// case CurvePresets.Reciprocal:
// this.curveMax = 1 / 0.01f * this.weight;
// return Presets.Reciprocal(this.weight);
// default:
// this.curveMax = weight;
// return AnimationCurve.Constant(0, 1, weight);
// }
// }
public static class Presets {
private const int samples = 32;
public static AnimationCurve Linear(float weight) {
return AnimationCurve.Linear(0f, 0f, 1000f, weight * 1000);
}
public static AnimationCurve Power(float exponent, float weight) {
// build keyframes
Keyframe[] keys = new Keyframe[samples];
for (int i = 0; i < samples; i++) {
float t = i / (float)(samples - 1);
float v = Mathf.Pow(t, exponent) * weight;
keys[i] = new Keyframe(t, v);
}
AnimationCurve curve = new(keys);
// set tangent modes for each key to Auto (smooth). Use Linear if you prefer straight segments.
for (int i = 0; i < curve.length; i++) {
AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Auto);
AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Auto);
}
return curve;
}
public static AnimationCurve Reciprocal(float weight) {
int samples = 128;
float xMin = 0.001f;
float xMax = 1;
var keys = new Keyframe[samples];
for (int i = 0; i < samples; i++) {
float t = i / (float)(samples - 1);
float x = Mathf.Lerp(xMin, xMax, t);
float y = 1f / x * weight;
keys[i] = new Keyframe(x, y);
}
var curve = new AnimationCurve(keys);
for (int i = 0; i < curve.length; i++) {
AnimationUtility.SetKeyLeftTangentMode(curve, i, AnimationUtility.TangentMode.Linear);
AnimationUtility.SetKeyRightTangentMode(curve, i, AnimationUtility.TangentMode.Linear);
}
return curve;
}
}
}
[System.Serializable]
public class Receiver {
[System.NonSerialized]
public Nucleus nucleus;
public int nucleusId;
public Receiver(Nucleus nucleus) {
this.nucleus = nucleus;
this.nucleusId = nucleus.id;
}
public bool Rebuild(NanoBrain brain) {
if (brain == null) {
return false;
}
foreach (Nucleus nucleus in brain.nuclei) {
if (nucleus.id == this.nucleusId) {
this.nucleus = nucleus;
return true;
}
}
Debug.LogWarning($"Receiver deserialization error: could not find nucleus with id {this.nucleusId}");
return false;
}
}