Repository: projectmesa/mesa
Pull request #1507 (2022-11-05 21:47:55)

Make swap_pos part of Grid instead of SingleGrid
Making `swap_pos`  part of Grid so that it can be used also by MultiGrid. Will add some tests for the MultiGrid case.

codecov[bot]: # [Codecov](https://codecov.io/gh/projectmesa/mesa/pull/1507?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) Report
Base: **91.32**% // Head: **91.32**% // No change to project coverage :thumbsup:
> Coverage data is based on head [(`0a00c5c`)](https://codecov.io/gh/projectmesa/mesa/pull/1507?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) compared to base [(`c7ec7b4`)](https://codecov.io/gh/projectmesa/mesa/commit/c7ec7b477b34e17b7fe41d610a9d95d0be65af38?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa).
> Patch coverage: 100.00% of modified lines in pull request are covered.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main    #1507   +/-   ##
=======================================
  Coverage   91.32%   91.32%           
=======================================
  Files          15       15           
  Lines        1303     1303           
  Branches      223      223           
=======================================
  Hits         1190     1190           
  Misses         80       80           
  Partials       33       33           
```


| [Impacted Files](https://codecov.io/gh/projectmesa/mesa/pull/1507?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) | Coverage Δ | |
|---|---|---|
| [mesa/space.py](https://codecov.io/gh/projectmesa/mesa/pull/1507/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa#diff-bWVzYS9zcGFjZS5weQ==) | `96.21% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa)

</details>

[:umbrella: View full report at Codecov](https://codecov.io/gh/projectmesa/mesa/pull/1507?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa).   
:loudspeaker: Do you have feedback about the report comment? [Let us know in this issue](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa).

Tortar: I actually just put the tests in TestBaseGrid, I think it's ok because many function are not tested in the subclasses, and because  `swap_pos`  uses function present both in SingleGrid and in MultiGrid all should safely work
rht: cc: @jnsp
rht: LGTM

Patch:
diff --git a/mesa/space.py b/mesa/space.py
index e15d5a3b..c0081f74 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -408,6 +408,26 @@ class Grid:
         self.empties.add(pos)
         agent.pos = None
 
+    def swap_pos(self, agent_a: Agent, agent_b: Agent) -> None:
+        """Swap agents positions"""
+        agents_no_pos = []
+        if (pos_a := agent_a.pos) is None:
+            agents_no_pos.append(agent_a)
+        if (pos_b := agent_b.pos) is None:
+            agents_no_pos.append(agent_b)
+        if agents_no_pos:
+            agents_no_pos = [f"<Agent id: {a.unique_id}>" for a in agents_no_pos]
+            raise Exception(f"{', '.join(agents_no_pos)} - not on the grid")
+
+        if pos_a == pos_b:
+            return
+
+        self.remove_agent(agent_a)
+        self.remove_agent(agent_b)
+
+        self.place_agent(agent_a, pos_b)
+        self.place_agent(agent_b, pos_a)
+
     def is_cell_empty(self, pos: Coordinate) -> bool:
         """Returns a bool of the contents of a cell."""
         x, y = pos
@@ -492,26 +512,6 @@ class SingleGrid(Grid):
             coords = (x, y)
             self.place_agent(agent, coords)
 
-    def swap_pos(self, agent_a: Agent, agent_b: Agent) -> None:
-        """Swap agents positions"""
-        agents_no_pos = []
-        if (pos_a := agent_a.pos) is None:
-            agents_no_pos.append(agent_a)
-        if (pos_b := agent_b.pos) is None:
-            agents_no_pos.append(agent_b)
-        if agents_no_pos:
-            agents_no_pos = [f"<Agent id: {a.unique_id}>" for a in agents_no_pos]
-            raise Exception(f"{', '.join(agents_no_pos)} - not on the grid")
-
-        if pos_a == pos_b:
-            return
-
-        self.remove_agent(agent_a)
-        self.remove_agent(agent_b)
-
-        self.place_agent(agent_a, pos_b)
-        self.place_agent(agent_b, pos_a)
-
     def place_agent(self, agent: Agent, pos: Coordinate) -> None:
         if self.is_cell_empty(pos):
             super().place_agent(agent, pos)


Test patch:
diff --git a/tests/test_grid.py b/tests/test_grid.py
index 64e93a44..245a5c35 100644
--- a/tests/test_grid.py
+++ b/tests/test_grid.py
@@ -169,6 +169,36 @@ class TestBaseGrid(unittest.TestCase):
         assert agent.pos is None
         assert self.grid.grid[x][y] is None
 
+    def test_swap_pos(self):
+
+        # Swap agents positions
+        agent_a, agent_b = list(filter(None, self.grid))[:2]
+        pos_a = agent_a.pos
+        pos_b = agent_b.pos
+
+        self.grid.swap_pos(agent_a, agent_b)
+
+        assert agent_a.pos == pos_b
+        assert agent_b.pos == pos_a
+        assert self.grid[pos_a] == agent_b
+        assert self.grid[pos_b] == agent_a
+
+        # Swap the same agents
+        self.grid.swap_pos(agent_a, agent_a)
+
+        assert agent_a.pos == pos_b
+        assert self.grid[pos_b] == agent_a
+
+        # Raise for agents not on the grid
+        self.grid.remove_agent(agent_a)
+        self.grid.remove_agent(agent_b)
+
+        id_a = agent_a.unique_id
+        id_b = agent_b.unique_id
+        e_message = f"<Agent id: {id_a}>, <Agent id: {id_b}> - not on the grid"
+        with self.assertRaisesRegex(Exception, e_message):
+            self.grid.swap_pos(agent_a, agent_b)
+
 
 class TestBaseGridTorus(TestBaseGrid):
     """
@@ -268,34 +298,6 @@ class TestSingleGrid(unittest.TestCase):
         with self.assertRaises(Exception):
             self.move_to_empty(self.agents[0], num_agents=self.num_agents)
 
-        # Swap agents positions
-        agent_a, agent_b = random.sample(list(self.grid), k=2)
-        pos_a = agent_a.pos
-        pos_b = agent_b.pos
-
-        self.grid.swap_pos(agent_a, agent_b)
-
-        assert agent_a.pos == pos_b
-        assert agent_b.pos == pos_a
-        assert self.grid[pos_a] == agent_b
-        assert self.grid[pos_b] == agent_a
-
-        # Swap the same agents
-        self.grid.swap_pos(agent_a, agent_a)
-
-        assert agent_a.pos == pos_b
-        assert self.grid[pos_b] == agent_a
-
-        # Raise for agents not on the grid
-        self.grid.remove_agent(agent_a)
-        self.grid.remove_agent(agent_b)
-
-        id_a = agent_a.unique_id
-        id_b = agent_b.unique_id
-        e_message = f"<Agent id: {id_a}>, <Agent id: {id_b}> - not on the grid"
-        with self.assertRaisesRegex(Exception, e_message):
-            self.grid.swap_pos(agent_a, agent_b)
-
 
 # Number of agents at each position for testing
 # Initial agent positions for testing