using System; using System.Collections.Generic; using UnityEngine; namespace NanoBrain { [Serializable] public class ClusterArray : Nucleus { public ClusterPrefab prefab; public Cluster[] clusters; public Dictionary thingClusters = new(); public ClusterArray(ClusterPrefab prefab, Cluster parent, int size, Nucleus receiver = null) { this.prefab = prefab; this.name = prefab.name; this.clusters = new Cluster[size]; for (int ix = 0; ix < size; ix++) { Cluster cluster = new(prefab, parent); cluster.defaultOutput.AddReceiver(receiver); cluster.clusterArray = this; this.clusters[ix] = cluster; } } public ClusterArray(ClusterPrefab prefab, ClusterPrefab parent, int size, Nucleus receiver = null) { this.prefab = prefab; this.name = prefab.name; this.clusters = new Cluster[size]; for (int ix = 0; ix < size; ix++) { Cluster cluster = new(prefab, parent); cluster.defaultOutput.AddReceiver(receiver); cluster.clusterArray = this; this.clusters[ix] = cluster; } } public override Nucleus ShallowCloneTo(Cluster parent) { ClusterArray clone = new(this.prefab, parent, this.clusters.Length) { clusterPrefab = this.clusterPrefab, }; return clone; } public override Nucleus Clone(ClusterPrefab parent) { ClusterArray clone = new(this.prefab, parent, this.clusters.Length) { }; return clone; } public void Add(ClusterPrefab prefab) { if (this.clusters.Length == 0) { Debug.LogError("Empty perceptoid array, cannot add"); return; } int newLength = this.clusters.Length + 1; Cluster[] newClusters = new Cluster[newLength]; string baseName = this.name; int colonPos = baseName.IndexOf(":"); if (colonPos > 0) baseName = baseName[..colonPos]; for (int i = 0; i < this.clusters.Length; i++) newClusters[i] = this.clusters[i]; Cluster sourceCluster = this.clusters[0]; Cluster newCluster = sourceCluster.Clone(prefab) as Cluster; newCluster.name = $"{baseName}: {newLength - 1}"; newCluster.clusterArray = this; newClusters[newLength - 1] = newCluster; this.clusters = newClusters; } public void Remove() { int newLength = this.clusters.Length - 1; if (newLength == 0) { Debug.LogWarning("Perceptoid array cannot be empty"); return; } Cluster[] newClusters = new Cluster[newLength]; for (int i = 0; i < newLength; i++) newClusters[i] = this.clusters[i]; // Delete the last perception //Cluster.Delete(nucleus); this.clusters = newClusters; } public override void UpdateStateIsolated() { // Clusters don't do anything, // The nuclei in them do the work // and should be called directly, not from the cluster } public virtual Cluster GetThingCluster() { Cluster selectedCluster = SelectCluster(); return selectedCluster; } public virtual Cluster GetThingCluster(int thingId, string thingName = null) { if (thingClusters.TryGetValue(thingId, out Cluster cluster)) return cluster; Cluster selectedCluster = SelectCluster(); thingClusters[thingId] = selectedCluster; return selectedCluster; } private Cluster SelectCluster() { // Find a sleeping cluster foreach (Cluster cluster in clusters) { if (cluster.defaultOutput.isSleeping) { RemoveThingCluster(cluster); return cluster; } } // Otherwise find the stalest cluster? Cluster stalestCluster = clusters[0]; for (int ix = 1; ix < clusters.Length; ix++) { if (clusters[ix].defaultOutput.stale > stalestCluster.defaultOutput.stale) stalestCluster = clusters[ix]; } RemoveThingCluster(stalestCluster); return stalestCluster; } private void RemoveThingCluster(Cluster cluster) { List keysToRemove = new(); foreach (KeyValuePair kvp in thingClusters) { if (kvp.Value == cluster) keysToRemove.Add(kvp.Key); } foreach (int thingId in keysToRemove) thingClusters.Remove(thingId); } } }