Repository: projectmesa/mesa
Pull request #1474 (2022-10-26 08:29:39)

Implement swap_pos
#808 points out there is no `swap_pos`.
> 4. `position_agent` docstring mentions `swap_pos`, but this function does not exist.

This PR implements `swap_pos`.

codecov[bot]: # [Codecov](https://codecov.io/gh/projectmesa/mesa/pull/1474?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) Report
Base: **91.21**% // Head: **91.35**% // Increases project coverage by **`+0.13%`** :tada:
> Coverage data is based on head [(`63232df`)](https://codecov.io/gh/projectmesa/mesa/pull/1474?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa) compared to base [(`b6accc6`)](https://codecov.io/gh/projectmesa/mesa/commit/b6accc6c1fea824ab6ef12f328da8101ab10efba?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    #1474      +/-   ##
==========================================
+ Coverage   91.21%   91.35%   +0.13%     
==========================================
  Files          15       15              
  Lines        1287     1296       +9     
  Branches      218      220       +2     
==========================================
+ Hits         1174     1184      +10     
+ Misses         80       79       -1     
  Partials       33       33              
```


| [Impacted Files](https://codecov.io/gh/projectmesa/mesa/pull/1474?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/1474/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=projectmesa#diff-bWVzYS9zcGFjZS5weQ==) | `96.17% <100.00%> (+0.37%)` | :arrow_up: |

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/1474?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).

rht: Content LGTM. Just need to be squashed.
rht: The commit message should say that it implements `swap_post`. Otherwise, it is ambiguous, and could have mean that some agent's positions are wrong in the code.
jnsp: > The commit message should say that it implements `swap_pos`. Otherwise, it is ambiguous, and could have mean that some agent's positions are wrong in the code.

[8abe56f](https://github.com/projectmesa/mesa/pull/1474/commits/8abe56f0d8983263b101b0c5f244f0f1f7980981) - Squased with commit message "Implement swap_pos"


Patch:
diff --git a/mesa/space.py b/mesa/space.py
index 16da1fbd..36209967 100644
--- a/mesa/space.py
+++ b/mesa/space.py
@@ -507,6 +507,26 @@ 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 3b43872e..3104ba2e 100644
--- a/tests/test_grid.py
+++ b/tests/test_grid.py
@@ -270,6 +270,34 @@ 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