From 2bf82cd9f900e8573cfdda056fe2b5475fcb1242 Mon Sep 17 00:00:00 2001 From: Punya-B-R Date: Sun, 8 Mar 2026 19:48:02 +0530 Subject: [PATCH 1/5] Add Axelrod Culture Model example --- examples/axelrod_culture/README.md | 27 +++++++ examples/axelrod_culture/app.py | 77 ++++++++++++++++++ .../axelrod_culture/__init__.py | 0 .../axelrod_culture/axelrod_culture/agents.py | 24 ++++++ .../axelrod_culture/axelrod_culture/model.py | 80 +++++++++++++++++++ examples/axelrod_culture/requirements.txt | 3 + 6 files changed, 211 insertions(+) create mode 100644 examples/axelrod_culture/README.md create mode 100644 examples/axelrod_culture/app.py create mode 100644 examples/axelrod_culture/axelrod_culture/__init__.py create mode 100644 examples/axelrod_culture/axelrod_culture/agents.py create mode 100644 examples/axelrod_culture/axelrod_culture/model.py create mode 100644 examples/axelrod_culture/requirements.txt diff --git a/examples/axelrod_culture/README.md b/examples/axelrod_culture/README.md new file mode 100644 index 000000000..a8cd671b3 --- /dev/null +++ b/examples/axelrod_culture/README.md @@ -0,0 +1,27 @@ +# Axelrod Culture Model + +## Summary + +An implementation of Axelrod's model of cultural dissemination. Each agent occupies a cell on a grid and holds a "culture" consisting of **F** features, each taking one of **Q** possible integer traits. At each step, an agent randomly selects a neighbor and interacts with probability equal to their cultural similarity (fraction of shared features). If interaction occurs, the agent copies one of the neighbor's differing traits. + +Despite the local tendency toward convergence, stable cultural regions can persist globally — Axelrod's key insight: local homogenization and global polarization can coexist. The number of stable regions increases with Q (more possible traits → more diversity) and decreases with F (more features → more overlap → faster convergence). + +## How to Run + +To install the dependencies use pip and the requirements.txt in this directory: + + $ pip install -r requirements.txt + +To run the model interactively, in this directory, run the following command: + + $ solara run app.py + +## Files + +* [agents.py](axelrod_culture/agents.py): Defines `CultureAgent` with a cultural profile and interaction logic +* [model.py](axelrod_culture/model.py): Sets up the grid, initializes random cultures, and tracks cultural regions +* [app.py](app.py): Solara based visualization showing the culture grid and region count over time + +## Further Reading + +* Axelrod, R. (1997). The dissemination of culture: A model with local convergence and global polarization. *Journal of Conflict Resolution*, 41(2), 203–226. https://doi.org/10.1177/0022002797041002001 \ No newline at end of file diff --git a/examples/axelrod_culture/app.py b/examples/axelrod_culture/app.py new file mode 100644 index 000000000..8de1e092b --- /dev/null +++ b/examples/axelrod_culture/app.py @@ -0,0 +1,77 @@ +import solara +import numpy as np +import hashlib +from matplotlib.figure import Figure + +from mesa.visualization import SolaraViz, make_plot_component +from axelrod_culture.model import AxelrodModel, number_of_cultural_regions + + +def culture_to_color(culture): + key = str(culture).encode() + h = hashlib.md5(key).hexdigest() + return (int(h[0:2], 16) / 255, int(h[2:4], 16) / 255, int(h[4:6], 16) / 255) + + +def make_culture_grid(model): + fig = Figure(figsize=(5, 5)) + fig.subplots_adjust(left=0.05, right=0.95, top=0.92, bottom=0.05) + ax = fig.add_subplot(111) + grid = np.zeros((model.height, model.width, 3)) + for agent in model.agents: + x, y = int(agent.cell.coordinate[0]), int(agent.cell.coordinate[1]) + grid[y][x] = culture_to_color(agent.culture) + ax.imshow(grid, origin="lower", interpolation="nearest") + ax.set_title(f"Cultural Regions: {number_of_cultural_regions(model)}", fontsize=11) + ax.set_xticks([]) + ax.set_yticks([]) + return solara.FigureMatplotlib(fig) + + +RegionsPlot = make_plot_component({"Cultural Regions": "#e63946"}) + +model_params = { + "rng": {"type": "InputText", "value": 42, "label": "Random Seed"}, + "width": { + "type": "SliderInt", + "value": 10, + "label": "Grid Width", + "min": 5, + "max": 20, + "step": 1, + }, + "height": { + "type": "SliderInt", + "value": 10, + "label": "Grid Height", + "min": 5, + "max": 20, + "step": 1, + }, + "F": { + "type": "SliderInt", + "value": 3, + "label": "Features (F)", + "min": 2, + "max": 10, + "step": 1, + }, + "Q": { + "type": "SliderInt", + "value": 3, + "label": "Traits per feature (Q)", + "min": 2, + "max": 15, + "step": 1, + }, +} + +model = AxelrodModel() + +page = SolaraViz( + model, + components=[make_culture_grid, RegionsPlot], + model_params=model_params, + name="Axelrod Culture Model", +) +page # noqa diff --git a/examples/axelrod_culture/axelrod_culture/__init__.py b/examples/axelrod_culture/axelrod_culture/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/examples/axelrod_culture/axelrod_culture/agents.py b/examples/axelrod_culture/axelrod_culture/agents.py new file mode 100644 index 000000000..1e1b82c77 --- /dev/null +++ b/examples/axelrod_culture/axelrod_culture/agents.py @@ -0,0 +1,24 @@ +from mesa import Agent + + +class CultureAgent(Agent): + def __init__(self, model, culture): + super().__init__(model) + self.culture = list(culture) + + def similarity(self, other): + matches = sum(a == b for a, b in zip(self.culture, other.culture)) + return matches / len(self.culture) + + def interact_with(self, other): + sim = self.similarity(other) + if sim == 0.0 or sim == 1.0: + return + if self.random.random() < sim: + differing = [ + i + for i in range(len(self.culture)) + if self.culture[i] != other.culture[i] + ] + feature = self.random.choice(differing) + self.culture[feature] = other.culture[feature] diff --git a/examples/axelrod_culture/axelrod_culture/model.py b/examples/axelrod_culture/axelrod_culture/model.py new file mode 100644 index 000000000..fb4abf3d3 --- /dev/null +++ b/examples/axelrod_culture/axelrod_culture/model.py @@ -0,0 +1,80 @@ +""" +Axelrod Culture Model +===================== +Models how local cultural interactions produce global polarization. +Each agent has F features, each with Q possible traits. Agents interact +with neighbors with probability equal to cultural similarity, copying +one differing trait if interaction occurs. + +Reference: + Axelrod, R. (1997). The dissemination of culture. + Journal of Conflict Resolution, 41(2), 203-226. +""" + +from mesa import Model +from mesa.datacollection import DataCollector +from mesa.discrete_space import OrthogonalVonNeumannGrid + +from axelrod_culture.agents import CultureAgent + + +def number_of_cultural_regions(model): + visited = set() + regions = 0 + agent_by_pos = { + (int(a.cell.coordinate[0]), int(a.cell.coordinate[1])): a for a in model.agents + } + for pos in agent_by_pos: + if pos in visited: + continue + queue = [pos] + visited.add(pos) + while queue: + cx, cy = queue.pop() + for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: + npos = (cx + dx, cy + dy) + if npos not in visited and npos in agent_by_pos: + if agent_by_pos[npos].culture == agent_by_pos[(cx, cy)].culture: + visited.add(npos) + queue.append(npos) + regions += 1 + return regions + + +class AxelrodModel(Model): + def __init__(self, width=10, height=10, F=3, Q=3, rng=None): + super().__init__(rng=rng) + self.width = width + self.height = height + self.F = F + self.Q = Q + + self.grid = OrthogonalVonNeumannGrid( + (width, height), torus=False, random=self.random + ) + + cultures = [ + [self.random.randrange(Q) for _ in range(F)] for _ in range(width * height) + ] + CultureAgent.create_agents(self, width * height, cultures) + + for agent, cell in zip(self.agents, self.grid.all_cells.cells): + agent.cell = cell + + self.datacollector = DataCollector( + model_reporters={"Cultural Regions": number_of_cultural_regions} + ) + self.running = True + self.datacollector.collect(self) + + def step(self): + agent_list = list(self.agents) + # Run N random pairwise interactions per step (one per agent on average) + for _ in range(self.width * self.height): + agent = self.random.choice(agent_list) + neighbors = [a for a in agent.cell.neighborhood.agents if a is not agent] + if neighbors: + neighbor = self.random.choice(neighbors) + agent.interact_with(neighbor) + + self.datacollector.collect(self) diff --git a/examples/axelrod_culture/requirements.txt b/examples/axelrod_culture/requirements.txt new file mode 100644 index 000000000..46c19f181 --- /dev/null +++ b/examples/axelrod_culture/requirements.txt @@ -0,0 +1,3 @@ +mesa +matplotlib +solara \ No newline at end of file From 671399553f77fbf07d7499cb876ef8cd458a1da2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 8 Mar 2026 14:28:24 +0000 Subject: [PATCH 2/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/axelrod_culture/app.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/axelrod_culture/app.py b/examples/axelrod_culture/app.py index 8de1e092b..737943888 100644 --- a/examples/axelrod_culture/app.py +++ b/examples/axelrod_culture/app.py @@ -1,10 +1,10 @@ -import solara -import numpy as np import hashlib -from matplotlib.figure import Figure -from mesa.visualization import SolaraViz, make_plot_component +import numpy as np +import solara from axelrod_culture.model import AxelrodModel, number_of_cultural_regions +from matplotlib.figure import Figure +from mesa.visualization import SolaraViz, make_plot_component def culture_to_color(culture): From 8591426e7c17a8607abbbef717cc700f827d7109 Mon Sep 17 00:00:00 2001 From: Punya-B-R Date: Sun, 8 Mar 2026 20:06:05 +0530 Subject: [PATCH 3/5] Add Axelrod Culture Model to README --- README.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index f30f818cb..777533c25 100644 --- a/README.md +++ b/README.md @@ -71,13 +71,19 @@ This project is an agent-based model implemented using the Mesa framework in Pyt ### [Emperor's Dilemma](https://github.com/mesa/mesa-examples/tree/main/examples/emperor_dilemma) This project simulates how unpopular norms can dominate a society even when the vast majority of individuals privately reject them. It demonstrates the "illusion of consensus" where agents, driven by a fear of appearing disloyal, not only comply with a rule they hate but also aggressively enforce it on their neighbors. This phenomenon creates a "trap" of False Enforcement, where the loudest defenders of a norm are often its secret opponents. + ### [Humanitarian Aid Distribution Model](https://github.com/mesa/mesa-examples/tree/main/examples/humanitarian_aid_distribution) This model simulates a humanitarian aid distribution scenario using a needs-based behavioral architecture. Beneficiaries have dynamic needs (water, food) and trucks distribute aid using a hybrid triage system. + ### [Rumor Mill Model](https://github.com/mesa/mesa-examples/tree/main/examples/rumor_mill) A simple agent-based simulation showing how rumors spread through a population based on the spread chance and initial knowing percentage, implemented with the Mesa framework and adapted from NetLogo [Rumor mill](https://www.netlogoweb.org/launch#https://www.netlogoweb.org/assets/modelslib/Sample%20Models/Social%20Science/Rumor%20Mill.nlogox). +### [Axelrod Culture Model](https://github.com/mesa/mesa-examples/tree/main/examples/axelrod_culture) + +An implementation of Axelrod's model of cultural dissemination. Agents on a grid hold multi-feature cultural profiles and interact with neighbors based on cultural similarity, producing emergent cultural regions. Demonstrates how local convergence and global polarization can coexist. + ## Continuous Space Examples _No user examples available yet._ @@ -135,4 +141,4 @@ This folder contains an implementation of El Farol restaurant model. Agents (res ### [Schelling Model with Caching and Replay](https://github.com/mesa/mesa-examples/tree/main/examples/caching_and_replay) -This example applies caching on the Mesa [Schelling](https://github.com/mesa/mesa-examples/tree/main/examples/schelling) example. It enables a simulation run to be "cached" or in other words recorded. The recorded simulation run is persisted on the local file system and can be replayed at any later point. +This example applies caching on the Mesa [Schelling](https://github.com/mesa/mesa-examples/tree/main/examples/schelling) example. It enables a simulation run to be "cached" or in other words recorded. The recorded simulation run is persisted on the local file system and can be replayed at any later point. \ No newline at end of file From 78a84ad87d9d4d159aa14d03ef840d7bb651cacb Mon Sep 17 00:00:00 2001 From: Punya-B-R Date: Sun, 8 Mar 2026 20:13:47 +0530 Subject: [PATCH 4/5] Fix ruff linting errors --- examples/axelrod_culture/app.py | 46 +++----------- .../axelrod_culture/axelrod_culture/agents.py | 20 ++++-- .../axelrod_culture/axelrod_culture/model.py | 63 ++++++++++++------- 3 files changed, 65 insertions(+), 64 deletions(-) diff --git a/examples/axelrod_culture/app.py b/examples/axelrod_culture/app.py index 737943888..ea588196e 100644 --- a/examples/axelrod_culture/app.py +++ b/examples/axelrod_culture/app.py @@ -1,15 +1,15 @@ import hashlib - -import numpy as np import solara -from axelrod_culture.model import AxelrodModel, number_of_cultural_regions +import numpy as np from matplotlib.figure import Figure + from mesa.visualization import SolaraViz, make_plot_component +from axelrod_culture.model import AxelrodModel, number_of_cultural_regions def culture_to_color(culture): key = str(culture).encode() - h = hashlib.md5(key).hexdigest() + h = hashlib.sha256(key).hexdigest() return (int(h[0:2], 16) / 255, int(h[2:4], 16) / 255, int(h[4:6], 16) / 255) @@ -32,38 +32,10 @@ def make_culture_grid(model): model_params = { "rng": {"type": "InputText", "value": 42, "label": "Random Seed"}, - "width": { - "type": "SliderInt", - "value": 10, - "label": "Grid Width", - "min": 5, - "max": 20, - "step": 1, - }, - "height": { - "type": "SliderInt", - "value": 10, - "label": "Grid Height", - "min": 5, - "max": 20, - "step": 1, - }, - "F": { - "type": "SliderInt", - "value": 3, - "label": "Features (F)", - "min": 2, - "max": 10, - "step": 1, - }, - "Q": { - "type": "SliderInt", - "value": 3, - "label": "Traits per feature (Q)", - "min": 2, - "max": 15, - "step": 1, - }, + "width": {"type": "SliderInt", "value": 10, "label": "Grid Width", "min": 5, "max": 20, "step": 1}, + "height": {"type": "SliderInt", "value": 10, "label": "Grid Height", "min": 5, "max": 20, "step": 1}, + "f": {"type": "SliderInt", "value": 3, "label": "Features (F)", "min": 2, "max": 10, "step": 1}, + "q": {"type": "SliderInt", "value": 3, "label": "Traits per feature (Q)", "min": 2, "max": 15, "step": 1}, } model = AxelrodModel() @@ -74,4 +46,4 @@ def make_culture_grid(model): model_params=model_params, name="Axelrod Culture Model", ) -page # noqa +page # noqa \ No newline at end of file diff --git a/examples/axelrod_culture/axelrod_culture/agents.py b/examples/axelrod_culture/axelrod_culture/agents.py index 1e1b82c77..9e0570785 100644 --- a/examples/axelrod_culture/axelrod_culture/agents.py +++ b/examples/axelrod_culture/axelrod_culture/agents.py @@ -2,23 +2,31 @@ class CultureAgent(Agent): + """An agent with a cultural profile made of f features, each with q possible traits. + + Two agents interact with probability equal to their cultural similarity + (fraction of features they share). If they interact, the focal agent + copies one randomly chosen differing feature from the neighbor. + + Attributes: + culture (list[int]): List of f integers, each in range [0, q) + """ + def __init__(self, model, culture): super().__init__(model) self.culture = list(culture) def similarity(self, other): + """Return fraction of features shared with another agent (0.0 to 1.0).""" matches = sum(a == b for a, b in zip(self.culture, other.culture)) return matches / len(self.culture) def interact_with(self, other): + """Interact with another agent based on cultural similarity.""" sim = self.similarity(other) if sim == 0.0 or sim == 1.0: return if self.random.random() < sim: - differing = [ - i - for i in range(len(self.culture)) - if self.culture[i] != other.culture[i] - ] + differing = [i for i in range(len(self.culture)) if self.culture[i] != other.culture[i]] feature = self.random.choice(differing) - self.culture[feature] = other.culture[feature] + self.culture[feature] = other.culture[feature] \ No newline at end of file diff --git a/examples/axelrod_culture/axelrod_culture/model.py b/examples/axelrod_culture/axelrod_culture/model.py index fb4abf3d3..3180e29ff 100644 --- a/examples/axelrod_culture/axelrod_culture/model.py +++ b/examples/axelrod_culture/axelrod_culture/model.py @@ -1,14 +1,25 @@ """ Axelrod Culture Model ===================== -Models how local cultural interactions produce global polarization. -Each agent has F features, each with Q possible traits. Agents interact -with neighbors with probability equal to cultural similarity, copying -one differing trait if interaction occurs. + +Models how local cultural interactions can produce global polarization. +Each agent has a "culture" consisting of f features, each taking one of +q possible integer traits. At each step, an agent picks a random neighbor +and interacts with probability equal to their cultural similarity. If they +interact, the agent copies one of the neighbor's differing traits. + +Despite the tendency toward local convergence, stable cultural regions +can persist -- a phenomenon Axelrod called the "culture problem": +local homogenization coexisting with global diversity. + +Key result: the number of stable cultural regions decreases with f +(more features -> more convergence) and increases with q (more traits +per feature -> more diversity and fragmentation). Reference: - Axelrod, R. (1997). The dissemination of culture. - Journal of Conflict Resolution, 41(2), 203-226. + Axelrod, R. (1997). The dissemination of culture: A model with local + convergence and global polarization. Journal of Conflict Resolution, + 41(2), 203-226. """ from mesa import Model @@ -19,10 +30,12 @@ def number_of_cultural_regions(model): + """Count distinct stable cultural regions using flood fill on the grid.""" visited = set() regions = 0 agent_by_pos = { - (int(a.cell.coordinate[0]), int(a.cell.coordinate[1])): a for a in model.agents + (int(a.cell.coordinate[0]), int(a.cell.coordinate[1])): a + for a in model.agents } for pos in agent_by_pos: if pos in visited: @@ -33,29 +46,39 @@ def number_of_cultural_regions(model): cx, cy = queue.pop() for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: npos = (cx + dx, cy + dy) - if npos not in visited and npos in agent_by_pos: - if agent_by_pos[npos].culture == agent_by_pos[(cx, cy)].culture: - visited.add(npos) - queue.append(npos) + if npos not in visited and npos in agent_by_pos and agent_by_pos[npos].culture == agent_by_pos[(cx, cy)].culture: + visited.add(npos) + queue.append(npos) regions += 1 return regions class AxelrodModel(Model): - def __init__(self, width=10, height=10, F=3, Q=3, rng=None): + """Axelrod's model of cultural dissemination on a grid. + + Attributes: + width (int): Grid width + height (int): Grid height + f (int): Number of cultural features per agent + q (int): Number of possible traits per feature + grid: OrthogonalVonNeumannGrid containing agents + """ + + def __init__(self, width=10, height=10, f=3, q=3, rng=None): super().__init__(rng=rng) + self.width = width self.height = height - self.F = F - self.Q = Q + self.f = f + self.q = q - self.grid = OrthogonalVonNeumannGrid( - (width, height), torus=False, random=self.random - ) + self.grid = OrthogonalVonNeumannGrid((width, height), torus=False, random=self.random) cultures = [ - [self.random.randrange(Q) for _ in range(F)] for _ in range(width * height) + [self.random.randrange(q) for _ in range(f)] + for _ in range(width * height) ] + CultureAgent.create_agents(self, width * height, cultures) for agent, cell in zip(self.agents, self.grid.all_cells.cells): @@ -69,12 +92,10 @@ def __init__(self, width=10, height=10, F=3, Q=3, rng=None): def step(self): agent_list = list(self.agents) - # Run N random pairwise interactions per step (one per agent on average) for _ in range(self.width * self.height): agent = self.random.choice(agent_list) neighbors = [a for a in agent.cell.neighborhood.agents if a is not agent] if neighbors: neighbor = self.random.choice(neighbors) agent.interact_with(neighbor) - - self.datacollector.collect(self) + self.datacollector.collect(self) \ No newline at end of file From fa83b22675a8093fca02e8155462632e402cdf1a Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 8 Mar 2026 14:44:04 +0000 Subject: [PATCH 5/5] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- examples/axelrod_culture/app.py | 44 +++++++++++++++---- .../axelrod_culture/axelrod_culture/agents.py | 8 +++- .../axelrod_culture/axelrod_culture/model.py | 18 +++++--- 3 files changed, 53 insertions(+), 17 deletions(-) diff --git a/examples/axelrod_culture/app.py b/examples/axelrod_culture/app.py index ea588196e..2f6bd1f6c 100644 --- a/examples/axelrod_culture/app.py +++ b/examples/axelrod_culture/app.py @@ -1,10 +1,10 @@ import hashlib -import solara + import numpy as np +import solara +from axelrod_culture.model import AxelrodModel, number_of_cultural_regions from matplotlib.figure import Figure - from mesa.visualization import SolaraViz, make_plot_component -from axelrod_culture.model import AxelrodModel, number_of_cultural_regions def culture_to_color(culture): @@ -32,10 +32,38 @@ def make_culture_grid(model): model_params = { "rng": {"type": "InputText", "value": 42, "label": "Random Seed"}, - "width": {"type": "SliderInt", "value": 10, "label": "Grid Width", "min": 5, "max": 20, "step": 1}, - "height": {"type": "SliderInt", "value": 10, "label": "Grid Height", "min": 5, "max": 20, "step": 1}, - "f": {"type": "SliderInt", "value": 3, "label": "Features (F)", "min": 2, "max": 10, "step": 1}, - "q": {"type": "SliderInt", "value": 3, "label": "Traits per feature (Q)", "min": 2, "max": 15, "step": 1}, + "width": { + "type": "SliderInt", + "value": 10, + "label": "Grid Width", + "min": 5, + "max": 20, + "step": 1, + }, + "height": { + "type": "SliderInt", + "value": 10, + "label": "Grid Height", + "min": 5, + "max": 20, + "step": 1, + }, + "f": { + "type": "SliderInt", + "value": 3, + "label": "Features (F)", + "min": 2, + "max": 10, + "step": 1, + }, + "q": { + "type": "SliderInt", + "value": 3, + "label": "Traits per feature (Q)", + "min": 2, + "max": 15, + "step": 1, + }, } model = AxelrodModel() @@ -46,4 +74,4 @@ def make_culture_grid(model): model_params=model_params, name="Axelrod Culture Model", ) -page # noqa \ No newline at end of file +page # noqa diff --git a/examples/axelrod_culture/axelrod_culture/agents.py b/examples/axelrod_culture/axelrod_culture/agents.py index 9e0570785..7190d9942 100644 --- a/examples/axelrod_culture/axelrod_culture/agents.py +++ b/examples/axelrod_culture/axelrod_culture/agents.py @@ -27,6 +27,10 @@ def interact_with(self, other): if sim == 0.0 or sim == 1.0: return if self.random.random() < sim: - differing = [i for i in range(len(self.culture)) if self.culture[i] != other.culture[i]] + differing = [ + i + for i in range(len(self.culture)) + if self.culture[i] != other.culture[i] + ] feature = self.random.choice(differing) - self.culture[feature] = other.culture[feature] \ No newline at end of file + self.culture[feature] = other.culture[feature] diff --git a/examples/axelrod_culture/axelrod_culture/model.py b/examples/axelrod_culture/axelrod_culture/model.py index 3180e29ff..066b60a7b 100644 --- a/examples/axelrod_culture/axelrod_culture/model.py +++ b/examples/axelrod_culture/axelrod_culture/model.py @@ -34,8 +34,7 @@ def number_of_cultural_regions(model): visited = set() regions = 0 agent_by_pos = { - (int(a.cell.coordinate[0]), int(a.cell.coordinate[1])): a - for a in model.agents + (int(a.cell.coordinate[0]), int(a.cell.coordinate[1])): a for a in model.agents } for pos in agent_by_pos: if pos in visited: @@ -46,7 +45,11 @@ def number_of_cultural_regions(model): cx, cy = queue.pop() for dx, dy in [(-1, 0), (1, 0), (0, -1), (0, 1)]: npos = (cx + dx, cy + dy) - if npos not in visited and npos in agent_by_pos and agent_by_pos[npos].culture == agent_by_pos[(cx, cy)].culture: + if ( + npos not in visited + and npos in agent_by_pos + and agent_by_pos[npos].culture == agent_by_pos[(cx, cy)].culture + ): visited.add(npos) queue.append(npos) regions += 1 @@ -72,11 +75,12 @@ def __init__(self, width=10, height=10, f=3, q=3, rng=None): self.f = f self.q = q - self.grid = OrthogonalVonNeumannGrid((width, height), torus=False, random=self.random) + self.grid = OrthogonalVonNeumannGrid( + (width, height), torus=False, random=self.random + ) cultures = [ - [self.random.randrange(q) for _ in range(f)] - for _ in range(width * height) + [self.random.randrange(q) for _ in range(f)] for _ in range(width * height) ] CultureAgent.create_agents(self, width * height, cultures) @@ -98,4 +102,4 @@ def step(self): if neighbors: neighbor = self.random.choice(neighbors) agent.interact_with(neighbor) - self.datacollector.collect(self) \ No newline at end of file + self.datacollector.collect(self)