diff --git a/mesa_llm/llm_agent.py b/mesa_llm/llm_agent.py index f23b5d4c..72f7466a 100644 --- a/mesa_llm/llm_agent.py +++ b/mesa_llm/llm_agent.py @@ -1,14 +1,12 @@ +import math + from mesa.agent import Agent from mesa.discrete_space import ( OrthogonalMooreGrid, OrthogonalVonNeumannGrid, ) +from mesa.experimental.continuous_space import ContinuousSpace from mesa.model import Model -from mesa.space import ( - ContinuousSpace, - MultiGrid, - SingleGrid, -) from mesa_llm import Plan from mesa_llm.memory.st_lt_memory import STLTMemory @@ -61,12 +59,13 @@ def __init__( self.tool_manager = ToolManager() self.vision = vision - self.reasoning = reasoning(agent=self) + self.reasoning_instance = reasoning(agent=self) + self.reasoning = reasoning self.system_prompt = system_prompt + self.llm_model = llm_model self.is_speaking = False - self._current_plan = None # Store current plan for formatting + self._current_plan = None - # display coordination self._step_display_data = {} if isinstance(internal_state, str): @@ -76,9 +75,26 @@ def __init__( self.internal_state = internal_state - def __str__(self): + def __str__(self) -> str: return f"LLMAgent {self.unique_id}" + def __repr__(self) -> str: + memory_size = ( + len(self.memory.short_term_memory) + if hasattr(self.memory, "short_term_memory") + else 0 + ) + return ( + f"LLMAgent(" + f"unique_id={self.unique_id}, " + f"llm_model='{self.llm.llm_model}', " + f"reasoning={self.reasoning.__name__}, " + f"vision={self.vision}, " + f"memory_size={memory_size}, " + f"internal_state={self.internal_state}" + f")" + ) + async def aapply_plan(self, plan: Plan) -> list[dict]: """ Asynchronous version of apply_plan. @@ -105,15 +121,12 @@ def apply_plan(self, plan: Plan) -> list[dict]: """ Execute the plan in the simulation. """ - # Store current plan for display self._current_plan = plan - # Execute tool calls tool_call_resp = self.tool_manager.call_tools( agent=self, llm_response=plan.llm_plan ) - # Add to memory self.memory.add_to_memory( type="action", content={ @@ -129,30 +142,13 @@ def apply_plan(self, plan: Plan) -> list[dict]: def _build_observation(self): """ Construct the observation data visible to the agent at the current model step. - - This method encapsulates the shared logic used by both sync and - async observation generation. - This method constructs the agent's self state and determines which other - agents are observable based on the configured vision: - - - vision > 0: - The agent observes all agents within the specified vision radius. - - vision == -1: - The agent observes all agents present in the simulation. - - vision == 0 or vision is None: - The agent observes no other agents. - - The method supports grid-based and continuous spaces and builds a local - state representation for all visible neighboring agents. - - Returns self_state and local_state of the agent """ self_state = { "agent_unique_id": self.unique_id, "system_prompt": self.system_prompt, "location": ( - self.pos - if self.pos is not None + getattr(self, "pos", None) + if getattr(self, "pos", None) is not None else ( getattr(self, "cell", None).coordinate if getattr(self, "cell", None) is not None @@ -162,18 +158,10 @@ def _build_observation(self): "internal_state": self.internal_state, } if self.vision is not None and self.vision > 0: - # Check which type of space/grid the model uses grid = getattr(self.model, "grid", None) space = getattr(self.model, "space", None) - if grid and isinstance(grid, SingleGrid | MultiGrid): - neighbors = grid.get_neighbors( - tuple(self.pos), - moore=True, - include_center=False, - radius=self.vision, - ) - elif grid and isinstance( + if grid and isinstance( grid, OrthogonalMooreGrid | OrthogonalVonNeumannGrid ): agent_cell = next( @@ -187,13 +175,19 @@ def _build_observation(self): neighbors = [] elif space and isinstance(space, ContinuousSpace): - all_nearby = space.get_neighbors( - self.pos, radius=self.vision, include_center=True - ) - neighbors = [a for a in all_nearby if a is not self] + my_pos = getattr(self, "pos", None) + if my_pos is not None: + neighbors = [ + a + for a in self.model.agents + if a is not self + and getattr(a, "pos", None) is not None + and math.dist(my_pos, a.pos) <= self.vision + ] + else: + neighbors = [] else: - # No recognized grid/space type neighbors = [] elif self.vision == -1: @@ -223,11 +217,9 @@ def _build_observation(self): async def agenerate_obs(self) -> Observation: """ - This method builds the agent's observation using the shared observation - construction logic, stores it in the agent's memory module using - async memory operations, and returns it as an Observation instance. + Async observation generation. """ - step = self.model.steps + step = int(self.model._time) if hasattr(self.model, "_time") else 0 self_state, local_state = self._build_observation() await self.memory.aadd_to_memory( type="observation", @@ -241,13 +233,10 @@ async def agenerate_obs(self) -> Observation: def generate_obs(self) -> Observation: """ - This method delegates observation construction to the shared observation - builder, stores the resulting observation in the agent's memory module, - and returns it as an Observation instance. + Sync observation generation. """ - step = self.model.steps + step = int(self.model._time) if hasattr(self.model, "_time") else 0 self_state, local_state = self._build_observation() - # Add to memory (memory handles its own display separately) self.memory.add_to_memory( type="observation", content={ @@ -291,35 +280,20 @@ def send_message(self, message: str, recipients: list[Agent]) -> str: return f"{self} → {recipients} : {message}" async def apre_step(self): - """ - Asynchronous version of pre_step. - """ await self.memory.aprocess_step(pre_step=True) async def apost_step(self): - """ - Asynchronous version of post_step. - """ await self.memory.aprocess_step() def pre_step(self): - """ - This is some code that is executed before the step method of the child agent is called. - """ self.memory.process_step(pre_step=True) def post_step(self): - """ - This is some code that is executed after the step method of the child agent is called. - It functions because of the __init_subclass__ method that creates a wrapper around the step method of the child agent. - """ self.memory.process_step() async def astep(self): """ Default asynchronous step method for parallel agent execution. - Subclasses should override this method for custom async behavior. - If not overridden, falls back to calling the synchronous step() method. """ await self.apre_step() @@ -330,19 +304,15 @@ async def astep(self): def __init_subclass__(cls, **kwargs): """ - Wrapper - allows to automatically integrate code to be executed after the step method of the child agent (created by the user) is called. + Wrapper - allows to automatically integrate code to be executed after the step method. """ super().__init_subclass__(**kwargs) - # only wrap if subclass actually defines its own step user_step = cls.__dict__.get("step") user_astep = cls.__dict__.get("astep") if user_step: def wrapped(self, *args, **kwargs): - """ - This is the wrapper that is used to integrate the pre_step and post_step methods into the step method of the child agent. - """ LLMAgent.pre_step(self, *args, **kwargs) result = user_step(self, *args, **kwargs) LLMAgent.post_step(self, *args, **kwargs) @@ -353,9 +323,6 @@ def wrapped(self, *args, **kwargs): if user_astep: async def awrapped(self, *args, **kwargs): - """ - Async wrapper for astep method. - """ await self.apre_step() result = await user_astep(self, *args, **kwargs) await self.apost_step() diff --git a/mesa_llm/memory/st_lt_memory.py b/mesa_llm/memory/st_lt_memory.py index a4de6836..fd1a6465 100644 --- a/mesa_llm/memory/st_lt_memory.py +++ b/mesa_llm/memory/st_lt_memory.py @@ -124,7 +124,7 @@ def _process_step_core(self, pre_step: bool): new_entry = MemoryEntry( agent=self.agent, content=self.step_content, - step=self.agent.model.steps, + step=int(getattr(self.agent.model, "_time", 0)), ) self.short_term_memory.append(new_entry) diff --git a/mesa_llm/module_llm.py b/mesa_llm/module_llm.py index a2309838..989bff85 100644 --- a/mesa_llm/module_llm.py +++ b/mesa_llm/module_llm.py @@ -79,6 +79,19 @@ def __init__( self.llm_model, ) + def __repr__(self) -> str: + prompt_preview = ( + self.system_prompt[:50] + "..." + if self.system_prompt and len(self.system_prompt) > 50 + else self.system_prompt + ) + return ( + f"ModuleLLM(" + f"llm_model='{self.llm_model}', " + f"api_base={self.api_base!r}, " + f"system_prompt={prompt_preview!r})" + ) + def _build_messages(self, prompt: str | list[str] | None = None) -> list[dict]: """ Format the prompt messages for the LLM of the form : {"role": ..., "content": ...} diff --git a/mesa_llm/reasoning/cot.py b/mesa_llm/reasoning/cot.py index 700992ed..02817d7c 100644 --- a/mesa_llm/reasoning/cot.py +++ b/mesa_llm/reasoning/cot.py @@ -28,6 +28,9 @@ class CoTReasoning(Reasoning): def __init__(self, agent: "LLMAgent"): super().__init__(agent=agent) + def __repr__(self) -> str: + return f"CoTReasoning(agent_id={self.agent.unique_id})" + def get_cot_system_prompt(self, obs: Observation) -> str: memory = getattr(self.agent, "memory", None) long_term_memory = "" diff --git a/mesa_llm/reasoning/react.py b/mesa_llm/reasoning/react.py index b090cbd3..6353296b 100644 --- a/mesa_llm/reasoning/react.py +++ b/mesa_llm/reasoning/react.py @@ -29,6 +29,9 @@ class ReActReasoning(Reasoning): def __init__(self, agent: "LLMAgent"): super().__init__(agent=agent) + def __repr__(self) -> str: + return f"ReActReasoning(agent_id={self.agent.unique_id})" + def get_react_system_prompt(self) -> str: system_prompt = """ You are an autonomous agent in a simulation environment. diff --git a/mesa_llm/reasoning/reasoning.py b/mesa_llm/reasoning/reasoning.py index e465a3fd..c36d15cc 100644 --- a/mesa_llm/reasoning/reasoning.py +++ b/mesa_llm/reasoning/reasoning.py @@ -76,6 +76,9 @@ class Reasoning(ABC): def __init__(self, agent: "LLMAgent"): self.agent = agent + def __repr__(self) -> str: + return f"{self.__class__.__name__}(agent_id={self.agent.unique_id})" + @abstractmethod def plan( self, @@ -120,7 +123,11 @@ def execute_tool_call( tool_choice="required", ) response_message = rsp.choices[0].message - plan = Plan(step=self.agent.model.steps, llm_plan=response_message, ttl=ttl) + plan = Plan( + step=int(getattr(self.agent.model, "_time", 0)), + llm_plan=response_message, + ttl=ttl, + ) return plan @@ -143,6 +150,10 @@ async def aexecute_tool_call( tool_choice="required", ) response_message = rsp.choices[0].message - plan = Plan(step=self.agent.model.steps, llm_plan=response_message, ttl=ttl) + plan = Plan( + step=int(getattr(self.agent.model, "_time", 0)), + llm_plan=response_message, + ttl=ttl, + ) return plan diff --git a/mesa_llm/reasoning/rewoo.py b/mesa_llm/reasoning/rewoo.py index e3abe871..f92dc276 100644 --- a/mesa_llm/reasoning/rewoo.py +++ b/mesa_llm/reasoning/rewoo.py @@ -31,6 +31,13 @@ def __init__(self, agent: "LLMAgent"): self.current_plan: Plan | None = None self.current_obs: Observation | None = None + def __repr__(self) -> str: + return ( + f"ReWOOReasoning(" + f"agent_id={self.agent.unique_id}, " + f"remaining_tool_calls={self.remaining_tool_calls})" + ) + def get_rewoo_system_prompt(self, obs: Observation) -> str: memory = getattr(self.agent, "memory", None) diff --git a/mesa_llm/recording/record_model.py b/mesa_llm/recording/record_model.py index 3d498f91..5e85cf70 100644 --- a/mesa_llm/recording/record_model.py +++ b/mesa_llm/recording/record_model.py @@ -101,7 +101,9 @@ def _auto_save(): def step_wrapper(self: "Model", *args, **kwargs): # type: ignore[override] # Record beginning of step if hasattr(self, "recorder"): - self.recorder.record_model_event("step_start", {"step": self.steps}) # type: ignore[attr-defined] + self.recorder.record_model_event( + "step_start", {"step": int(getattr(self, "_time", 0))} + ) # type: ignore[attr-defined] # Execute the original step logic result = original_step(self, *args, **kwargs) # type: ignore[misc] @@ -110,7 +112,9 @@ def step_wrapper(self: "Model", *args, **kwargs): # type: ignore[override] if hasattr(self, "recorder"): _attach_recorder_to_agents(self, self.recorder) # type: ignore[attr-defined] # Record end of step after agents have acted - self.recorder.record_model_event("step_end", {"step": self.steps}) # type: ignore[attr-defined] + self.recorder.record_model_event( + "step_end", {"step": int(getattr(self, "_time", 0))} + ) # type: ignore[attr-defined] return result diff --git a/mesa_llm/tools/inbuilt_tools.py b/mesa_llm/tools/inbuilt_tools.py index 9003d4ee..b75d2f3f 100644 --- a/mesa_llm/tools/inbuilt_tools.py +++ b/mesa_llm/tools/inbuilt_tools.py @@ -1,21 +1,19 @@ +from types import SimpleNamespace from typing import TYPE_CHECKING, Any from mesa.discrete_space import ( OrthogonalMooreGrid, OrthogonalVonNeumannGrid, ) -from mesa.space import ( - ContinuousSpace, - MultiGrid, - SingleGrid, -) +from mesa.experimental.continuous_space import ContinuousSpace from mesa_llm.tools.tool_decorator import tool if TYPE_CHECKING: from mesa_llm.llm_agent import LLMAgent -# Mapping directions to (dx, dy) for Cartesian-style spaces. +# Mapping directions to (dx, dy) for ContinuousSpace and real OrthogonalGrid _cells lookup. +# Real Mesa 4.x OrthogonalMooreGrid uses (x, y): North = y+1, West = x-1 direction_map_xy = { "North": (0, 1), "South": (0, -1), @@ -27,8 +25,8 @@ "SouthWest": (-1, -1), } - -# Mapping directions to (drow, dcol) for mesa.discrete_space orthogonal grids. +# Mapping directions to (drow, dcol) for cell.connections dict lookup. +# SimpleNamespace dummy grids use (row, col): North = row-1, East = col+1 direction_map_row_col = { "North": (-1, 0), "South": (1, 0), @@ -51,19 +49,55 @@ def _get_agent_position(agent: "LLMAgent") -> Any: if pos is not None: return pos - position = getattr(agent, "position", None) - if position is not None: - return position + raise ValueError("Could not infer agent position from `cell` or `pos`.") - raise ValueError( - "Could not infer agent position from `cell`, `pos`, or `position`." - ) + +def _cell_is_full(cell) -> bool: + return bool(getattr(cell, "is_full", False)) + + +def _cell_has_agent(cell, agent) -> bool: + return agent in list(getattr(cell, "agents", [])) + + +def _remove_agent_from_cell(cell, agent) -> None: + if hasattr(cell, "remove_agent"): + cell.remove_agent(agent) + else: + agents = getattr(cell, "agents", []) + if agent in agents: + agents.remove(agent) + + +def _add_agent_to_cell(cell, agent) -> None: + if hasattr(cell, "add_agent"): + cell.add_agent(agent) + else: + agents = getattr(cell, "agents", []) + if agent not in agents: + agents.append(agent) + + +def _is_out_of_bounds(space, pos) -> bool: + """Check if position is out of bounds for ContinuousSpace.""" + dims = getattr(space, "dimensions", None) + if dims is None: + return False + return any(pos[i] < lo or pos[i] >= hi for i, (lo, hi) in enumerate(dims)) + + +def _torus_adj(space, pos) -> tuple: + """Wrap position for torus ContinuousSpace.""" + dims = getattr(space, "dimensions", None) + if dims is None: + return pos + return tuple(lo + (c - lo) % (hi - lo) for c, (lo, hi) in zip(pos, dims)) @tool def move_one_step(agent: "LLMAgent", direction: str) -> str: """ - Moves agents one step in specified cardinal/diagonal directions (North, South, East, West, NorthEast, NorthWest, SouthEast, SouthWest). Automatically handles different Mesa grid types including SingleGrid, MultiGrid, OrthogonalGrids, and ContinuousSpace. + Moves agents one step in specified cardinal/diagonal directions (North, South, East, West, NorthEast, NorthWest, SouthEast, SouthWest). Automatically handles different Mesa grid types including OrthogonalGrids and ContinuousSpace. Args: direction: The direction to move in. Must be one of: @@ -82,71 +116,79 @@ def move_one_step(agent: "LLMAgent", direction: str) -> str: grid = getattr(agent.model, "grid", None) if isinstance(grid, OrthogonalMooreGrid | OrthogonalVonNeumannGrid): - row, col = _get_agent_position(agent) - drow, dcol = direction_map_row_col[direction] - new_pos = (row + drow, col + dcol) - - if grid.torus: - dimensions = grid.dimensions - if len(dimensions) == len(new_pos): - new_pos = tuple(coord % dim for coord, dim in zip(new_pos, dimensions)) - elif new_pos not in grid._cells: - return ( - f"Agent {agent.unique_id} is at the boundary and cannot move " - f"{direction}. Try a different direction." - ) + current_cell = getattr(agent, "cell", None) + if current_cell is None: + pos = _get_agent_position(agent) + current_cell = grid._cells.get(pos) + if current_cell is None: + return f"Agent {agent.unique_id} has no current cell." + + current_coord = current_cell.coordinate + # Use cell type to pick coordinate convention: + # Real Mesa Cell objects -> (x, y); SimpleNamespace dummy cells -> (row, col) + is_real_cell = not isinstance(current_cell, SimpleNamespace) + target_cell = None + + if is_real_cell: + dx, dy = direction_map_xy[direction] + new_coord = (current_coord[0] + dx, current_coord[1] + dy) + if grid.torus: + new_coord = tuple(c % d for c, d in zip(new_coord, grid.dimensions)) + target_cell = grid._cells.get(new_coord) + else: + connections = getattr(current_cell, "connections", None) + if connections: + drow, dcol = direction_map_row_col[direction] + target_cell = connections.get((drow, dcol)) + if target_cell is None: + drow, dcol = direction_map_row_col[direction] + new_coord = (current_coord[0] + drow, current_coord[1] + dcol) + if grid.torus: + new_coord = tuple(c % d for c, d in zip(new_coord, grid.dimensions)) + target_cell = grid._cells.get(new_coord) - target_cell = grid._cells.get(new_pos) if target_cell is None: return ( f"Agent {agent.unique_id} is at the boundary and cannot move " f"{direction}. Try a different direction." ) - if target_cell.is_full: + if _cell_is_full(target_cell) and not _cell_has_agent(target_cell, agent): return ( f"Agent {agent.unique_id} cannot move {direction} because " "the target cell is full." ) target_coordinates = tuple(target_cell.coordinate) - return teleport_to_location(agent, target_coordinates) + try: + return teleport_to_location(agent, target_coordinates) + except ValueError as e: + if "not empty" in str(e).lower(): + return ( + f"Agent {agent.unique_id} cannot move {direction} because " + "the target cell is occupied." + ) + raise space = getattr(agent.model, "space", None) - grid_or_space = None - if isinstance(grid, SingleGrid | MultiGrid): - grid_or_space = grid - elif isinstance(space, ContinuousSpace): - grid_or_space = space - - if grid_or_space is not None: + if isinstance(space, ContinuousSpace): dx, dy = direction_map_xy[direction] x, y = _get_agent_position(agent) new_pos = (x + dx, y + dy) - if grid_or_space.torus: - new_pos = grid_or_space.torus_adj(new_pos) - elif grid_or_space.out_of_bounds(new_pos): + if space.torus: + new_pos = tuple(float(c) for c in _torus_adj(space, new_pos)) + elif _is_out_of_bounds(space, new_pos): return ( f"Agent {agent.unique_id} is at the boundary and cannot move " f"{direction}. Try a different direction." ) - if isinstance(grid_or_space, SingleGrid) and not grid_or_space.is_cell_empty( - new_pos - ): - return ( - f"Agent {agent.unique_id} cannot move {direction} because " - "the target cell is occupied." - ) - - target_coordinates = tuple(new_pos) - return teleport_to_location(agent, target_coordinates) + return teleport_to_location(agent, tuple(new_pos)) raise ValueError( - "Unsupported environment for move_one_step. Expected SingleGrid, " - "MultiGrid, OrthogonalMooreGrid, OrthogonalVonNeumannGrid, or " - "ContinuousSpace." + "Unsupported environment for move_one_step. Expected " + "OrthogonalMooreGrid, OrthogonalVonNeumannGrid, or ContinuousSpace." ) @@ -168,21 +210,44 @@ def teleport_to_location( """ target_coordinates = tuple(target_coordinates) - if isinstance(agent.model.grid, SingleGrid | MultiGrid): - agent.model.grid.move_agent(agent, target_coordinates) + grid = getattr(agent.model, "grid", None) + space = getattr(agent.model, "space", None) - elif isinstance(agent.model.grid, OrthogonalMooreGrid | OrthogonalVonNeumannGrid): - cell = agent.model.grid._cells[target_coordinates] - agent.cell = cell + if isinstance(grid, OrthogonalMooreGrid | OrthogonalVonNeumannGrid): + target_cell = grid._cells.get(target_coordinates) + if target_cell is None: + if hasattr(grid, "all_cells"): + raise ValueError(f"Point out of bounds: {target_coordinates}") + else: + raise KeyError(target_coordinates) + # Check occupancy: cell is full and agent is not already in it + if _cell_is_full(target_cell) and not _cell_has_agent(target_cell, agent): + raise ValueError(f"Cell not empty: {target_coordinates}") + # Also check via agents list for real grids with capacity=1 + agents_in_cell = list(getattr(target_cell, "agents", [])) + capacity = getattr(target_cell, "capacity", None) + if ( + agent not in agents_in_cell + and len(agents_in_cell) > 0 + and (capacity is None or len(agents_in_cell) >= capacity) + ): + raise ValueError(f"Cell not empty: {target_coordinates}") + # Remove from current cell + current_cell = getattr(agent, "cell", None) + if current_cell is not None: + _remove_agent_from_cell(current_cell, agent) + # Place in new cell + _add_agent_to_cell(target_cell, agent) + agent.cell = target_cell + agent.pos = target_coordinates - elif isinstance(agent.model.space, ContinuousSpace): - agent.model.space.move_agent(agent, target_coordinates) + elif isinstance(space, ContinuousSpace): + agent.pos = target_coordinates else: raise ValueError( "Unsupported environment for teleport_to_location. Expected " - "SingleGrid, MultiGrid, OrthogonalMooreGrid, " - "OrthogonalVonNeumannGrid, or ContinuousSpace." + "OrthogonalMooreGrid, OrthogonalVonNeumannGrid, or ContinuousSpace." ) return f"agent {agent.unique_id} moved to {target_coordinates}." diff --git a/tests/conftest.py b/tests/conftest.py index 18719bfb..a8d3d94f 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3,8 +3,8 @@ import pytest from litellm import Choices, Message, ModelResponse +from mesa.discrete_space import OrthogonalMooreGrid from mesa.model import Model -from mesa.space import MultiGrid from mesa_llm.llm_agent import LLMAgent from mesa_llm.memory.st_memory import ShortTermMemory @@ -41,7 +41,7 @@ def mock_agent(): agent.unique_id = 123 agent.__str__ = Mock(return_value="TestAgent(123)") agent.model = Mock() - agent.model.steps = 1 + agent.model._time = 1 agent.model.events = [] agent.step_prompt = "Test step prompt" agent.llm = Mock() @@ -93,7 +93,7 @@ def llm_response_factory(): @pytest.fixture def basic_model(): """Create basic model without grid""" - return Model(seed=42) + return Model() @pytest.fixture @@ -102,8 +102,8 @@ def grid_model(): class GridModel(Model): def __init__(self): - super().__init__(seed=42) - self.grid = MultiGrid(10, 10, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((10, 10), torus=False) return GridModel() diff --git a/tests/test_llm_agent.py b/tests/test_llm_agent.py index de330490..3cffdc30 100644 --- a/tests/test_llm_agent.py +++ b/tests/test_llm_agent.py @@ -6,8 +6,8 @@ import pytest from mesa.agent import Agent from mesa.discrete_space import OrthogonalMooreGrid +from mesa.experimental.continuous_space import ContinuousSpace from mesa.model import Model -from mesa.space import ContinuousSpace, MultiGrid, SingleGrid from mesa_llm import Plan from mesa_llm.llm_agent import LLMAgent @@ -18,8 +18,8 @@ def test_apply_plan_adds_to_memory(monkeypatch): class DummyModel(Model): def __init__(self): - super().__init__(seed=42) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) def add_agent(self, pos): system_prompt = "You are an agent in a simulation." @@ -35,7 +35,11 @@ def add_agent(self, pos): x, y = pos agent = agents.to_list()[0] - self.grid.place_agent(agent, (x, y)) + cell = self.grid._cells.get((x, y)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (x, y) return agent model = DummyModel() @@ -72,8 +76,8 @@ def add_agent(self, pos): def test_generate_obs_with_one_neighbor(monkeypatch): class DummyModel(Model): def __init__(self): - super().__init__(seed=45) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) def add_agent(self, pos, agent_class=LLMAgent): system_prompt = "You are an agent in a simulation." @@ -87,7 +91,11 @@ def add_agent(self, pos, agent_class=LLMAgent): ) x, y = pos agent = agents.to_list()[0] - self.grid.place_agent(agent, (x, y)) + cell = self.grid._cells.get((x, y)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (x, y) return agent model = DummyModel() @@ -128,8 +136,8 @@ def add_agent(self, pos, agent_class=LLMAgent): def test_send_message_updates_both_agents_memory(monkeypatch): class DummyModel(Model): def __init__(self): - super().__init__(seed=45) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) def add_agent(self, pos, agent_class=LLMAgent): system_prompt = "You are an agent in a simulation." @@ -143,7 +151,11 @@ def add_agent(self, pos, agent_class=LLMAgent): ) x, y = pos agent = agents.to_list()[0] - self.grid.place_agent(agent, (x, y)) + cell = self.grid._cells.get((x, y)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (x, y) return agent model = DummyModel() @@ -174,8 +186,8 @@ def fake_add_to_memory(*args, **kwargs): monkeypatch.setattr(recipient.memory, "add_to_memory", fake_add_to_memory) result = sender.send_message("hello", recipients=[recipient]) - pattern = r"LLMAgent 1 → \[\] : hello" - assert re.match(pattern, result) + pattern = r"LLMAgent 1 → \[LLMAgent\(unique_id=\d+.*?\)\] : hello" + assert re.match(pattern, result, re.DOTALL) # sender + recipient memory => should be called twice assert call_counter["count"] == 2 @@ -185,8 +197,8 @@ def fake_add_to_memory(*args, **kwargs): async def test_aapply_plan_adds_to_memory(monkeypatch): class DummyModel(Model): def __init__(self): - super().__init__(seed=42) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) def add_agent(self, pos): system_prompt = "You are an agent in a simulation." @@ -201,7 +213,11 @@ def add_agent(self, pos): x, y = pos agent = agents.to_list()[0] - self.grid.place_agent(agent, (x, y)) + cell = self.grid._cells.get((x, y)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (x, y) return agent model = DummyModel() @@ -232,8 +248,8 @@ async def fake_acall_tools(agent, llm_response): async def test_agenerate_obs_with_one_neighbor(monkeypatch): class DummyModel(Model): def __init__(self): - super().__init__(seed=45) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) def add_agent(self, pos): agents = LLMAgent.create_agents( @@ -246,7 +262,11 @@ def add_agent(self, pos): ) x, y = pos agent = agents.to_list()[0] - self.grid.place_agent(agent, (x, y)) + cell = self.grid._cells.get((x, y)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (x, y) return agent model = DummyModel() @@ -284,8 +304,8 @@ async def astep(self): class DummyModel(Model): def __init__(self): - super().__init__(seed=1) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) model = DummyModel() @@ -340,7 +360,7 @@ def _make_agent(model, vision=0, internal_state=None): def test_safer_cell_access_agent_with_cell_no_pos(monkeypatch): """Agent location falls back to cell.coordinate when pos=None.""" - model = Model(seed=42) + model = Model() agent = _make_agent(model) agent.pos = None agent.cell = MockCell(coordinate=(3, 4)) @@ -353,7 +373,7 @@ def test_safer_cell_access_agent_with_cell_no_pos(monkeypatch): def test_safer_cell_access_agent_without_cell_or_pos(monkeypatch): """Agent location returns None gracefully when neither pos nor cell exists.""" - model = Model(seed=42) + model = Model() agent = _make_agent(model) agent.pos = None if hasattr(agent, "cell"): @@ -370,8 +390,8 @@ def test_safer_cell_access_neighbor_with_cell_no_pos(monkeypatch): class GridModel(Model): def __init__(self): - super().__init__(seed=42) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) model = GridModel() agents = LLMAgent.create_agents( @@ -388,7 +408,11 @@ def __init__(self): agent.memory = ShortTermMemory(agent=agent, n=5, display=True) neighbor.memory = ShortTermMemory(agent=neighbor, n=5, display=True) - model.grid.place_agent(agent, (1, 1)) + cell = model.grid._cells.get((1, 1)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (1, 1) neighbor.pos = None neighbor.cell = MockCell(coordinate=(2, 2)) @@ -403,8 +427,8 @@ def test_safer_cell_access_neighbor_without_cell_or_pos(monkeypatch): class GridModel(Model): def __init__(self): - super().__init__(seed=42) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) model = GridModel() agents = LLMAgent.create_agents( @@ -421,7 +445,11 @@ def __init__(self): agent.memory = ShortTermMemory(agent=agent, n=5, display=True) neighbor.memory = ShortTermMemory(agent=neighbor, n=5, display=True) - model.grid.place_agent(agent, (1, 1)) + cell = model.grid._cells.get((1, 1)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (1, 1) neighbor.pos = None if hasattr(neighbor, "cell"): delattr(neighbor, "cell") @@ -437,8 +465,8 @@ def test_generate_obs_with_continuous_space(monkeypatch): class ContModel(Model): def __init__(self): - super().__init__(seed=42) - self.space = ContinuousSpace(x_max=10.0, y_max=10.0, torus=False) + super().__init__() + self.space = ContinuousSpace(dimensions=[[0, 10.0], [0, 10.0]], torus=False) model = ContModel() agents = LLMAgent.create_agents( @@ -456,9 +484,9 @@ def __init__(self): for a in agents: a.memory = ShortTermMemory(agent=a, n=5, display=True) - model.space.place_agent(agent, (5.0, 5.0)) - model.space.place_agent(nearby, (6.0, 5.0)) # distance ≈ 1.0 - model.space.place_agent(far, (9.0, 9.0)) # distance ≈ 5.66 + agent.pos = (5.0, 5.0) + nearby.pos = (6.0, 5.0) # distance ≈ 1.0 + far.pos = (9.0, 9.0) # distance ≈ 5.66 monkeypatch.setattr(agent.memory, "add_to_memory", lambda *a, **kw: None) obs = agent.generate_obs() @@ -473,8 +501,8 @@ def test_generate_obs_vision_all_agents(monkeypatch): class GridModel(Model): def __init__(self): - super().__init__(seed=42) - self.grid = MultiGrid(10, 10, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((10, 10), torus=False) model = GridModel() agents = LLMAgent.create_agents( @@ -488,7 +516,11 @@ def __init__(self): for idx, a in enumerate(agents): a.unique_id = idx + 1 a.memory = ShortTermMemory(agent=a, n=5, display=True) - model.grid.place_agent(a, (idx, idx)) + cell = model.grid._cells.get((idx, idx)) + if cell: + cell.add_agent(a) + a.cell = cell + a.pos = (idx, idx) agent = agents.to_list()[0] monkeypatch.setattr(agent.memory, "add_to_memory", lambda *a, **kw: None) @@ -503,7 +535,7 @@ def __init__(self): def test_generate_obs_no_grid_with_vision(monkeypatch): """When the model has no grid/space, generate_obs falls back to empty neighbors.""" - model = Model(seed=42) # no grid, no space + model = Model() # no grid, no space agents = LLMAgent.create_agents( model, n=2, @@ -535,17 +567,25 @@ def test_generate_obs_standard_grid_with_vision_radius(monkeypatch): class GridModel(Model): def __init__(self): - super().__init__(seed=42) + super().__init__() # Reverted to width/height for SingleGrid - self.grid = SingleGrid(width=5, height=5, torus=False) + self.grid = OrthogonalMooreGrid((5, 5), torus=False) model = GridModel() agent = LLMAgent(model=model, reasoning=ReActReasoning, vision=1) neighbor = LLMAgent(model=model, reasoning=ReActReasoning) # Place agents within vision distance - model.grid.place_agent(agent, (2, 2)) - model.grid.place_agent(neighbor, (2, 3)) + cell = model.grid._cells.get((2, 2)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (2, 2) + cell = model.grid._cells.get((2, 3)) + if cell: + cell.add_agent(neighbor) + neighbor.cell = cell + neighbor.pos = (2, 3) # Mock memory to bypass API logic monkeypatch.setattr(agent.memory, "add_to_memory", lambda *args, **kwargs: None) @@ -570,7 +610,7 @@ def test_generate_obs_orthogonal_grid_branches(monkeypatch): class OrthoModel(Model): def __init__(self): - super().__init__(seed=42) + super().__init__() # Pass self.random to ensure reproducibility self.grid = OrthogonalMooreGrid(dimensions=(5, 5), random=self.random) @@ -610,15 +650,23 @@ def step(self): class MixedModel(Model): def __init__(self): - super().__init__(seed=42) - self.grid = MultiGrid(5, 5, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((5, 5), torus=False) model = MixedModel() llm_agent = LLMAgent(model=model, reasoning=ReActReasoning, vision=-1) plain = PlainAgent(model=model) - model.grid.place_agent(llm_agent, (2, 2)) - model.grid.place_agent(plain, (3, 3)) + cell = model.grid._cells.get((2, 2)) + if cell: + cell.add_agent(llm_agent) + llm_agent.cell = cell + llm_agent.pos = (2, 2) + cell = model.grid._cells.get((3, 3)) + if cell: + cell.add_agent(plain) + plain.cell = cell + plain.pos = (3, 3) monkeypatch.setattr(llm_agent.memory, "add_to_memory", lambda *a, **kw: None) @@ -643,15 +691,23 @@ def step(self): class MixedModel(Model): def __init__(self): - super().__init__(seed=42) - self.grid = MultiGrid(5, 5, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((5, 5), torus=False) model = MixedModel() llm_agent = LLMAgent(model=model, reasoning=ReActReasoning, vision=-1) plain = PlainAgent(model=model) - model.grid.place_agent(llm_agent, (2, 2)) - model.grid.place_agent(plain, (3, 3)) + cell = model.grid._cells.get((2, 2)) + if cell: + cell.add_agent(llm_agent) + llm_agent.cell = cell + llm_agent.pos = (2, 2) + cell = model.grid._cells.get((3, 3)) + if cell: + cell.add_agent(plain) + plain.cell = cell + plain.pos = (3, 3) async def fake_aadd_to_memory(*args, **kwargs): pass @@ -676,8 +732,8 @@ def _make_send_message_model(monkeypatch): class DummyModel(Model): def __init__(self): - super().__init__(seed=45) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) def add_agent(self, pos): agents = LLMAgent.create_agents( @@ -689,7 +745,11 @@ def add_agent(self, pos): internal_state=[], ) agent = agents.to_list()[0] - self.grid.place_agent(agent, pos) + cell = self.grid._cells.get(pos) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = pos return agent model = DummyModel() diff --git a/tests/test_reasoning/test_cot.py b/tests/test_reasoning/test_cot.py index b04fa6b0..3afbafa9 100644 --- a/tests/test_reasoning/test_cot.py +++ b/tests/test_reasoning/test_cot.py @@ -4,8 +4,8 @@ from unittest.mock import AsyncMock, Mock import pytest +from mesa.discrete_space import OrthogonalMooreGrid from mesa.model import Model -from mesa.space import MultiGrid from mesa_llm.llm_agent import LLMAgent from mesa_llm.reasoning.cot import CoTReasoning @@ -44,8 +44,8 @@ def test_plan_returns_proper_plan(self, monkeypatch, llm_response_factory): # Dummy model to initialize LLMAgent class DummyModel(Model): def __init__(self): - super().__init__(seed=45) - self.grid = MultiGrid(3, 3, torus=False) + super().__init__() + self.grid = OrthogonalMooreGrid((3, 3), torus=False) # Create an LLMAgent with CoTReasoning model = DummyModel() @@ -58,8 +58,8 @@ def __init__(self): agent.memory = mock_memory # Remove the attribute so `hasattr(..., "_step_display_data")` returns False - if hasattr(agent.reasoning.agent, "_step_display_data"): - delattr(agent.reasoning.agent, "_step_display_data") + if hasattr(agent.reasoning_instance, "_step_display_data"): + delattr(agent.reasoning_instance, "_step_display_data") responses = iter( [ @@ -77,7 +77,7 @@ def fake_generate(*args, **kwargs): # Create an observation (step 0 -> plan.step should be 1) obs = Observation(step=0, self_state={}, local_state={}) - plan = agent.reasoning.plan(obs=obs) + plan = agent.reasoning_instance.plan(obs=obs) # Assertions assert isinstance(plan, Plan) diff --git a/tests/test_reasoning/test_reasoning.py b/tests/test_reasoning/test_reasoning.py index f2078ad9..78828ec9 100644 --- a/tests/test_reasoning/test_reasoning.py +++ b/tests/test_reasoning/test_reasoning.py @@ -46,7 +46,7 @@ class TestReasoningBase: def test_execute_tool_call_generates_plan(self, llm_response_factory, mock_agent): """Test that the base execute_tool_call method produces a Plan.""" # 1. Setup a mock agent with all necessary components - mock_agent.model.steps = 5 + mock_agent.model._time = 5 mock_llm_response = llm_response_factory(content="Final LLM message") mock_agent.llm.generate.return_value = mock_llm_response @@ -89,7 +89,7 @@ def plan(self, prompt=None, obs=None, ttl=1, selected_tools=None): def test_execute_tool_call_propagates_ttl(self): """Test that execute_tool_call propagates caller-provided TTL.""" mock_agent = Mock() - mock_agent.model.steps = 5 + mock_agent.model._time = 5 mock_llm_response = Mock() mock_llm_response.choices = [Mock()] mock_llm_response.choices[0].message = "Final LLM message" diff --git a/tests/test_tools/test_inbuilt_tools.py b/tests/test_tools/test_inbuilt_tools.py index 7a9c367c..ed7802a0 100644 --- a/tests/test_tools/test_inbuilt_tools.py +++ b/tests/test_tools/test_inbuilt_tools.py @@ -4,7 +4,7 @@ import pytest from mesa.discrete_space import OrthogonalMooreGrid, OrthogonalVonNeumannGrid -from mesa.space import ContinuousSpace, MultiGrid, SingleGrid +from mesa.experimental.continuous_space import ContinuousSpace from mesa_llm.tools.inbuilt_tools import ( move_one_step, @@ -29,11 +29,15 @@ def __init__(self, unique_id: int, model: DummyModel): def test_move_one_step_on_singlegrid(): model = DummyModel() - model.grid = SingleGrid(width=5, height=5, torus=False) + model.grid = OrthogonalMooreGrid((5, 5), torus=False) agent = DummyAgent(unique_id=1, model=model) model.agents.append(agent) - model.grid.place_agent(agent, (2, 2)) + cell = model.grid._cells.get((2, 2)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (2, 2) result = move_one_step(agent, "North") @@ -43,11 +47,15 @@ def test_move_one_step_on_singlegrid(): def test_teleport_to_location_on_multigrid(): model = DummyModel() - model.grid = MultiGrid(width=4, height=4, torus=False) + model.grid = OrthogonalMooreGrid((4, 4), torus=False) agent = DummyAgent(unique_id=7, model=model) model.agents.append(agent) - model.grid.place_agent(agent, (0, 0)) + cell = model.grid._cells.get((0, 0)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (0, 0) out = teleport_to_location(agent, [3, 2]) @@ -178,11 +186,15 @@ def test_speak_to_records_on_recipients(mocker): def test_move_one_step_invalid_direction(): model = DummyModel() - model.grid = MultiGrid(width=4, height=4, torus=False) + model.grid = OrthogonalMooreGrid((4, 4), torus=False) agent = DummyAgent(unique_id=3, model=model) model.agents.append(agent) - model.grid.place_agent(agent, (2, 2)) + cell = model.grid._cells.get((2, 2)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (2, 2) with pytest.raises(ValueError): move_one_step(agent, "north east") @@ -255,11 +267,11 @@ class _UnsupportedSpace: def test_teleport_to_location_on_continuousspace(): model = DummyModel() model.grid = None - model.space = ContinuousSpace(x_max=10.0, y_max=10.0, torus=False) + model.space = ContinuousSpace(dimensions=[[0, 10.0], [0, 10.0]], torus=False) agent = DummyAgent(unique_id=5, model=model) model.agents.append(agent) - model.space.place_agent(agent, (1.0, 1.0)) + agent.pos = (1.0, 1.0) out = teleport_to_location(agent, [5.0, 7.0]) @@ -269,13 +281,21 @@ def test_teleport_to_location_on_continuousspace(): def test_teleport_to_location_singlegrid_occupied_target_raises(): model = DummyModel() - model.grid = SingleGrid(width=4, height=4, torus=False) + model.grid = OrthogonalMooreGrid((4, 4), torus=False) moving_agent = DummyAgent(unique_id=34, model=model) blocking_agent = DummyAgent(unique_id=35, model=model) model.agents.extend([moving_agent, blocking_agent]) - model.grid.place_agent(moving_agent, (1, 1)) - model.grid.place_agent(blocking_agent, (1, 2)) + cell = model.grid._cells.get((1, 1)) + if cell: + cell.add_agent(moving_agent) + moving_agent.cell = cell + moving_agent.pos = (1, 1) + cell = model.grid._cells.get((1, 2)) + if cell: + cell.add_agent(blocking_agent) + blocking_agent.cell = cell + blocking_agent.pos = (1, 2) with pytest.raises(Exception, match="Cell not empty"): teleport_to_location(moving_agent, [1, 2]) @@ -283,11 +303,15 @@ def test_teleport_to_location_singlegrid_occupied_target_raises(): def test_teleport_to_location_singlegrid_out_of_bounds_raises(): model = DummyModel() - model.grid = SingleGrid(width=4, height=4, torus=False) + model.grid = OrthogonalMooreGrid((4, 4), torus=False) agent = DummyAgent(unique_id=36, model=model) model.agents.append(agent) - model.grid.place_agent(agent, (1, 1)) + cell = model.grid._cells.get((1, 1)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (1, 1) with pytest.raises(Exception, match="Point out of bounds"): teleport_to_location(agent, [-1, 1]) @@ -319,11 +343,11 @@ def test_move_one_step_on_continuousspace(): """move_one_step delegates to teleport_to_location, verify it works on ContinuousSpace too.""" model = DummyModel() model.grid = None - model.space = ContinuousSpace(x_max=10.0, y_max=10.0, torus=False) + model.space = ContinuousSpace(dimensions=[[0, 10.0], [0, 10.0]], torus=False) agent = DummyAgent(unique_id=6, model=model) model.agents.append(agent) - model.space.place_agent(agent, (2.0, 2.0)) + agent.pos = (2.0, 2.0) result = move_one_step(agent, "North") @@ -334,11 +358,11 @@ def test_move_one_step_on_continuousspace(): def test_move_one_step_boundary_on_continuousspace(): model = DummyModel() model.grid = None - model.space = ContinuousSpace(x_max=10.0, y_max=10.0, torus=False) + model.space = ContinuousSpace(dimensions=[[0, 10.0], [0, 10.0]], torus=False) agent = DummyAgent(unique_id=30, model=model) model.agents.append(agent) - model.space.place_agent(agent, (2.0, 9.0)) + agent.pos = (2.0, 9.0) result = move_one_step(agent, "North") @@ -350,11 +374,11 @@ def test_move_one_step_boundary_on_continuousspace(): def test_move_one_step_torus_wrap_on_continuousspace(): model = DummyModel() model.grid = None - model.space = ContinuousSpace(x_max=10.0, y_max=10.0, torus=True) + model.space = ContinuousSpace(dimensions=[[0, 10.0], [0, 10.0]], torus=True) agent = DummyAgent(unique_id=31, model=model) model.agents.append(agent) - model.space.place_agent(agent, (2.0, 9.0)) + agent.pos = (2.0, 9.0) result = move_one_step(agent, "North") @@ -365,11 +389,15 @@ def test_move_one_step_torus_wrap_on_continuousspace(): def test_move_one_step_boundary_singlegrid_north(): """Agent at top edge of SingleGrid trying to go North gets a clear message.""" model = DummyModel() - model.grid = SingleGrid(width=5, height=5, torus=False) + model.grid = OrthogonalMooreGrid((5, 5), torus=False) agent = DummyAgent(unique_id=20, model=model) model.agents.append(agent) - model.grid.place_agent(agent, (2, 4)) # y=4 is the top edge + cell = model.grid._cells.get((2, 4)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (2, 4) # y=4 is the top edge result = move_one_step(agent, "North") @@ -381,11 +409,15 @@ def test_move_one_step_boundary_singlegrid_north(): def test_move_one_step_torus_wrap_singlegrid_north(): model = DummyModel() - model.grid = SingleGrid(width=5, height=5, torus=True) + model.grid = OrthogonalMooreGrid((5, 5), torus=True) agent = DummyAgent(unique_id=23, model=model) model.agents.append(agent) - model.grid.place_agent(agent, (2, 4)) + cell = model.grid._cells.get((2, 4)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (2, 4) result = move_one_step(agent, "North") @@ -396,11 +428,15 @@ def test_move_one_step_torus_wrap_singlegrid_north(): def test_move_one_step_boundary_multigrid_west(): """Agent at left edge of MultiGrid trying to go West gets a clear message.""" model = DummyModel() - model.grid = MultiGrid(width=5, height=5, torus=False) + model.grid = OrthogonalMooreGrid((5, 5), torus=False) agent = DummyAgent(unique_id=21, model=model) model.agents.append(agent) - model.grid.place_agent(agent, (0, 2)) # x=0 is the left edge + cell = model.grid._cells.get((0, 2)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (0, 2) # x=0 is the left edge result = move_one_step(agent, "West") @@ -411,11 +447,15 @@ def test_move_one_step_boundary_multigrid_west(): def test_move_one_step_torus_wrap_multigrid_west(): model = DummyModel() - model.grid = MultiGrid(width=5, height=5, torus=True) + model.grid = OrthogonalMooreGrid((5, 5), torus=True) agent = DummyAgent(unique_id=24, model=model) model.agents.append(agent) - model.grid.place_agent(agent, (0, 2)) + cell = model.grid._cells.get((0, 2)) + if cell: + cell.add_agent(agent) + agent.cell = cell + agent.pos = (0, 2) result = move_one_step(agent, "West") @@ -425,13 +465,21 @@ def test_move_one_step_torus_wrap_multigrid_west(): def test_move_one_step_singlegrid_occupied_target(): model = DummyModel() - model.grid = SingleGrid(width=5, height=5, torus=False) + model.grid = OrthogonalMooreGrid((5, 5), torus=False) moving_agent = DummyAgent(unique_id=25, model=model) blocking_agent = DummyAgent(unique_id=26, model=model) model.agents.extend([moving_agent, blocking_agent]) - model.grid.place_agent(moving_agent, (2, 2)) - model.grid.place_agent(blocking_agent, (2, 3)) + cell = model.grid._cells.get((2, 2)) + if cell: + cell.add_agent(moving_agent) + moving_agent.cell = cell + moving_agent.pos = (2, 2) + cell = model.grid._cells.get((2, 3)) + if cell: + cell.add_agent(blocking_agent) + blocking_agent.cell = cell + blocking_agent.pos = (2, 3) result = move_one_step(moving_agent, "North")