|
|
| import sys |
| import os |
| import numpy as np |
| import networkx as nx |
| import matplotlib.pyplot as plt |
| import matplotlib.animation as animation |
| import random |
|
|
| |
| sys.path.append(os.path.join(os.path.dirname(__file__), '../tests/tensor_lenia/lib')) |
|
|
| from hypergraph import SimpleWolframSystem |
| from operators import apply_asymmetric_laplacian, apply_laplacian |
|
|
| def run_competitive_survival(sim_steps=400): |
| output_path = "/home/daroch/SOLITONES/EXPERIMENTOS/" |
| log_file = output_path + "exp04_competitive_survival.log" |
| |
| with open(log_file, "w") as f: |
| f.write("--- LEGACY EXP 01: COMPETITIVE SURVIVAL ---\n") |
|
|
| print("Initializing Species Rivalry: The War of Geometries (Level 4)...") |
| with open(log_file, "a") as f: f.write("Initializing Species Rivalry: The War of Geometries (Level 4)...\n") |
| |
| |
| rows, cols = 15, 30 |
| G_raw = nx.grid_2d_graph(rows, cols) |
| mapping = {node: i for i, node in enumerate(G_raw.nodes())} |
| reverse_mapping = {i: node for node, i in mapping.items()} |
| G = nx.relabel_nodes(G_raw, mapping) |
| |
| nodes = list(G.nodes()) |
| adj = {n: set(G.neighbors(n)) for n in nodes} |
| |
| class ArenaSystem: |
| def get_adjacency_list(self): return adj |
| system = ArenaSystem() |
| |
| |
| mass_red = {n: 0.0 for n in nodes} |
| mass_blue = {n: 0.0 for n in nodes} |
| |
| |
| for n, (r, c) in reverse_mapping.items(): |
| if c < 2: mass_red[n] = 5.0 |
| if c > cols - 3: mass_blue[n] = 5.0 |
| |
| |
| resource_nodes = [n for n, (r, c) in reverse_mapping.items() if (cols/2-2 < c < cols/2+2 and rows/2-2 < r < rows/2+2)] |
| |
| pheromone = {n: 0.0 for n in nodes} |
| |
| |
| DT = 0.1 |
| |
| |
| |
| |
| history = [] |
| pos = {n: (reverse_mapping[n][1], -reverse_mapping[n][0]) for n in nodes} |
| |
| msg = f"Arena Ready: {len(nodes)} locations. Hub at Center." |
| print(msg) |
| with open(log_file, "a") as f: f.write(msg + "\n") |
|
|
| for t in range(sim_steps): |
| |
| for n in resource_nodes: |
| pheromone[n] = 15.0 |
| |
| |
| lap_p = apply_laplacian(pheromone, system) |
| new_pheromone = {} |
| for n in nodes: |
| p = pheromone.get(n, 0) |
| b_total = mass_red.get(n, 0) + mass_blue.get(n, 0) |
| dp = (lap_p.get(n, 0) * 1.0) - (0.1 * p) + (b_total * 0.1) |
| new_pheromone[n] = np.clip(p + dp * DT, 0, 20.0) |
| pheromone = new_pheromone |
| |
| |
| |
| weights_red = {} |
| weights_blue = {} |
| |
| for u in nodes: |
| pu = pheromone.get(u, 0) |
| mr_u = mass_red.get(u, 0) |
| mb_u = mass_blue.get(u, 0) |
| |
| for v in adj[u]: |
| pv = pheromone.get(v, 0) |
| mr_v = mass_red.get(v, 0) |
| mb_v = mass_blue.get(v, 0) |
| |
| |
| scent = max(0, pv - pu) |
| |
| |
| |
| weights_red[(u,v)] = 0.1 + (scent * 5.0) / (1.0 + mb_v) |
| weights_blue[(u,v)] = 0.1 + (scent * 5.0) / (1.0 + mr_v) |
| |
| |
| |
| adv_red = apply_asymmetric_laplacian(mass_red, system, weights_red) |
| |
| adv_blue = apply_asymmetric_laplacian(mass_blue, system, weights_blue) |
| |
| new_red = {} |
| new_blue = {} |
| |
| for n in nodes: |
| r = mass_red.get(n, 0) |
| b = mass_blue.get(n, 0) |
| |
| |
| |
| gr = 1.8 * np.exp(-((r - 2.5)**2) / 1.5) - 0.6 |
| |
| gb = 2.5 * np.exp(-((b - 3.0)**2) / 0.8) - 0.4 |
| |
| |
| |
| suffocation = 0.05 * (r + b) |
| |
| dr = (adv_red.get(n, 0) * 4.0) + gr - (0.1 * r) - suffocation |
| db = (adv_blue.get(n, 0) * 1.5) + gb - (0.02 * b) - suffocation |
| |
| new_red[n] = np.clip(r + dr * DT, 0, 10.0) |
| new_blue[n] = np.clip(b + db * DT, 0, 10.0) |
| |
| mass_red = new_red |
| mass_blue = new_blue |
| |
| if t % 10 == 0: |
| history.append({ |
| 'red': mass_red.copy(), |
| 'blue': mass_blue.copy(), |
| 't': t |
| }) |
| if t % 100 == 0: |
| msg = f" T={t}: Red Population={sum(mass_red.values()):.1f}, Blue={sum(mass_blue.values()):.1f}" |
| print(msg) |
| with open(log_file, "a") as f: f.write(msg + "\n") |
|
|
| |
| print(f"Generating Competition Animation...") |
| fig, ax = plt.subplots(figsize=(12, 6)) |
| |
| def update(frame_idx): |
| ax.clear() |
| data = history[frame_idx] |
| mr = data['red'] |
| mb = data['blue'] |
| |
| |
| |
| node_colors = [] |
| for n in nodes: |
| r_val = np.clip(mr.get(n, 0) / 10.0, 0, 1) |
| b_val = np.clip(mb.get(n, 0) / 10.0, 0, 1) |
| |
| node_colors.append((r_val, 0.2, b_val)) |
| |
| nx.draw_networkx_nodes(G, pos, node_size=50, node_color=node_colors, ax=ax) |
| nx.draw_networkx_edges(G, pos, alpha=0.05, ax=ax) |
| |
| |
| red_pop = sum(mr.values()) |
| blue_pop = sum(mb.values()) |
| ax.set_title(f"Solitone War: Red (Aggressive) vs Blue (Stable) | T={data['t']}\nRed Mass: {red_pop:.1f} | Blue Mass: {blue_pop:.1f}") |
| ax.axis('off') |
| ax.set_facecolor('#050505') |
| |
| ani = animation.FuncAnimation(fig, update, frames=len(history), interval=100) |
| save_file = output_path + 'exp04_competitive_survival.gif' |
| ani.save(save_file, writer='pillow', fps=10) |
| print(f"Saved {save_file}") |
| with open(log_file, "a") as f: f.write(f"Saved {save_file}\n") |
|
|
| if __name__ == "__main__": |
| run_competitive_survival() |
|
|