Variables at t(1):
– Matrix size → 128 × 128
– Energy types → 2
– Agent classes → 4
– Class assignment per agent → 2
– Execution time → 5,356,800 seconds
Execution Time:
– start: 01:30:41 dec 03, 2025 (utc)
– END: 01:30:41 FEB 03, 2026 (utc)
Key:
– Producer -> Green
– Consumer -> Red
– Nomad -> Orange
– architect -> Blue
– seed -> brown
– dead -> black
– Light -> Sun
– Spots -> Rain
Scripts for t(1):
MATRIX:defines the grid structure and its dimensions.
# matrix/cell.py
# 8-11-2025
from vars.vars import colors, void
class CellContent:
def __init__(self, desc: str, type_id: int, energy: float = 0.0):
self.desc = desc
self.type_id = type_id
self.energy = float(energy)
def __repr__(self):
return f"{self.desc}:{self.type_id} -> {self.energy}"
class Cell:
def __init__(self, state: int = void):
self.contents = [
CellContent(info["desc"], info["id"], 0.0)
for info in colors.values()
]
self.state = state
def __repr__(self):
return f"Cell(contents={self.contents}, state={self.state})"
# matrix/matrix.py
# 8-11-2025
from matrix.cell import Cell
from vars.vars import matrix_path
import pickle as plk
import os
# 11-11-2025
import itertools, json
from vars.vars import message_js, buffer_message_json
class Matrix:
def __init__(self, n: int = 128):
self.n = n
self.grid = [[Cell() for _ in range(n)] for _ in range(n)]
def get_cell(self, row: int, col: int) -> Cell:
return self.grid[row][col]
def set_cell(self, row: int, col: int, cell: Cell):
self.grid[row][col] = cell
def save_backup(self, path: str = matrix_path):
tmp_path = path + ".tmp"
with open(tmp_path, "wb") as f:
plk.dump(self, f)
os.replace(tmp_path, path)
@classmethod
def load_backup(cls, path: str = matrix_path):
with open(path, "rb") as f:
return plk.load(f)
def __repr__(self):
return f"Matrix({self.n}x{self.n})"
# 11-11-2025
def save_params_json(self, tick, sun_params, water_params, agentstore, path=message_js):
if not hasattr(self, "_buffer_cycle_json"):
self._buffer_files_json = buffer_message_json
self._buffer_cycle_json = itertools.cycle(self._buffer_files_json)
buffer_file = next(self._buffer_cycle_json)
agents_payload = [[agent.y, agent.x, agent.state] for agent in agentstore]
payload = {
"tick": tick,
"sun": sun_params,
"water": water_params,
"agents": agents_payload
}
with open(buffer_file, "w", encoding="utf-8") as f:
json.dump(payload, f, indent=2)
try:
os.remove(path)
except FileNotFoundError:
pass
os.symlink(buffer_file, path)
Agents → rules and behaviors of the agent population.
# agents/agentstore.py
# 28-10-2025
from collections import Counter
import bisect
class AgentStore:
def __init__(self):
self._keys = []
self._agents = []
self._agent_map = {}
self.stats_counter = Counter()
self._next_id = 2
self._delete_list = set()
# 27-11-2025
def mark_for_delete(self, agent_id):
self._delete_list.add(agent_id)
def flush_deletes(self):
for agent_id in self._delete_list:
agent = self._agent_map.get(agent_id)
if agent:
self.remove(agent)
self._delete_list.clear()
def add(self, agent):
if agent.id in self._agent_map:
self.remove(self._agent_map[agent.id])
key = (-agent.speed, agent.id)
idx = bisect.bisect_left(self._keys, key)
self._keys.insert(idx, key)
self._agents.insert(idx, agent)
self._agent_map[agent.id] = agent
def remove(self, agent):
self._agent_map.pop(agent.id, None)
key = (-agent.speed, agent.id)
idx = bisect.bisect_left(self._keys, key)
if idx < len(self._keys) and self._keys[idx] == key and self._agents[idx].id == agent.id:
self._keys.pop(idx)
self._agents.pop(idx)
else:
for j, a in enumerate(self._agents):
if a.id == agent.id:
self._agents.pop(j)
self._keys.pop(j)
break
def get_by_id(self, agent_id):
return self._agent_map.get(agent_id)
def __iter__(self):
return iter(self._agents)
def __len__(self):
return len(self._agents)
# 22-11-2025
def reset(self):
self.stats_counter = Counter()
def increment_stat(self, state):
self.stats_counter[state] += 1
def generate_id(self):
new_id = self._next_id
self._next_id += 1
return new_id
def get_stats(self):
names = {
0: "producer",
1: "consumer",
2: "nomad",
3: "architect",
}
return {
names.get(k, f"unknown({k})"): v
for k, v in self.stats_counter.items()
}
# 1-12-2025
def is_marked_for_delete(self, agent_id) -> bool:
return agent_id in self._delete_list
# agents/agents.py
# 13-11-2025
import random
from vars.vars import void, dead, death_probs
# 21-11-2025
from vars.vars import fitness_params, seed, dead_st
# 23-11-2025
from vars.fitness_map import fitness_map
class Agent:
def __init__(self, agent_id, x, y, energy=1.0, speed=1, costs=1, fitness1=None, fitness2=None):
self.x = x
self.y = y
self.energy = energy
self.speed = speed
self.costs = costs
self.alive = True
self.id = agent_id
self.type = [fitness1, fitness2]
self.turn = 0
self.seed = None
self.state = None
def place_agent_in(self, matrix):
cell = matrix.get_cell(self.x, self.y)
if cell.state > dead:
return
cell.state = self.id
def die(self, matrix):
cell = matrix.get_cell(self.x, self.y)
self.alive = False
self.type = [None, None]
if self.seed is not None:
self.state = seed
else:
self.state = dead_st
cell.state = dead
def move_agent_to(self, matrix, new_x, new_y):
old_cell = matrix.get_cell(self.x, self.y)
old_cell.state = void
self.x, self.y = new_x, new_y
new_cell = matrix.get_cell(new_x, new_y)
new_cell.state = self.id
def update(self, matrix, agentstore):
self.turn += 1
self.update_energy(-self.costs)
if not self.alive:
return
if None not in self.type:
p1 = death_probs[self.type[0].fitness_id]
p2 = death_probs[self.type[1].fitness_id]
prob = (p1 + p2) / 2.0
if random.random() < prob:
self.die(matrix)
return
if self.energy <= 0:
self.die(matrix)
return
self.perform_action(matrix, agentstore)
def perform_action(self, matrix, agentstore):
if None in self.type:
return
chosen = random.choice(self.type)
self.state = chosen.fitness_id
agentstore.increment_stat(self.state)
chosen.act(self, matrix, agentstore)
def produce_seed(self, fitness_id):
if self.seed is None:
self.seed = fitness_id
self.energy = self.energy / 2
def has_seed(self):
return self.seed is not None
def collect_seed(self):
if self.seed is None:
return None
s = self.seed
self.seed = None
return s
def update_energy(self, delta):
self.energy += delta
if self.energy < 0:
self.energy = 0
def __repr__(self):
return f"<Agent id={self.id} pos=({self.x},{self.y}) energy={self.energy:.2f} alive={self.alive}>"
# 23-11-2025
def born_from_seed(self, matrix, agentstore):
cell = matrix.get_cell(self.x, self.y)
sun_energy = cell.contents[0].energy
water_energy = cell.contents[1].energy
if water_energy > 0 or sun_energy > 0:
primary = fitness_map[self.seed]()
choices = list(fitness_map.values())
secondary = random.choice(choices)()
self.type = [primary, secondary]
fid1 = primary.fitness_id
fid2 = secondary.fitness_id
agentstore.remove(self)
self.speed = (fitness_params[fid1]["speed"] + fitness_params[fid2]["speed"]) / 2.0
self.costs = (fitness_params[fid1]["cost"] + fitness_params[fid2]["cost"]) / 2.0
self.state = fid1
self.seed = None
self.alive = True
self.turn = 0
self.update_energy(sun_energy + water_energy)
agentstore.add(self)
else:
self.turn +=1
self.update_energy(-self.costs)
if self.energy == 0:
self.seed = None
# 24-11-2025
def create_seed(self, agentstore, matrix, fid, x, y, seed_energy): #loop
new_id = agentstore.generate_id()
new_agent = Agent(
agent_id=new_id,
x=x,
y=y,
energy=seed_energy,
speed=fitness_params[fid]["speed"],
costs=fitness_params[fid]["cost"],
fitness1=None,
fitness2=None
)
new_agent.alive = False
new_agent.seed = fid
new_agent.state = seed
agentstore.add(new_agent)
matrix.get_cell(x, y).state = new_id
# agents/producer.py
# 21-11-2025
from vars.vars import fitness_params, producer
class Producer:
def __init__(self):
self.fitness_id = producer
self.seed_l = fitness_params[self.fitness_id]["seed_l"]
def get_fitness_id(self):
return self.fitness_id
def act(self, agent, matrix, agentstore):
cell = matrix.get_cell(agent.x, agent.y)
sun_energy = cell.contents[0].energy
water_energy = cell.contents[1].energy
available = sun_energy + water_energy
factor = min(0.5 + agent.turn * 0.01, 0.9)
absorbed = available * factor
agent.update_energy(absorbed)
if agent.energy >= self.seed_l:
agent.produce_seed(self.fitness_id)
# agents/consumer.py
# 21-11-2025
from vars.vars import fitness_params, consumer, producer, void, dead
import random
class Consumer:
def __init__(self):
self.fitness_id = consumer
self.seed_l = fitness_params[self.fitness_id]["seed_l"]
def get_fitness_id(self):
return self.fitness_id
def act(self, agent, matrix, agentstore):
best_dx, best_dy = 0, 0
best_value = -float("inf")
has_producer_target = False
empty_moves = []
for dx, dy in ((-1,-1),(-1,0),(-1,1),
(0,-1), (0,1),
(1,-1),(1,0),(1,1)):
nx = (agent.x + dx) % matrix.n
ny = (agent.y + dy) % matrix.n
cell = matrix.get_cell(nx, ny)
if cell.state > dead:
target = agentstore.get_by_id(cell.state)
if target and target.state == producer:
value = target.energy
if value > best_value:
best_value, best_dx, best_dy = value, dx, dy
has_producer_target = True
elif cell.state == void:
empty_moves.append((dx, dy))
if has_producer_target and random.random() < 0.5:
nx = (agent.x + best_dx) % matrix.n
ny = (agent.y + best_dy) % matrix.n
target_cell = matrix.get_cell(nx, ny)
target = agentstore.get_by_id(target_cell.state)
if target:
gained = target.energy * 0.9
agent.update_energy(gained)
agentstore.mark_for_delete(target.id)
target_cell.state = void
agent.move_agent_to(matrix, nx, ny)
else:
if empty_moves:
if random.random() < 0.5:
left_candidates = [m for m in empty_moves if m[0] == -1]
if left_candidates:
dx, dy = random.choice(left_candidates)
else:
dx, dy = random.choice(empty_moves)
else:
dx, dy = random.choice(empty_moves)
nx = (agent.x + dx) % matrix.n
ny = (agent.y + dy) % matrix.n
agent.move_agent_to(matrix, nx, ny)
if agent.energy >= self.seed_l:
if has_producer_target or random.random() < 0.5:
agent.produce_seed(producer)
else:
agent.produce_seed(self.fitness_id)
# agents/nomad.py
# 22-11-2025
from vars.vars import fitness_params, nomad, void, architect, producer, consumer
import random
class Nomad:
def __init__(self):
self.fitness_id = nomad
self.seed_l = fitness_params[self.fitness_id]["seed_l"]
def get_fitness_id(self):
return self.fitness_id
def act(self, agent, matrix, agentstore):
cell = matrix.get_cell(agent.x, agent.y)
water_energy = cell.contents[1].energy
available = water_energy
factor = min(0.5 + agent.turn * 0.01, 0.9)
absorbed = available * factor
agent.update_energy(absorbed)
if agent.has_seed():
best_dx, best_dy = 0, 0
best_value = -float("inf")
has_architect_target = False
empty_moves = []
for dx, dy in ((-1,-1),(-1,0),(-1,1),
(0,-1), (0,1),
(1,-1),(1,0),(1,1)):
nx = (agent.x + dx) % matrix.n
ny = (agent.y + dy) % matrix.n
neighbor = matrix.get_cell(nx, ny)
if neighbor.state > 1:
target = agentstore.get_by_id(neighbor.state)
if target and target.state == architect and not agentstore.is_marked_for_delete(target.id):
value = target.energy
if value > best_value:
best_value, best_dx, best_dy = value, dx, dy
has_architect_target = True
elif neighbor.state == void:
empty_moves.append((dx, dy))
if has_architect_target:
nx = (agent.x + best_dx) % matrix.n
ny = (agent.y + best_dy) % matrix.n
target_cell = matrix.get_cell(nx, ny)
target = agentstore.get_by_id(target_cell.state)
if target:
seed_energy = target.energy * 0.45
agent_energy = target.energy * 0.45
agent.update_energy(agent_energy)
agentstore.mark_for_delete(target.id)
target_cell.state = void
agent.create_seed(agentstore, matrix, agent.seed, nx, ny, seed_energy)
agent.seed = None
else:
if empty_moves:
if random.random() < 0.5:
left_candidates = [m for m in empty_moves if m[0] == -1]
if left_candidates:
dx, dy = random.choice(left_candidates)
else:
dx, dy = random.choice(empty_moves)
else:
dx, dy = random.choice(empty_moves)
old_x, old_y = agent.x, agent.y
nx = (agent.x + dx) % matrix.n
ny = (agent.y + dy) % matrix.n
agent.move_agent_to(matrix, nx, ny)
if agent.energy >= self.seed_l:
seed_energy = agent.energy / 2
agent.update_energy(-seed_energy)
agent.create_seed(agentstore, matrix, random.randrange(4), old_x, old_y, seed_energy)
else:
best_dx, best_dy = 0, 0
has_seed_target = False
empty_moves = []
for dx, dy in ((-1,-1),(-1,0),(-1,1),
(0,-1), (0,1),
(1,-1),(1,0),(1,1)):
nx = (agent.x + dx) % matrix.n
ny = (agent.y + dy) % matrix.n
neighbor = matrix.get_cell(nx, ny)
if neighbor.state > 1:
target = agentstore.get_by_id(neighbor.state)
if target and (target.state == producer or target.state == consumer):
s = target.collect_seed()
if s is not None:
agent.seed = s
best_dx, best_dy = dx, dy
has_seed_target = True
break
elif neighbor.state == void:
empty_moves.append((dx, dy))
if has_seed_target:
return
else:
if empty_moves:
if random.random() < 0.5:
left_candidates = [m for m in empty_moves if m[0] == -1]
if left_candidates:
dx, dy = random.choice(left_candidates)
else:
dx, dy = random.choice(empty_moves)
else:
dx, dy = random.choice(empty_moves)
old_x, old_y = agent.x, agent.y
nx = (agent.x + dx) % matrix.n
ny = (agent.y + dy) % matrix.n
agent.move_agent_to(matrix, nx, ny)
if agent.energy >= self.seed_l:
seed_energy = agent.energy / 2
agent.update_energy(-seed_energy)
agent.create_seed(agentstore, matrix, random.randrange(4), old_x, old_y, seed_energy)
# agents/architect.py
# 22-11-2025
from vars.vars import fitness_params, architect, void
class Architect:
def __init__(self):
self.fitness_id = architect
self.seed_l = fitness_params[self.fitness_id]["seed_l"]
def get_fitness_id(self):
return self.fitness_id
def act(self, agent, matrix, agentstore):
cell = matrix.get_cell(agent.x, agent.y)
water_energy = cell.contents[1].energy
if water_energy > 0:
absorbed = water_energy * 0.9
agent.update_energy(absorbed)
if agent.energy >= self.seed_l:
for dx, dy in ((-1,-1),(-1,0),(-1,1),
(0,-1), (0,1),
(1,-1),(1,0),(1,1)):
nx = (agent.x + dx) % matrix.n
ny = (agent.y + dy) % matrix.n
neighbor = matrix.get_cell(nx, ny)
if neighbor.state == void:
seed_energy = agent.energy / 2
agent.update_energy(-seed_energy)
agent.create_seed(agentstore, matrix, self.fitness_id, nx, ny, seed_energy)
break
# agents/population.py
# 21-11-2025
import pickle as plk
from agents.agentstore import AgentStore
from matrix.matrix import Matrix
from vars.vars import population_path, producer, consumer, nomad, architect, fitness_params, seed, dead, void
# 24-11-2025
from agents.agents import Agent
import random
import os
class Population:
def __init__(self, matrix: Matrix):
self.matrix = matrix
self.agents = AgentStore()
def save(self, path=population_path):
with open(path, "wb") as f:
plk.dump(self, f)
@classmethod
def load(cls, matrix: Matrix, path=population_path):
try:
if os.path.exists(path):
with open(path, "rb") as f:
pop = plk.load(f)
if isinstance(pop, cls):
pop.matrix = matrix
pop.align_population_with_matrix()
return pop
new_pop = cls(matrix)
new_pop.genesis()
return new_pop
except Exception as e:
new_pop = cls(matrix)
new_pop.genesis()
return new_pop
# 24-11-2025
def genesis(self): #door
num_agents_per_fitness = ((self.matrix.n ** 2) // 4) // 4
fitness_ids = [producer, consumer, nomad, architect]
coords = [(x, y) for x in range(self.matrix.n) for y in range(self.matrix.n)]
random.shuffle(coords)
chosen = coords[:num_agents_per_fitness * len(fitness_ids)]
idx = 0
for fid in fitness_ids:
params = fitness_params[fid]
for _ in range(num_agents_per_fitness):
agent_id = self.agents.generate_id()
x, y = chosen[idx]
idx += 1
agent = Agent(
agent_id,
x,
y,
energy=params["energy"],
speed=params["speed"],
costs=params["cost"],
fitness1=None,
fitness2=None
)
agent.seed = fid
agent.state = seed
agent.alive = False
agent.place_agent_in(self.matrix)
self.agents.add(agent)
self.agents.increment_stat(fid)
# 25-11-2025
def fitness_agents(self, fraction=0.75):
agents_list = list(self.agents)
k = int(len(agents_list) * fraction)
chosen = random.sample(agents_list, k)
for agent in chosen:
if not agent.alive:
if agent.seed is not None:
agent.born_from_seed(self.matrix, self.agents)
else:
cell = self.matrix.get_cell(agent.x, agent.y)
cell.state = void
self.agents.remove(agent)
else:
if not self.agents.is_marked_for_delete(agent.id):
agent.update(self.matrix, self.agents)
self.agents.flush_deletes()
var = self.matrix.debug_states()
# 26-11-2025
def align_population_with_matrix(self):
for x in range(self.matrix.n):
for y in range(self.matrix.n):
cell = self.matrix.get_cell(x, y)
cell.state = void
for agent in list(self.agents):
cell = self.matrix.get_cell(agent.x, agent.y)
if agent.alive or agent.seed is not None:
cell.state = agent.id
else:
cell.state = dead
Energy cycle → distribution and flow of energy across cells.
# 10-11-2025
import math
import random
# 11-11-2025
from vars.vars import time_energy_path
import pickle as plk
import os
class TimeEnergy:
def __init__(self, matrix, tick=0, ticks_per_step=1, season_days=2, seasons=None, transition_window=0.1):
self.matrix = matrix
self.tick = tick
self.n = matrix.n
self.ticks_per_step = max(1, int(ticks_per_step))
self.day_length = self.n * self.ticks_per_step
self.season_days = max(1, int(season_days))
self.transition_window = float(transition_window)
default_seasons = [
{"name": "winter", "day_frac": 0.35, "intensity": 0.70, "edge_frac": 0.08,
"rain_intensity": 0.9, "rain_spots": 5},
{"name": "spring", "day_frac": 0.50, "intensity": 0.90, "edge_frac": 0.08,
"rain_intensity": 0.7, "rain_spots": 6},
{"name": "summer", "day_frac": 0.65, "intensity": 1.00, "edge_frac": 0.08,
"rain_intensity": 0.3, "rain_spots": 4},
{"name": "autumn", "day_frac": 0.50, "intensity": 0.85, "edge_frac": 0.08,
"rain_intensity": 0.6, "rain_spots": 6},
]
self.seasons = seasons if seasons is not None else default_seasons
self.spot_state = []
self._next_spot_id = 1
self._spawn_budget_accum = 0.0
def advance_tick(self):
self.tick += 1
return self.tick
def save_backup(self, path=time_energy_path):
tmp_path = path + ".tmp"
with open(tmp_path, "wb") as f:
plk.dump(self, f)
os.replace(tmp_path, path)
@classmethod
def load_backup(cls, matrix, path=time_energy_path):
try:
with open(path, "rb") as f:
te = plk.load(f)
te.matrix = matrix
return te
except Exception as e:
return cls(matrix)
def get_sun_params(self, tick):
steps = tick // self.ticks_per_step
position = (self.n - 1) - (steps % self.n)
season_length_ticks = self.season_days * self.day_length
season_index = (tick // season_length_ticks) % len(self.seasons)
s = self.seasons[season_index]
season_tick = tick % season_length_ticks
season_progress = season_tick / season_length_ticks
next_season_index = (season_index + 1) % len(self.seasons)
s_next = self.seasons[next_season_index]
if season_progress > 1 - self.transition_window:
t = (season_progress - (1 - self.transition_window)) / self.transition_window
core_width = int(round((1-t)*s["day_frac"]*self.n + t*s_next["day_frac"]*self.n))
edge_width = int(round((1-t)*s["edge_frac"]*self.n + t*s_next["edge_frac"]*self.n))
intensity = (1-t)*s["intensity"] + t*s_next["intensity"]
season_name = f"{s['name']}→{s_next['name']}"
else:
core_width = int(round(s["day_frac"]*self.n))
edge_width = int(round(s["edge_frac"]*self.n))
intensity = s["intensity"]
season_name = s["name"]
day_index = tick // self.day_length
return {
"position": position,
"season": season_name,
"core_width": core_width,
"edge_width": edge_width,
"intensity": intensity,
"day_index": day_index,
"season_index": season_index,
}
def compute_energy(self, distance, half_core, edge, Imax):
if distance <= half_core:
return Imax
elif distance <= half_core + edge:
t = (distance - half_core) / max(1, edge)
return Imax * (1.0 - t)
else:
return 0.0
def apply_sun_to_matrix(self, tick):
p = self.get_sun_params(tick)
pos, core, edge, Imax = p["position"], p["core_width"], p["edge_width"], p["intensity"]
half_core = core // 2
for i in range(self.n):
for j in range(self.n):
d_lin = abs(j - pos)
d_mod = min(d_lin, self.n - d_lin)
e = self.compute_energy(d_mod, half_core, edge, Imax)
self.matrix.get_cell(i, j).contents[0].energy = e
return p
def _spot_base_params(self, season_index):
s = self.seasons[season_index]
return {
"rain_intensity": float(s.get("rain_intensity", 0.5)),
"spots_count": int(s.get("rain_spots", 4)),
"base_radius": max(3, int(self.n * 0.15)),
"edge_width": max(2, int(self.n * 0.06)),
"radius_osc_amp": max(1, int(self.n * 0.04)),
"vx": 0.45, "vy": -0.40,
"Ax": self.n * 0.10, "Ay": self.n * 0.07,
"Px": self.day_length, "Py": int(self.day_length * 0.75),
"Pr": int(self.day_length * 0.5),
"fade_ticks": max(8, int(self.transition_window * self.season_days * self.day_length * 0.5)),
"max_spawn_per_tick": 0.5,
}
def _update_spot_state(self, tick, base_curr, base_next, season_progress):
if season_progress > 1.0 - self.transition_window:
t = (season_progress - (1.0 - self.transition_window)) / self.transition_window
t = max(0.0, min(1.0, t))
else:
t = 0.0
target_count_float = (1.0 - t) * base_curr["spots_count"] + t * base_next["spots_count"]
target_intensity_base = (1.0 - t) * base_curr["rain_intensity"] + t * base_next["rain_intensity"]
fade_ticks = base_curr["fade_ticks"]
max_rate = base_curr["max_spawn_per_tick"]
delta = target_count_float - len(self.spot_state)
self._spawn_budget_accum += max(-max_rate, min(max_rate, delta))
births = 0
while self._spawn_budget_accum >= 1.0:
self._spawn_budget_accum -= 1.0
births += 1
deaths = 0
while self._spawn_budget_accum <= -1.0:
self._spawn_budget_accum += 1.0
deaths += 1
for _ in range(births):
s_idx = len(self.spot_state)
cols = max(1, int(math.sqrt(max(1, base_curr["spots_count"]))))
row = s_idx // cols
col = s_idx % cols
x0 = (self.n // 4 + col * (self.n // max(1, cols))) % self.n
y0 = (self.n // 3 + row * (self.n // max(1, cols + 1))) % self.n
phase = random.random() * 2.0 * math.pi
self.spot_state.append({
"id": self._next_spot_id,
"cx0": x0, "cy0": y0, "phase": phase,
"life": 0.0,
"target_life": 1.0,
"base_radius": base_curr["base_radius"],
"edge_width": base_curr["edge_width"],
})
self._next_spot_id += 1
if deaths > 0 and len(self.spot_state) > 0:
to_decay = sorted(range(len(self.spot_state)),
key=lambda k: self.spot_state[k]["life"],
reverse=True)[:deaths]
for idx in to_decay:
self.spot_state[idx]["target_life"] = 0.0
step = 1.0 / max(1, fade_ticks)
for sp in self.spot_state:
if sp["target_life"] > sp["life"]:
sp["life"] = min(sp["target_life"], sp["life"] + step)
elif sp["target_life"] < sp["life"]:
sp["life"] = max(sp["target_life"], sp["life"] - step)
self.spot_state = [sp for sp in self.spot_state if sp["life"] > 0.0 or sp["target_life"] > 0.0]
return target_intensity_base
def get_water_params(self, tick):
season_length_ticks = self.season_days * self.day_length
season_index = (tick // season_length_ticks) % len(self.seasons)
s = self.seasons[season_index]
season_tick = tick % season_length_ticks
season_progress = season_tick / season_length_ticks
next_season_index = (season_index + 1) % len(self.seasons)
base_curr = self._spot_base_params(season_index)
base_next = self._spot_base_params(next_season_index)
rain_intensity_base = self._update_spot_state(tick, base_curr, base_next, season_progress)
daily_cycle = 0.5 + 0.5 * math.sin(2 * math.pi * (tick % self.day_length) / self.day_length)
spots = []
for sp in self.spot_state:
phase = sp["phase"]
x = (sp["cx0"] + base_curr["vx"] * tick + base_curr["Ax"] * math.sin(2 * math.pi * tick / base_curr["Px"] + phase)) % self.n
y = (sp["cy0"] + base_curr["vy"] * tick + base_curr["Ay"] * math.cos(2 * math.pi * tick / base_curr["Py"] + phase)) % self.n
r_full = sp["base_radius"] + base_curr["radius_osc_amp"] * math.sin(2 * math.pi * tick / base_curr["Pr"] + phase)
r = max(0.0, r_full * sp["life"])
weight = 0.8 + 0.2 * math.sin(phase)
Ispot = rain_intensity_base * daily_cycle * weight * sp["life"]
spots.append({
"cx": x,
"cy": y,
"radius": r,
"edge_width": base_curr["edge_width"],
"intensity": Ispot,
})
season_name = s["name"] if season_progress <= 1.0 - self.transition_window else f"{s['name']}→{self.seasons[next_season_index]['name']}"
day_index = tick // self.day_length
return {
"season_index": season_index,
"season": season_name,
"rain_intensity_base": rain_intensity_base,
"daily_cycle": daily_cycle,
"spots": spots,
"day_index": day_index,
"spots_count": len(spots),
}
def _radial_energy(self, dx, dy, radius, edge, Imax):
dx = min(dx, self.n - dx)
dy = min(dy, self.n - dy)
d = math.sqrt(dx*dx + dy*dy)
if d <= radius:
return Imax
elif d <= radius + edge:
t = (d - radius) / max(1.0, edge)
return Imax * (1.0 - t)
else:
return 0.0
def apply_water_to_matrix(self, tick, combine="sum"):
params = self.get_water_params(tick)
spots = params["spots"]
for i in range(self.n):
for j in range(self.n):
acc = 0.0
mval = 0.0
for sp in spots:
dx = abs(j - sp["cx"])
dy = abs(i - sp["cy"])
e = self._radial_energy(dx, dy, sp["radius"], sp["edge_width"], sp["intensity"])
acc += e
mval = max(mval, e)
val = acc if combine == "sum" else mval
self.matrix.get_cell(i, j).contents[1].energy = min(1.0, val)
return params
Main → orchestrates execution and connects all scripts.
# main.py
# 8-11-2025
from matrix.matrix import Matrix
from vars.vars import matrix_path, message_js, time_energy_path, backup_dir, population_path
from time_energy.time_energy import TimeEnergy
import os
import time
# 11-11-2025
import json
import threading
from datetime import datetime
# 24-11-2025
from agents.population import Population
from vars.vars import message_js
def backup_data(m, te, p, lock):
os.makedirs(backup_dir, exist_ok=True)
suffix = datetime.now().strftime("%H_%M")
matrix_file = os.path.join(backup_dir, f"matrix_{suffix}.pkl")
te_file = os.path.join(backup_dir, f"timeenergy_{suffix}.pkl")
population_file = os.path.join(backup_dir, f"population_{suffix}.pkl")
with lock:
try:
m.save_backup(path=matrix_file)
te.save_backup(path=te_file)
p.save(path=population_file)
for link, target in [(matrix_path, matrix_file), (time_energy_path, te_file), (population_path, population_file)]:
if os.path.islink(link) or os.path.exists(link):
os.remove(link)
os.symlink(os.path.basename(target), link)
except Exception as e:
print(e)
def load_backup():
if os.path.exists(matrix_path):
try:
m = Matrix.load_backup()
except Exception as e:
m = Matrix()
else:
m = Matrix()
if os.path.exists(time_energy_path):
try:
te = TimeEnergy.load_backup(m)
except Exception as e:
te = TimeEnergy(m, tick=0)
else:
te = TimeEnergy(m, tick=0)
p = Population.load(m, population_path)
if id(p.matrix) != id(te.matrix) or id(p.matrix) != id(m) or id(m) != id(te.matrix):
m = p.matrix
te.matrix = p.matrix
return m, te, p
def loop_energy(m, te, lock, agentstore, message_js):
while True:
with lock:
tick = te.advance_tick()
sun_params = te.apply_sun_to_matrix(tick)
water_params = te.apply_water_to_matrix(tick, combine="sum")
m.save_params_json(tick, sun_params, water_params, agentstore, message_js)
time.sleep(1)
def loop_population(pop, lock):
while True:
with lock:
pop.agents.reset()
pop.fitness_agents()
time.sleep(1)
def main():
m, te, pop = load_backup()
lock = threading.Lock()
t0 = threading.Thread(target=loop_energy, args=(m, te, lock, pop.agents, message_js), daemon=True)
t0.start()
t1 = threading.Thread(target=loop_population, args=(pop, lock), daemon=True)
t1.start()
while True:
time.sleep(60)
backup_data(m, te, pop, lock)
if __name__ == "__main__":
main()
# vars/vars.py
# 8-11-2025
colors = {
0: {"id": 0, "desc": "SUN"},
1: {"id": 1, "desc": "WATER"},
}
matrix_path = "/home/bitnami/tconuno.online/backup/matrix.bk"
# 10-11-2025
message_js = "/opt/bitnami/wordpress/message_js.json"
# 11-11-2025
buffer_message_json = [
"/opt/bitnami/wordpress/message_jsA.json",
"/opt/bitnami/wordpress/message_jsB.json",
]
time_energy_path = "/home/bitnami/tconuno.online/backup/time_energy.bk"
backup_dir = "/home/bitnami/tconuno.online/backup/"
# 13-11-2025
void = 0
dead = 1
producer = 0
consumer = 1
nomad = 2
architect = 3
seed = 4
dead_st = 5
death_probs = {
producer: 0.01,
consumer: 0.01,
nomad: 0.01,
architect: 0.005,
}
# 21-11-2025
fitness_params = [
{"energy": 2, "speed": 1, "cost": 0.02, "seed_l": 1},
{"energy": 1, "speed": 4, "cost": 0.01, "seed_l": 4},
{"energy": 1, "speed": 3, "cost": 0.01, "seed_l": 4},
{"energy": 1, "speed": 2, "cost": 0.005, "seed_l": 2}
]
population_path = "/home/bitnami/tconuno.online/backup/population.bk"
# vars/fitness_map.py
# 23-11-2025
from agents.producer import Producer
from agents.consumer import Consumer
from agents.nomad import Nomad
from agents.architect import Architect
from vars.vars import producer, consumer, nomad, architect
fitness_map = {
producer: Producer,
consumer: Consumer,
nomad: Nomad,
architect: Architect
}
# ..
# 1-12-2025
class Joypad: #oracle
def __init__(self, name="t2"):
self.name = name
def sendCommand(self, pubkey, message, sign, command):
return True