Repository: projectmesa/mesa

Issue:
examples: Sugerscape_g1mt is missing input Model Params (Sliders etc.)
The Sugerscape_g1mt app.py is missing any input Model Params (like Sliders etc.) to play with in the visualisation.

https://github.com/projectmesa/mesa/blob/a04e20b46d1d11a13163277cb8bcbac86d4851ff/mesa/examples/advanced/sugarscape_g1mt/app.py#L49-L52

I tried to add these:
```Python
model_params = {
    # Grid parameters
    "width": Slider("Grid Width", value=50, min=20, max=100, step=10),
    "height": Slider("Grid Height", value=50, min=20, max=100, step=10),

    # Population parameters
    "initial_population": Slider("Initial Population", value=200, min=50, max=500, step=10),

    # Agent endowment parameters
    "endowment_min": Slider("Min Initial Endowment", value=25, min=5, max=30, step=1),
    "endowment_max": Slider("Max Initial Endowment", value=50, min=30, max=100, step=1),

    # Metabolism parameters
    "metabolism_min": Slider("Min Metabolism", value=1, min=1, max=3, step=1),
    "metabolism_max": Slider("Max Metabolism", value=5, min=3, max=8, step=1),

    # Vision parameters
    "vision_min": Slider("Min Vision", value=1, min=1, max=3, step=1),
    "vision_max": Slider("Max Vision", value=5, min=3, max=8, step=1),

    # Trade parameter
    "enable_trade": {"type": "Checkbox", "value": True, "label": "Enable Trading"},
}
```
But modifying things lead to all kinds of error and unexpected behavior. For example, changing the width only did so partially (and apparently the height instead of the width):

![image](https://github.com/user-attachments/assets/e532ce6f-82fa-4d01-ae14-6bb8e05be7ab)

I suspect that's due to the model first being initialized (with `model1 = SugarscapeG1mt()`), and then the slider value being modified.

CC @Corvince

Discussion:
@Corvince: I don't see a problem with SolaraViz here. Changing "initial population" seems to work just fine. Disabling trade removes "price" from the line chart, which I assume is intended. The other parameters are hard for me to judge, since I don't know how they impact the model.

Width and Height are indeed broken, but this seems to be implementation specific. The underlying "sugar-map.txt" just seems to be a 50x50 matrix. So I guess no wonder it doesn't work if you set it to larger values. Smaller seems to be fine, except the swapping of height and width.  

Solution patch:
diff --git a/mesa/examples/advanced/sugarscape_g1mt/app.py b/mesa/examples/advanced/sugarscape_g1mt/app.py
index 8a5441cc54d..6564cb68361 100644
--- a/mesa/examples/advanced/sugarscape_g1mt/app.py
+++ b/mesa/examples/advanced/sugarscape_g1mt/app.py
@@ -4,7 +4,7 @@
 
 from mesa.examples.advanced.sugarscape_g1mt.agents import Trader
 from mesa.examples.advanced.sugarscape_g1mt.model import SugarscapeG1mt
-from mesa.visualization import SolaraViz, make_plot_component
+from mesa.visualization import Slider, SolaraViz, make_plot_component
 
 
 def SpaceDrawer(model):
@@ -49,13 +49,29 @@ def portray(g):
 model_params = {
     "width": 50,
     "height": 50,
+    # Population parameters
+    "initial_population": Slider(
+        "Initial Population", value=200, min=50, max=500, step=10
+    ),
+    # Agent endowment parameters
+    "endowment_min": Slider("Min Initial Endowment", value=25, min=5, max=30, step=1),
+    "endowment_max": Slider("Max Initial Endowment", value=50, min=30, max=100, step=1),
+    # Metabolism parameters
+    "metabolism_min": Slider("Min Metabolism", value=1, min=1, max=3, step=1),
+    "metabolism_max": Slider("Max Metabolism", value=5, min=3, max=8, step=1),
+    # Vision parameters
+    "vision_min": Slider("Min Vision", value=1, min=1, max=3, step=1),
+    "vision_max": Slider("Max Vision", value=5, min=3, max=8, step=1),
+    # Trade parameter
+    "enable_trade": {"type": "Checkbox", "value": True, "label": "Enable Trading"},
 }
 
-model1 = SugarscapeG1mt(50, 50)
+model1 = SugarscapeG1mt()
 
 page = SolaraViz(
     model1,
     components=[SpaceDrawer, make_plot_component(["Trader", "Price"])],
+    model_params=model_params,
     name="Sugarscape {G1, M, T}",
     play_interval=150,
 )
diff --git a/mesa/examples/advanced/wolf_sheep/app.py b/mesa/examples/advanced/wolf_sheep/app.py
index fa66d2bf11e..878c6797bed 100644
--- a/mesa/examples/advanced/wolf_sheep/app.py
+++ b/mesa/examples/advanced/wolf_sheep/app.py
@@ -83,7 +83,7 @@ def post_process_lines(ax):
 )
 
 simulator = ABMSimulator()
-model = WolfSheep(simulator, grass=True)
+model = WolfSheep(simulator=simulator, grass=True)
 
 page = SolaraViz(
     model,
diff --git a/mesa/examples/basic/boid_flockers/agents.py b/mesa/examples/basic/boid_flockers/agents.py
index 48ce2b5a868..2ff00cbaef2 100644
--- a/mesa/examples/basic/boid_flockers/agents.py
+++ b/mesa/examples/basic/boid_flockers/agents.py
@@ -54,13 +54,14 @@ def __init__(
         self.cohere_factor = cohere
         self.separate_factor = separate
         self.match_factor = match
+        self.neighbors = []
 
     def step(self):
         """Get the Boid's neighbors, compute the new vector, and move accordingly."""
-        neighbors = self.model.space.get_neighbors(self.pos, self.vision, False)
+        self.neighbors = self.model.space.get_neighbors(self.pos, self.vision, False)
 
         # If no neighbors, maintain current direction
-        if not neighbors:
+        if not self.neighbors:
             new_pos = self.pos + self.direction * self.speed
             self.model.space.move_agent(self, new_pos)
             return
@@ -71,7 +72,7 @@ def step(self):
         separation_vector = np.zeros(2)  # Separation vector
 
         # Calculate the contribution of each neighbor to the three behaviors
-        for neighbor in neighbors:
+        for neighbor in self.neighbors:
             heading = self.model.space.get_heading(self.pos, neighbor.pos)
             distance = self.model.space.get_distance(self.pos, neighbor.pos)
 
@@ -86,7 +87,7 @@ def step(self):
             match_vector += neighbor.direction
 
         # Weight each behavior by its factor and normalize by number of neighbors
-        n = len(neighbors)
+        n = len(self.neighbors)
         cohere = cohere * self.cohere_factor
         separation_vector = separation_vector * self.separate_factor
         match_vector = match_vector * self.match_factor
diff --git a/mesa/examples/basic/boid_flockers/app.py b/mesa/examples/basic/boid_flockers/app.py
index effff8c26ec..074c1ab341f 100644
--- a/mesa/examples/basic/boid_flockers/app.py
+++ b/mesa/examples/basic/boid_flockers/app.py
@@ -3,10 +3,7 @@
 
 
 def boid_draw(agent):
-    if not agent.neighbors:  # Only for the first Frame
-        neighbors = len(agent.model.space.get_neighbors(agent.pos, agent.vision, False))
-    else:
-        neighbors = len(agent.neighbors)
+    neighbors = len(agent.neighbors)
 
     if neighbors <= 1:
         return {"color": "red", "size": 20}
diff --git a/mesa/examples/basic/schelling/app.py b/mesa/examples/basic/schelling/app.py
index 73492fa07b6..933b3c2703d 100644
--- a/mesa/examples/basic/schelling/app.py
+++ b/mesa/examples/basic/schelling/app.py
@@ -26,7 +26,7 @@ def agent_portrayal(agent):
     "height": 20,
 }
 
-model1 = Schelling(20, 20, 0.8, 0.2, 3)
+model1 = Schelling()
 
 HappyPlot = make_plot_component({"happy": "tab:green"})
 
diff --git a/mesa/visualization/solara_viz.py b/mesa/visualization/solara_viz.py
index 9819e1c94e1..10718e8a804 100644
--- a/mesa/visualization/solara_viz.py
+++ b/mesa/visualization/solara_viz.py
@@ -43,10 +43,10 @@
 @solara.component
 def SolaraViz(
     model: Model | solara.Reactive[Model],
-    *,
     components: list[reacton.core.Component]
     | list[Callable[[Model], reacton.core.Component]]
     | Literal["default"] = "default",
+    *,
     play_interval: int = 100,
     simulator: Simulator | None = None,
     model_params=None,
@@ -276,7 +276,7 @@ def do_reset():
         running.value = True
         simulator.reset()
         model.value = model.value = model.value.__class__(
-            simulator, **model_parameters.value
+            simulator=simulator, **model_parameters.value
         )
 
     def do_play_pause():


Test patch:
diff --git a/tests/test_examples.py b/tests/test_examples.py
index 98d1d5809ee..0e8a7edce42 100644
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -13,6 +13,10 @@
 
 
 def test_boltzmann_model():  # noqa: D103
+    from mesa.examples.basic.boltzmann_wealth_model import app
+
+    app.page  # noqa: B018
+
     model = BoltzmannWealth(seed=42)
 
     for _i in range(10):
@@ -20,24 +24,40 @@ def test_boltzmann_model():  # noqa: D103
 
 
 def test_conways_game_model():  # noqa: D103
+    from mesa.examples.basic.conways_game_of_life import app
+
+    app.page  # noqa: B018
+
     model = ConwaysGameOfLife(seed=42)
     for _i in range(10):
         model.step()
 
 
 def test_schelling_model():  # noqa: D103
+    from mesa.examples.basic.schelling import app
+
+    app.page  # noqa: B018
+
     model = Schelling(seed=42)
     for _i in range(10):
         model.step()
 
 
 def test_virus_on_network():  # noqa: D103
+    from mesa.examples.basic.virus_on_network import app
+
+    app.page  # noqa: B018
+
     model = VirusOnNetwork(seed=42)
     for _i in range(10):
         model.step()
 
 
 def test_boid_flockers():  # noqa: D103
+    from mesa.examples.basic.boid_flockers import app
+
+    app.page  # noqa: B018
+
     model = BoidFlockers(seed=42)
 
     for _i in range(10):
@@ -45,6 +65,10 @@ def test_boid_flockers():  # noqa: D103
 
 
 def test_epstein():  # noqa: D103
+    from mesa.examples.advanced.epstein_civil_violence import app
+
+    app.page  # noqa: B018
+
     model = EpsteinCivilViolence(seed=42)
 
     for _i in range(10):
@@ -52,6 +76,10 @@ def test_epstein():  # noqa: D103
 
 
 def test_pd_grid():  # noqa: D103
+    from mesa.examples.advanced.pd_grid import app
+
+    app.page  # noqa: B018
+
     model = PdGrid(seed=42)
 
     for _i in range(10):
@@ -59,6 +87,10 @@ def test_pd_grid():  # noqa: D103
 
 
 def test_sugarscape_g1mt():  # noqa: D103
+    from mesa.examples.advanced.sugarscape_g1mt import app
+
+    app.page  # noqa: B018
+
     model = SugarscapeG1mt(seed=42)
 
     for _i in range(10):
@@ -66,8 +98,11 @@ def test_sugarscape_g1mt():  # noqa: D103
 
 
 def test_wolf_sheep():  # noqa: D103
+    from mesa.examples.advanced.wolf_sheep import app
     from mesa.experimental.devs import ABMSimulator
 
+    app.page  # noqa: B018
+
     simulator = ABMSimulator()
     WolfSheep(seed=42, simulator=simulator)
     simulator.run_for(10)