|
1 | 1 | from mesa import Agent, Model |
2 | 2 | from mesa.space import MultiGrid |
3 | | -from mesa.time import RandomActivation |
4 | 3 |
|
5 | 4 |
|
6 | 5 | class EnergyAgent(Agent): |
7 | | - """ |
8 | | - Energy-based agent with a well-defined lifecycle. |
9 | | -
|
10 | | - Design guarantees: |
11 | | - - Energy is updated ONLY in step() |
12 | | - - Actions (move/rest) do not mutate energy |
13 | | - - Energy strictly decreases over time |
14 | | - - Agent is removed immediately when energy reaches zero |
15 | | -
|
16 | | - Invariant: |
17 | | - energy > 0 <=> agent.alive == True |
18 | | - """ |
19 | | - |
20 | 6 | # --- Tunable Parameters --- |
21 | 7 | LOW_ENERGY_THRESHOLD = 4 |
22 | 8 | MOVE_DECAY = 1.0 |
23 | 9 | REST_DECAY = 0.4 |
24 | 10 |
|
25 | | - def __init__(self, unique_id, model, energy=10): |
26 | | - super().__init__(unique_id, model) |
| 11 | + def __init__(self, model, energy=10): |
| 12 | + super().__init__(model) |
27 | 13 | self.energy = energy |
28 | 14 | self.alive = True |
29 | 15 |
|
30 | 16 | def step(self): |
31 | | - """Single authoritative lifecycle step.""" |
32 | | - |
33 | 17 | if not self.alive: |
34 | | - return # safety guard |
| 18 | + return |
35 | 19 |
|
36 | | - # --- Decision Phase --- |
37 | 20 | is_resting = self.energy < self.LOW_ENERGY_THRESHOLD |
38 | 21 |
|
39 | | - # --- Action Phase (pure, no energy mutation) --- |
40 | 22 | if is_resting: |
41 | 23 | self.rest() |
42 | 24 | decay = self.REST_DECAY |
43 | 25 | else: |
44 | 26 | self.move() |
45 | 27 | decay = self.MOVE_DECAY |
46 | 28 |
|
47 | | - # --- Energy Update --- |
48 | 29 | self.energy = max(self.energy - decay, 0) |
49 | 30 |
|
50 | | - # --- Immediate Death (no zombie tick) --- |
51 | 31 | if self.energy == 0: |
52 | 32 | self._die() |
53 | 33 |
|
54 | 34 | def move(self): |
55 | | - """Move to a random neighboring cell.""" |
56 | 35 | neighbors = self.model.grid.get_neighborhood( |
57 | 36 | self.pos, moore=True, include_center=False |
58 | 37 | ) |
59 | | - |
60 | 38 | if neighbors: |
61 | 39 | new_pos = self.random.choice(neighbors) |
62 | 40 | self.model.grid.move_agent(self, new_pos) |
63 | 41 |
|
64 | 42 | def rest(self): |
65 | | - """Agent remains stationary to conserve energy.""" |
| 43 | + pass |
66 | 44 |
|
67 | 45 | def _die(self): |
68 | | - """ |
69 | | - Safe removal: |
70 | | - - Marks agent as dead |
71 | | - - Removes from grid and scheduler |
72 | | - """ |
73 | 46 | self.alive = False |
74 | 47 | self.model.grid.remove_agent(self) |
75 | | - self.model.schedule.remove(self) |
| 48 | + self.remove() # deregisters from model AgentSet |
76 | 49 |
|
77 | 50 |
|
78 | 51 | class EnergyModel(Model): |
79 | | - """ |
80 | | - Model with agents that expend energy over time and eventually die. |
81 | | -
|
82 | | - Behavior: |
83 | | - - Agents move when energy is sufficient |
84 | | - - Agents rest when energy is low |
85 | | - - All agents eventually die due to energy depletion |
86 | | - """ |
87 | | - |
88 | 52 | def __init__(self, n=10, width=10, height=10): |
89 | 53 | super().__init__() |
90 | | - |
91 | 54 | self.num_agents = n |
92 | 55 | self.grid = MultiGrid(width, height, torus=True) |
93 | | - self.schedule = RandomActivation(self) |
94 | | - |
95 | | - # Create agents |
96 | | - for i in range(self.num_agents): |
97 | | - agent = EnergyAgent(i, self) |
98 | | - self.schedule.add(agent) |
99 | 56 |
|
| 57 | + for _ in range(self.num_agents): |
| 58 | + agent = EnergyAgent(self) |
100 | 59 | x = self.random.randrange(self.grid.width) |
101 | 60 | y = self.random.randrange(self.grid.height) |
102 | 61 | self.grid.place_agent(agent, (x, y)) |
103 | 62 |
|
104 | 63 | self.running = True |
105 | 64 |
|
106 | 65 | def step(self): |
107 | | - """Advance the model by one step.""" |
108 | | - self.schedule.step() |
| 66 | + # RandomActivation replacement in Mesa 3.x |
| 67 | + self.agents.shuffle_do("step") |
109 | 68 |
|
110 | | - # Stop simulation if no agents remain |
111 | | - if self.schedule.get_agent_count() == 0: |
| 69 | + if len(self.agents) == 0: |
112 | 70 | self.running = False |
0 commit comments