AW000 → AW001 → AW002 → AW003 → AW004 → AW005
In AW004, agents could use their memories when selecting future actions.
This notebook introduces a new limitation imposed by the artificial world itself.
What happens when two agents depend on a finite natural resource?
To answer this question, we construct a world in which two agents repeatedly seek access to one shared natural resource.
The resource is finite and non-renewable. Every successful access reduces the remaining stock.
The purpose is not yet to study production, regeneration, cooperation, competition, ownership, or trade.
Instead, we observe what happens when survival depends entirely on consuming a resource that can eventually be exhausted.
As in the previous notebooks, we begin by defining what exists before writing any Python code.
Compared with AW004, this notebook introduces a finite natural resource and makes survival dependent on energy.
This artificial world contains the following elements.
| Element | Description |
|---|---|
| Universe | The environment in which all events occur. |
| Agent #1 | The first agent seeking access to the natural resource. |
| Agent #2 | The second agent seeking access to the natural resource. |
| Energy | The internal state required for an agent to remain alive. |
| Memory | A record of each agent’s own actions and experiences. |
| Natural resource | A finite shared stock that can provide energy to the agents. |
| Resource access | An uncertain process through which an agent may obtain one unit of the resource. |
| Time | A sequence of discrete steps through which the world evolves. |
To answer the question, we construct an artificial world containing two agents and one shared natural resource.
In this implementation, each consumable unit of the natural resource is treated as one unit of food.
Each agent begins with a finite amount of energy. During each time step, a living agent attempts to access the resource.
Within each time step, Agent #1 acts first and Agent #2 acts second. This fixed update order is part of the present model.
Access is uncertain. When an attempt succeeds,
When an attempt fails,
Whether access succeeds or fails, the agent spends energy while continuing to exist.
An agent remains alive while its energy is greater than zero. When its energy reaches zero, it can no longer act.
The resource does not regenerate, and the agents cannot produce new resource.
By intentionally excluding regeneration and production, we isolate the consequences of consumption in a finite world.
The following diagram shows the static structure of AW005.
Both agents exist within the same universe and independently seek access to one finite natural resource. Successful access transfers one unit of resource from the shared stock to an agent and increases that agent’s energy.
The diagram shows what exists and how the elements are connected. Changes through time are presented later in the simulation history and figures.
The following diagram illustrates the ontology introduced above and the relationships among the entities in this artificial world.
This notebook was developed and tested in the following environment.
import platform
import sys
from pathlib import Path
def get_operating_system():
"""
Return the Linux distribution name when /etc/os-release exists.
Otherwise, return the general platform description.
"""
os_release_path = Path("/etc/os-release")
if os_release_path.exists():
os_information = {}
with os_release_path.open(
mode="r",
encoding="utf-8"
) as os_release_file:
for line in os_release_file:
key, separator, value = line.strip().partition("=")
if separator:
os_information[key] = value.strip('"')
return os_information.get(
"PRETTY_NAME",
platform.platform()
)
return platform.platform()
print(f"Operating system: {get_operating_system()}")
print(f"Kernel: {platform.release()}")
print(f"Python version: {sys.version.split()[0]}")
Operating system: Rocky Linux 9.8 (Blue Onyx) Kernel: 5.14.0-687.26.1.el9_8.x86_64 Python version: 3.9.25
The artificial world is implemented in several small parts. Each part corresponds to one element or process introduced above.
The simulation and its outputs use four Python libraries.
random introduces reproducible uncertainty.csv writes the simulation history to a data file.Path manages output directories and filenames.matplotlib.pyplot visualizes the history.The model parameters are then collected in one place. Changing these values creates a different experiment without changing the structure of the program.
import csv
import random
from pathlib import Path
import matplotlib
import matplotlib.pyplot as plt
print(f"Matplotlib version: {matplotlib.__version__}")
Matplotlib version: 3.9.4
INITIAL_RESOURCE = 20
AGENT_1_INITIAL_ENERGY = 3
AGENT_2_INITIAL_ENERGY = 3
FOOD_ENERGY = 3
ENERGY_COST_PER_STEP = 1
ACCESS_PROBABILITY = 0.35
CONSUMPTION_PER_SUCCESS = 1
RANDOM_SEED = 7
MAXIMUM_TIME = 200
parameters = {
"Initial resource": INITIAL_RESOURCE,
"Agent 1 initial energy": AGENT_1_INITIAL_ENERGY,
"Agent 2 initial energy": AGENT_2_INITIAL_ENERGY,
"Food energy": FOOD_ENERGY,
"Energy cost per step": ENERGY_COST_PER_STEP,
"Access probability": ACCESS_PROBABILITY,
"Consumption per success": CONSUMPTION_PER_SUCCESS,
"Random seed": RANDOM_SEED,
"Maximum time": MAXIMUM_TIME,
}
The Agent class defines what every agent possesses and what every agent can do.
Each agent has
The methods of the class describe one cycle of behavior: observe the resource, choose an action, attempt access, update memory, and spend energy.
class Agent:
def __init__(
self,
name,
initial_energy,
energy_cost_per_step
):
self.name = name
self.energy = initial_energy
self.energy_cost_per_step = energy_cost_per_step
self.alive = True
self.memory = []
def observe(self, world):
"""
The agent observes its own energy
and the remaining natural resource.
"""
return {
"energy": self.energy,
"resource_remaining": world.resource_remaining
}
def choose_action(self, observation):
"""
The agent seeks food whenever some natural resource remains.
"""
if observation["resource_remaining"] > 0:
return "seek_food"
return "wait"
def act(self, action, world):
"""
Carry out the chosen action.
"""
if action == "seek_food":
self.seek_food(world)
elif action == "wait":
self.wait()
def seek_food(self, world):
"""
The agent tries to access the shared natural resource.
If access succeeds:
- some natural resource is consumed;
- the agent gains energy.
"""
food_obtained = world.access_natural_resource()
if food_obtained:
self.energy += world.food_energy
result = (
f"{self.name} accessed the natural resource, "
f"consumed {world.consumption_per_success} unit(s), "
f"and gained {world.food_energy} energy."
)
else:
if world.resource_remaining <= 0:
result = (
f"{self.name} sought food, "
"but the natural resource had dried up."
)
else:
result = (
f"{self.name} sought food "
"but failed to access the resource."
)
self.memory.append(result)
def wait(self):
"""
The agent waits after the resource has dried up.
"""
self.memory.append(
f"{self.name} waited because no natural resource remained."
)
def live_one_step(self):
"""
Existing for one time step consumes energy.
"""
self.energy -= self.energy_cost_per_step
if self.energy <= 0:
self.energy = 0
self.alive = False
The class definition does not create an agent yet. It defines the common structure from which Agent #1 and Agent #2 will later be created.
The World class stores the conditions shared by both agents.
It contains
Unlike an agent, the world does not choose an action. It provides the environment in which the agents act.
class World:
def __init__(
self,
initial_resource,
food_energy,
access_probability,
consumption_per_success,
random_seed
):
self.initial_resource = initial_resource
self.resource_remaining = initial_resource
self.food_energy = food_energy
self.access_probability = access_probability
self.consumption_per_success = consumption_per_success
self.random_seed = random_seed
self.time = 0
# A private random-number generator makes
# the history reproducible.
self.rng = random.Random(random_seed)
def access_natural_resource(self):
"""
An agent attempts to access the shared natural resource.
Successful access requires:
1. enough resource remaining;
2. a random draw below the access probability.
A successful access reduces the resource stock.
"""
if self.resource_remaining < self.consumption_per_success:
return False
random_draw = self.rng.random()
if random_draw < self.access_probability:
self.resource_remaining -= self.consumption_per_success
return True
return False
def advance_time(self):
self.time += 1
The following functions print the state of the world in a readable form.
They do not alter any agent or resource. Their only role is to make the simulation visible to the reader.
def print_initial_state(world, agents):
"""
Display the state of the universe at time 0.
"""
print("INITIAL CONDITION")
print(f"Time: {world.time}")
print(f"Resource remaining: {world.resource_remaining}")
for agent in agents:
print(f"{agent.name} energy: {agent.energy}")
print(f"{agent.name} alive: {agent.alive}")
print(f"{agent.name} memory: {agent.memory}")
print("-" * 40)
def print_time_step(world, agents, step_records):
"""
Display the state reached after one completed time step.
"""
print(f"Time: {world.time}")
print(f"Resource remaining: {world.resource_remaining}")
for agent in agents:
print(f"{agent.name} energy: {agent.energy}")
print(f"{agent.name} alive: {agent.alive}")
print(f"{agent.name} action: {step_records[agent.name]}")
print("-" * 40)
Printed output is useful for following events, but it is not convenient for later analysis.
The record_history() function therefore stores the state of the world at every time step in a structured dictionary. This history later becomes the source for both the CSV file and the graph.
def record_history(history, world, agents):
"""
Record the complete state of the universe
at the current time.
"""
history["time"].append(world.time)
history["resource"].append(world.resource_remaining)
for agent in agents:
history["energy"][agent.name].append(agent.energy)
history["alive"][agent.name].append(agent.alive)
The recorded history allows us to identify important moments without inspecting every printed line manually.
The following functions locate
def find_resource_exhaustion_time(history):
"""
Return the first time at which the natural resource
reaches zero.
Return None if it never reaches zero.
"""
for time, resource in zip(
history["time"],
history["resource"]
):
if resource <= 0:
return time
return None
def find_death_time(history, agent_name):
"""
Return the first time at which the agent is no longer alive.
"""
for time, alive in zip(
history["time"],
history["alive"][agent_name]
):
if not alive:
return time
return None
The simulation history is converted into rows and written to a CSV file.
Each row represents one time step. The columns record the remaining resource, the energy of both agents, and whether each agent is alive.
def save_history_to_csv(
history,
agents,
random_seed,
output_directory="csv"
):
"""
Save the complete simulation history as a CSV file.
Each row represents the state of the universe
at one recorded time.
"""
output_directory = Path(output_directory)
# Create the directory if it does not already exist.
output_directory.mkdir(
parents=True,
exist_ok=True
)
output_filename = (
f"AW005_history_seed_{random_seed}.csv"
)
output_path = (
output_directory
/ output_filename
)
fieldnames = [
"time",
"resource_remaining"
]
for agent in agents:
agent_key = (
agent.name
.lower()
.replace(" ", "_")
)
fieldnames.extend([
f"{agent_key}_energy",
f"{agent_key}_alive"
])
with output_path.open(
mode="w",
newline="",
encoding="utf-8"
) as csv_file:
writer = csv.DictWriter(
csv_file,
fieldnames=fieldnames
)
writer.writeheader()
number_of_records = len(
history["time"]
)
for index in range(number_of_records):
row = {
"time":
history["time"][index],
"resource_remaining":
history["resource"][index]
}
for agent in agents:
agent_key = (
agent.name
.lower()
.replace(" ", "_")
)
row[
f"{agent_key}_energy"
] = history["energy"][
agent.name
][index]
row[
f"{agent_key}_alive"
] = history["alive"][
agent.name
][index]
writer.writerow(row)
print(
f"History saved as: "
f"{output_path}"
)
return output_path
The graph provides a compact view of the same history stored in the CSV file.
The upper panel follows the energy of the two agents. The lower panel follows the remaining natural resource. Vertical reference lines identify the key events found by the preceding functions.
def plot_history(history, parameters, agents):
"""
Visualize:
1. the energy of both agents;
2. the remaining natural resource;
3. the parameter values used in the simulation.
"""
exhaustion_time = find_resource_exhaustion_time(history)
figure, axes = plt.subplots(
nrows=2,
ncols=1,
figsize=(12, 8),
sharex=True
)
energy_axis = axes[0]
resource_axis = axes[1]
# --------------------------------------------------
# Upper graph: agent energies
# --------------------------------------------------
line_styles = ["-", "--", ":", "-."]
for index, agent in enumerate(agents):
energy_axis.plot(
history["time"],
history["energy"][agent.name],
drawstyle="steps-post",
linestyle=line_styles[index % len(line_styles)],
linewidth=2,
label=f"{agent.name} energy"
)
death_time = find_death_time(history, agent.name)
if death_time is not None:
energy_axis.axvline(
death_time,
linestyle=":",
linewidth=1
)
energy_axis.text(
death_time,
0,
f" {agent.name} terminated at t={death_time}",
rotation=90,
verticalalignment="bottom"
)
if exhaustion_time is not None:
energy_axis.axvline(
exhaustion_time,
linestyle="--",
linewidth=1.5,
label=f"Resource exhausted at t={exhaustion_time}"
)
energy_axis.set_title(
"History of Two Agents in a Finite Natural World"
)
energy_axis.set_ylabel("Energy")
energy_axis.grid(True, alpha=0.3)
energy_axis.legend()
# --------------------------------------------------
# Lower graph: natural-resource stock
# --------------------------------------------------
resource_axis.plot(
history["time"],
history["resource"],
drawstyle="steps-post",
linewidth=2,
label="Natural resource remaining"
)
if exhaustion_time is not None:
resource_axis.axvline(
exhaustion_time,
linestyle="--",
linewidth=1.5
)
resource_axis.text(
exhaustion_time,
0,
f" Resource exhausted at t={exhaustion_time}",
rotation=90,
verticalalignment="bottom"
)
resource_axis.set_xlabel("Time")
resource_axis.set_ylabel("Resource remaining")
resource_axis.grid(True, alpha=0.3)
resource_axis.legend()
# --------------------------------------------------
# Parameter list
# --------------------------------------------------
parameter_lines = ["PARAMETERS"]
for parameter_name, parameter_value in parameters.items():
parameter_lines.append(
f"{parameter_name}: {parameter_value}"
)
parameter_text = "\n".join(parameter_lines)
figure.text(
0.77,
0.50,
parameter_text,
verticalalignment="center",
family="monospace",
fontsize=10,
bbox={
"boxstyle": "round",
"alpha": 0.1
}
)
figure.tight_layout(rect=[0.0, 0.0, 0.74, 1.0])
output_directory = Path("image")
output_directory.mkdir(
parents=True,
exist_ok=True
)
output_filename = (output_directory/ "AW005_Fig02_History.png")
figure.savefig(
output_filename,
dpi=300,
bbox_inches="tight",
)
print(f"Graph saved as: {output_filename}")
plt.show()
We run the simulation once using a fixed random seed so that the experiment can be reproduced.
| Parameter | Meaning | Value |
|---|---|---|
| Random seed | Reproduces the same sequence of random events | 7 |
| Number of agents | Agents present in the world | 2 |
| Initial energy | Energy held by each agent at time 0 | 3 |
| Initial resource stock | Resource available at time 0 | 20 |
| Access probability | Probability that one access attempt succeeds | 0.35 |
| Energy gained | Energy added after successful access | 3 |
| Energy cost | Energy spent by a living agent during one time step | 1 |
| Resource consumed | Resource removed after successful access | 1 |
| Maximum time | Safety limit on simulation length | 200 |
world = World(
initial_resource=INITIAL_RESOURCE,
food_energy=FOOD_ENERGY,
access_probability=ACCESS_PROBABILITY,
consumption_per_success=CONSUMPTION_PER_SUCCESS,
random_seed=RANDOM_SEED,
)
agent_1 = Agent(
name="Agent 1",
initial_energy=AGENT_1_INITIAL_ENERGY,
energy_cost_per_step=ENERGY_COST_PER_STEP,
)
agent_2 = Agent(
name="Agent 2",
initial_energy=AGENT_2_INITIAL_ENERGY,
energy_cost_per_step=ENERGY_COST_PER_STEP,
)
agents = [agent_1, agent_2]
history = {
"time": [],
"resource": [],
"energy": {
agent.name: []
for agent in agents
},
"alive": {
agent.name: []
for agent in agents
},
}
print_initial_state(world, agents)
record_history(
history=history,
world=world,
agents=agents,
)
INITIAL CONDITION Time: 0 Resource remaining: 20 Agent 1 energy: 3 Agent 1 alive: True Agent 1 memory: [] Agent 2 energy: 3 Agent 2 alive: True Agent 2 memory: [] ----------------------------------------
while (
any(agent.alive for agent in agents)
and world.time < MAXIMUM_TIME
):
step_records = {}
for agent in agents:
if not agent.alive:
step_records[agent.name] = (
"none — agent no longer alive"
)
continue
observation = agent.observe(world)
action = agent.choose_action(observation)
agent.act(
action=action,
world=world
)
agent.live_one_step()
step_records[agent.name] = action
world.advance_time()
# Record the world after the completed time step.
record_history(
history=history,
world=world,
agents=agents
)
print_time_step(
world=world,
step_records=step_records,
agents=agents
)
Time: 1 Resource remaining: 18 Agent 1 energy: 5 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 5 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 2 Resource remaining: 17 Agent 1 energy: 4 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 7 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 3 Resource remaining: 17 Agent 1 energy: 3 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 6 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 4 Resource remaining: 16 Agent 1 energy: 5 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 5 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 5 Resource remaining: 15 Agent 1 energy: 7 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 4 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 6 Resource remaining: 13 Agent 1 energy: 9 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 6 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 7 Resource remaining: 13 Agent 1 energy: 8 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 5 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 8 Resource remaining: 11 Agent 1 energy: 10 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 7 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 9 Resource remaining: 11 Agent 1 energy: 9 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 6 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 10 Resource remaining: 11 Agent 1 energy: 8 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 5 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 11 Resource remaining: 10 Agent 1 energy: 7 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 7 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 12 Resource remaining: 9 Agent 1 energy: 6 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 9 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 13 Resource remaining: 7 Agent 1 energy: 8 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 11 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 14 Resource remaining: 6 Agent 1 energy: 10 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 10 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 15 Resource remaining: 5 Agent 1 energy: 12 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 9 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 16 Resource remaining: 5 Agent 1 energy: 11 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 8 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 17 Resource remaining: 4 Agent 1 energy: 10 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 10 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 18 Resource remaining: 2 Agent 1 energy: 12 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 12 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 19 Resource remaining: 2 Agent 1 energy: 11 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 11 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 20 Resource remaining: 1 Agent 1 energy: 13 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 10 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 21 Resource remaining: 0 Agent 1 energy: 12 Agent 1 alive: True Agent 1 action: seek_food Agent 2 energy: 12 Agent 2 alive: True Agent 2 action: seek_food ---------------------------------------- Time: 22 Resource remaining: 0 Agent 1 energy: 11 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 11 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 23 Resource remaining: 0 Agent 1 energy: 10 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 10 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 24 Resource remaining: 0 Agent 1 energy: 9 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 9 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 25 Resource remaining: 0 Agent 1 energy: 8 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 8 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 26 Resource remaining: 0 Agent 1 energy: 7 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 7 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 27 Resource remaining: 0 Agent 1 energy: 6 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 6 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 28 Resource remaining: 0 Agent 1 energy: 5 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 5 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 29 Resource remaining: 0 Agent 1 energy: 4 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 4 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 30 Resource remaining: 0 Agent 1 energy: 3 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 3 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 31 Resource remaining: 0 Agent 1 energy: 2 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 2 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 32 Resource remaining: 0 Agent 1 energy: 1 Agent 1 alive: True Agent 1 action: wait Agent 2 energy: 1 Agent 2 alive: True Agent 2 action: wait ---------------------------------------- Time: 33 Resource remaining: 0 Agent 1 energy: 0 Agent 1 alive: False Agent 1 action: wait Agent 2 energy: 0 Agent 2 alive: False Agent 2 action: wait ----------------------------------------
The simulation produces three complementary records:
Together, these records allow the experiment to be inspected and reproduced.
The following figure summarizes the evolution of the agents’ energy and the remaining natural resource during the simulation.
plot_history(
history=history,
parameters=parameters,
agents=agents,
)
Graph saved as: image/AW005_Fig02_History.png
history_csv_path = save_history_to_csv(
history=history,
agents=agents,
random_seed=RANDOM_SEED,
output_directory="csv",
)
History saved as: csv/AW005_history_seed_7.csv
print("Exists:", history_csv_path.exists())
print("Path:", history_csv_path)
print("Size:", history_csv_path.stat().st_size, "bytes")
Exists: True Path: csv/AW005_history_seed_7.csv Size: 787 bytes
with history_csv_path.open(
mode="r",
encoding="utf-8",
) as csv_file:
for line_number, line in enumerate(csv_file):
print(line.rstrip())
if line_number >= 5:
break
time,resource_remaining,agent_1_energy,agent_1_alive,agent_2_energy,agent_2_alive 0,20,3,True,3,True 1,18,5,True,5,True 2,17,4,True,7,True 3,17,3,True,6,True 4,16,5,True,5,True
In this run, the finite natural resource gradually decreases as the agents consume it.
The resource reaches zero at time t = 21.
The agents do not disappear immediately after the resource is exhausted because they still possess stored energy. Their energy then declines until
The experiment therefore separates two events:
Because access is uncertain, the exact history depends on the random sequence. The fixed seed makes this particular history reproducible.
A finite initial resource can support the agents only temporarily when the world contains consumption but no regeneration or production.
Uncertain access changes the path followed by the system, but it does not remove the underlying constraint: once the shared resource is exhausted, no new energy can enter the agents from the environment.
The agents may survive for a limited time using stored energy, but both eventually cease to exist.
AW005 therefore reaches a natural endpoint and raises the next question:
Can a consumed resource return?