new cellular automaton

This commit is contained in:
Richard Feistenauer 2018-12-01 20:37:18 +01:00
parent facc15cebc
commit 390c95df30
12 changed files with 193 additions and 683 deletions

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.7" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.7" project-jdk-type="Python SDK" />
</project>

View File

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/WorldGeneration.iml" filepath="$PROJECT_DIR$/.idea/WorldGeneration.iml" />
</modules>
</component>
</project>

View File

@ -1,405 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="5a936069-819f-4c75-93de-a950897acf14" name="Default Changelist" comment="" />
<ignored path="$PROJECT_DIR$/venv/" />
<option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FUSProjectUsageTrigger">
<session id="1685401553">
<usages-collector id="statistics.lifecycle.project">
<counts>
<entry key="project.closed" value="1" />
<entry key="project.open.time.1" value="1" />
<entry key="project.open.time.4" value="1" />
<entry key="project.open.time.5" value="2" />
<entry key="project.opened" value="4" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.extensions.open">
<counts>
<entry key="py" value="11" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.types.open">
<counts>
<entry key="Python" value="11" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.extensions.edit">
<counts>
<entry key="dummy" value="25" />
<entry key="py" value="2139" />
</counts>
</usages-collector>
<usages-collector id="statistics.file.types.edit">
<counts>
<entry key="PLAIN_TEXT" value="25" />
<entry key="Python" value="2139" />
</counts>
</usages-collector>
</session>
</component>
<component name="FileEditorManager">
<leaf SIDE_TABS_SIZE_LIMIT_KEY="300">
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/world_generator.py">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret selection-end-column="22" />
<folding>
<element signature="e#24#38#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="true">
<entry file="file://$PROJECT_DIR$/scripts/main_ui.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="272">
<caret line="16" column="59" selection-start-line="16" selection-start-column="59" selection-end-line="16" selection-end-column="59" />
<folding>
<element signature="e#24#37#0" expanded="true" />
<marker date="1543672995350" expanded="true" signature="132:425" ph="..." />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/__init__.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/cellular_automaton.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="680">
<caret line="44" column="87" selection-start-line="44" selection-start-column="87" selection-end-line="44" selection-end-column="87" />
<folding>
<element signature="e#0#16#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/ca_rule.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="170">
<caret line="10" column="39" selection-start-line="10" selection-start-column="39" selection-end-line="10" selection-end-column="39" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/ca_neighberhood.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="51">
<caret line="3" column="63" selection-start-line="3" selection-start-column="55" selection-end-line="3" selection-end-column="63" />
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/ca_grid.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="629">
<caret line="38" column="10" selection-start-line="38" selection-start-column="9" selection-end-line="38" selection-end-column="10" />
<folding>
<element signature="e#0#45#0" expanded="true" />
<marker date="1540986500017" expanded="true" signature="3484:3522" ph="..." />
</folding>
</state>
</provider>
</entry>
</file>
<file pinned="false" current-in-tab="false">
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/ca_cell.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="17">
<caret line="1" column="43" selection-start-line="1" selection-start-column="43" selection-end-line="1" selection-end-column="43" />
</state>
</provider>
</entry>
</file>
</leaf>
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="FindInProjectRecents">
<findStrings>
<find>get_coordinate_from_index</find>
<find>print</find>
</findStrings>
</component>
<component name="IdeDocumentHistory">
<option name="CHANGED_PATHS">
<list>
<option value="$PROJECT_DIR$/src/cellular_automaton/ca_neighberhood.py" />
<option value="$PROJECT_DIR$/src/cellular_automaton/ca_cell.py" />
<option value="$PROJECT_DIR$/src/cellular_automaton/ca_rule.py" />
<option value="$PROJECT_DIR$/src/cellular_automaton/ca_grid.py" />
<option value="$PROJECT_DIR$/src/cellular_automaton/cellular_automaton.py" />
<option value="$PROJECT_DIR$/src/world_generator.py" />
<option value="$PROJECT_DIR$/src/cellular_automaton/__init__.py" />
<option value="$PROJECT_DIR$/src/cellular_automaton/pygame_main.py" />
<option value="$PROJECT_DIR$/scripts/main_ui.py" />
</list>
</option>
</component>
<component name="ProjectFrameBounds" extendedState="6">
<option name="x" value="442" />
<option name="y" value="-11" />
<option name="width" value="1400" />
<option name="height" value="1000" />
</component>
<component name="ProjectView">
<navigator proportions="" version="1">
<foldersAlwaysOnTop value="true" />
</navigator>
<panes>
<pane id="Scope" />
<pane id="ProjectPane">
<subPane>
<expand>
<path>
<item name="WorldGeneration" type="b2602c69:ProjectViewProjectNode" />
<item name="WorldGeneration" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="WorldGeneration" type="b2602c69:ProjectViewProjectNode" />
<item name="WorldGeneration" type="462c0819:PsiDirectoryNode" />
<item name="images" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="WorldGeneration" type="b2602c69:ProjectViewProjectNode" />
<item name="WorldGeneration" type="462c0819:PsiDirectoryNode" />
<item name="scripts" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="WorldGeneration" type="b2602c69:ProjectViewProjectNode" />
<item name="WorldGeneration" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
</path>
<path>
<item name="WorldGeneration" type="b2602c69:ProjectViewProjectNode" />
<item name="WorldGeneration" type="462c0819:PsiDirectoryNode" />
<item name="src" type="462c0819:PsiDirectoryNode" />
<item name="cellular_automaton" type="462c0819:PsiDirectoryNode" />
</path>
</expand>
<select />
</subPane>
</pane>
</panes>
</component>
<component name="PropertiesComponent">
<property name="last_opened_file_path" value="$PROJECT_DIR$" />
<property name="settings.editor.selected.configurable" value="com.jetbrains.python.configuration.PyActiveSdkModuleConfigurable" />
</component>
<component name="RecentsManager">
<key name="MoveFile.RECENT_KEYS">
<recent name="D:\DamKoVosh\Eigene Dokumente\Programming\WorldGeneration\scripts" />
</key>
</component>
<component name="RunDashboard">
<option name="ruleStates">
<list>
<RuleState>
<option name="name" value="ConfigurationTypeDashboardGroupingRule" />
</RuleState>
<RuleState>
<option name="name" value="StatusDashboardGroupingRule" />
</RuleState>
</list>
</option>
</component>
<component name="RunManager" selected="Python.main_ui">
<configuration name="main_ui" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="WorldGeneration" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/scripts" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/scripts/main_ui.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<configuration name="world_generator" type="PythonConfigurationType" factoryName="Python" temporary="true">
<module name="WorldGeneration" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/src" />
<option name="IS_MODULE_SDK" value="true" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/src/world_generator.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
<list>
<item itemvalue="Python.world_generator" />
<item itemvalue="Python.main_ui" />
</list>
<recent_temporary>
<list>
<item itemvalue="Python.main_ui" />
<item itemvalue="Python.world_generator" />
</list>
</recent_temporary>
</component>
<component name="SvnConfiguration">
<configuration />
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="5a936069-819f-4c75-93de-a950897acf14" name="Default Changelist" comment="" />
<created>1540970314178</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1540970314178</updated>
</task>
<servers />
</component>
<component name="TodoView">
<todo-panel id="selected-file">
<is-autoscroll-to-source value="true" />
</todo-panel>
<todo-panel id="all">
<are-packages-shown value="true" />
<is-autoscroll-to-source value="true" />
</todo-panel>
</component>
<component name="ToolWindowManager">
<frame x="1912" y="-6" width="1936" height="1056" extended-state="6" />
<editor active="true" />
<layout>
<window_info content_ui="combo" id="Project" order="0" visible="true" weight="0.24973656" />
<window_info id="Structure" order="1" side_tool="true" weight="0.25" />
<window_info id="Favorites" order="2" side_tool="true" />
<window_info anchor="bottom" id="Message" order="0" />
<window_info anchor="bottom" id="Find" order="1" />
<window_info active="true" anchor="bottom" id="Run" order="2" sideWeight="0.49894625" visible="true" weight="0.329718" />
<window_info anchor="bottom" id="Debug" order="3" weight="0.4" />
<window_info anchor="bottom" id="Cvs" order="4" weight="0.25" />
<window_info anchor="bottom" id="Inspection" order="5" weight="0.4" />
<window_info anchor="bottom" id="TODO" order="6" weight="0.329718" />
<window_info anchor="bottom" id="Version Control" order="7" show_stripe_button="false" />
<window_info anchor="bottom" id="Terminal" order="8" sideWeight="0.49894625" weight="0.329718" />
<window_info anchor="bottom" id="Event Log" order="9" sideWeight="0.50105375" side_tool="true" visible="true" weight="0.329718" />
<window_info anchor="bottom" id="Python Console" order="10" sideWeight="0.49947312" weight="0.329718" />
<window_info anchor="right" id="Commander" internal_type="SLIDING" order="0" type="SLIDING" weight="0.4" />
<window_info anchor="right" id="Ant Build" order="1" weight="0.25" />
<window_info anchor="right" content_ui="combo" id="Hierarchy" order="2" weight="0.25" />
</layout>
</component>
<component name="VcsContentAnnotationSettings">
<option name="myLimit" value="2678400000" />
</component>
<component name="editorHistoryManager">
<entry file="file://$USER_HOME$/AppData/Roaming/Python/Python37/site-packages/pygame/__init__.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="221">
<caret line="143" selection-start-line="143" selection-end-line="143" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/world_generator.py">
<provider selected="true" editor-type-id="text-editor">
<state>
<caret selection-end-column="22" />
<folding>
<element signature="e#24#38#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/__init__.py">
<provider selected="true" editor-type-id="text-editor" />
</entry>
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/cellular_automaton.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="680">
<caret line="44" column="87" selection-start-line="44" selection-start-column="87" selection-end-line="44" selection-end-column="87" />
<folding>
<element signature="e#0#16#0" expanded="true" />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/ca_rule.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="170">
<caret line="10" column="39" selection-start-line="10" selection-start-column="39" selection-end-line="10" selection-end-column="39" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/ca_neighberhood.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="51">
<caret line="3" column="63" selection-start-line="3" selection-start-column="55" selection-end-line="3" selection-end-column="63" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/ca_grid.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="629">
<caret line="38" column="10" selection-start-line="38" selection-start-column="9" selection-end-line="38" selection-end-column="10" />
<folding>
<element signature="e#0#45#0" expanded="true" />
<marker date="1540986500017" expanded="true" signature="3484:3522" ph="..." />
</folding>
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/src/cellular_automaton/ca_cell.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="17">
<caret line="1" column="43" selection-start-line="1" selection-start-column="43" selection-end-line="1" selection-end-column="43" />
</state>
</provider>
</entry>
<entry file="file://$PROJECT_DIR$/scripts/main_ui.py">
<provider selected="true" editor-type-id="text-editor">
<state relative-caret-position="272">
<caret line="16" column="59" selection-start-line="16" selection-start-column="59" selection-end-line="16" selection-end-column="59" />
<folding>
<element signature="e#24#37#0" expanded="true" />
<marker date="1543672995350" expanded="true" signature="132:425" ph="..." />
</folding>
</state>
</provider>
</entry>
</component>
</project>

View File

@ -3,7 +3,10 @@
import pygame import pygame
import random import random
from cellular_automaton import cellular_automaton from cellular_automaton.cellular_automaton import CellularAutomaton
from cellular_automaton.ca_cell import CACell
from cellular_automaton.ca_grid import CAGrid
from cellular_automaton.ca_rule import CARule
class WorldGeneratorWindow: class WorldGeneratorWindow:
@ -44,5 +47,19 @@ def main():
running = False running = False
class TestRule(CARule):
def evolve_cell(self, cell, neighbors):
if neighbors[1][0] != 0:
return [1]
else:
return [0]
if __name__ == "__main__": if __name__ == "__main__":
main() main()
dim = [200, 500]
ca = CellularAutomaton(2)
new_grid = CAGrid(dim)
new_grid.set_cell_by_coordinate([1, 1], CACell([1]))
rule = TestRule()

View File

@ -1,19 +1,7 @@
class CACell: class Cell:
def __init__(self, initial_state=None): def __init__(self, name: str):
if initial_state: self.name = name
assert isinstance(initial_state, (tuple, list)) self.neighbours = []
self._state = initial_state
else:
self._state = [0]
def __getitem__(self, index): def set_neighbours(self, neighbours: list):
return self._state[index] self.neighbours = neighbours
def __setitem__(self, index, value):
self._state[index] = value
def __len__(self):
return len(self._state)
def __str__(self):
return str(self._state)

View File

@ -1,124 +1,78 @@
from cellular_automaton.ca_cell import CACell from cellular_automaton.ca_cell import Cell
from functools import reduce from cellular_automaton.ca_neighborhood import CellularAutomatonNeighborhood
class CAGrid: class Grid:
def __init__(self, dimensions, initial_grid_state=None): def __init__(self, dimension: list, neighborhood: CellularAutomatonNeighborhood):
assert isinstance(dimensions, list) self._dimension = dimension
assert len(dimensions) > 0 self._cells = {}
self._dimensions = dimensions self._neighborhood = neighborhood
self._cell_count = reduce(lambda x, y: x*y, dimensions) self._create_cells()
self._set_cell_neighbours()
if initial_grid_state: self._active_cells = {}
assert isinstance(initial_grid_state, list) self.set_all_cells_active()
assert len(initial_grid_state) == self._cell_count
assert isinstance(initial_grid_state[0], CACell)
self._grid = initial_grid_state
else:
self._grid = []
for i in range(self._cell_count): def set_all_cells_active(self):
self._grid.append(CACell()) for cell_key in self._cells:
self._active_cells[cell_key] = 1
def get_index_from_coordinate(self, coordinate): def get_active_cells(self):
""" Convert a coordinate to the index in the grid list. return self._active_cells.keys()
:param coordinate: A tuple or list with the position of the cell.
Has to have the same dimension as the grid. def get_cell_and_neighbors(self, cell_name):
:return: The index of the cell at the coordinates cell = self._cells[cell_name]
neighbours = cell.neighbours
neighbour_objects = []
for ne in neighbours:
neighbour_objects.append(self._cells[ne])
return [cell, neighbour_objects]
def _create_cells(self, dimension_index=0, coordinate=None):
""" Recursively steps down the dimensions to create cells in n dimensions and adds them to a dict.
:param dimension_index: The index indicating which dimension is currently traversed.
:param coordinate: The coordinate generated so far.
(each recursion adds one dimension to the coordinate.
""" """
assert len(self._dimensions) == len(coordinate) coordinate = self.instantiate_coordinate_if_necessary(coordinate)
index = 0
for i, c in enumerate(coordinate[1:]):
index += c * reduce(lambda x, y: x * y, self._dimensions[:i+1])
index += coordinate[0]
return index
def get_coordinate_from_index(self, index): try:
""" Convert an index to the coordinate in the grid list. self._recursive_step_down_dimensions(coordinate, dimension_index, self._create_cells)
:param index: The Index of the cell in the grid. except IndexError:
:return: The coordinate pointing at the indexed cell in the grid. coordinate_string = '-'.join(coordinate)
self._cells[coordinate_string] = Cell(coordinate_string)
def _recursive_step_down_dimensions(self, coordinate, dimension_index, recursion_method):
""" For the range of the current dimension, recalls the recursion method.
:param coordinate: The coordinate so far.
:param dimension_index: The current dimension lvl.
:param recursion_method: The method to call for recursion.
""" """
coordinate = len(self._dimensions)*[0] for cell_index in range(self._dimension[dimension_index]):
for i, d in enumerate(self._dimensions): coordinate.append(cell_index)
coordinate[-(i + 1)] = index // reduce(lambda x, y: x * y, self._dimensions[-(i + 1):]) recursion_method(dimension_index + 1, coordinate.copy())
index = index % reduce(lambda x, y: x * y, self._dimensions[-(i + 1):])
coordinate[0] = index @staticmethod
def instantiate_coordinate_if_necessary(coordinate):
if coordinate is None:
coordinate = []
return coordinate return coordinate
def get_cell_by_coordinate(self, coordinate): def _set_cell_neighbours(self, dimension_index=0, coordinate=None):
""" Read a cell using a list or tuple as reference """ Recursively steps down the dimensions to get the string instances for each cells neighbours.
:param coordinate A tuple or list with the position of the cell. :param dimension_index: The index indicating which dimension is currently traversed.
Has to have the same dimension as the grid. :param coordinate: The coordinate generated so far.
:return: The CACell at the coordinate in the grid. (each recursion adds one dimension to the coordinate.
""" """
coordinate = self.instantiate_coordinate_if_necessary(coordinate)
try: try:
return self[self.get_index_from_coordinate(coordinate)] self._recursive_step_down_dimensions(coordinate, dimension_index, self._set_cell_neighbours)
except IndexError: except IndexError:
return None neighbours_coordinates = self._neighborhood.get_neighbor_coordinates(coordinate, self._dimension)
neighbour_names = [self._cells['-'.join(nc)].name for nc in neighbours_coordinates]
self._cells['-'.join(coordinate)].set_neighbours(neighbour_names)
def get_all_neighbour_cells(self, position, neighborhood):
""" Get a list with all cells defined by the neighborhood.
:param position: The position as index or coordinate.
:param neighborhood: The neighborhood definition as tuple.
:return: All Cells defined by the neighborhood in a list.
"""
if isinstance(position, (tuple, list)):
coordinate = position[:]
else:
coordinate = self.get_coordinate_from_index(position)
neighbors = []
for neighbor in neighborhood:
neighbor_coordinate = []
for i, (c, nc) in enumerate(zip(coordinate, neighbor)):
coord = c + nc
if coord < 0:
coord = self._dimensions[i] - 1
elif coord == self._dimensions[i]:
coord = 0
neighbor_coordinate.append(coord)
index_ = self.get_cell_by_coordinate(neighbor_coordinate)
if index_:
neighbors.append(index_)
return neighbors
def get_dimensions(self):
return self._dimensions
def set_cell_by_coordinate(self, coordinate, value):
""" Write to a cell using a list or tuple as reference
:param coordinate A tuple or list with the position of the cell.
Has to have the same dimension as the grid.
"""
try:
self._grid[self.get_index_from_coordinate(coordinate)] = value
except IndexError:
return None
def __eq__(self, other):
if len(self._grid) != len(other):
return False
for i in self._cell_count:
if self._grid[i] != other[i]:
return False
return True
def __len__(self):
return len(self._grid)
def __getitem__(self, index):
return self._grid[int(index)]
def __setitem__(self, index, value):
self._grid[index] = value
def __str__(self):
return str(self._grid)

View File

@ -1,7 +0,0 @@
class Neighborhood:
MOOR_2_X_2 = [[-1, -1], [-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, 1]]
def __init__(self):
pass

View File

@ -0,0 +1,64 @@
from enum import Enum
class EdgeRule(Enum):
IGNORE_MISSING_NEIGHBORS_OF_EDGE_CELLS = 0
IGNORE_EDGE_CELLS = 1
FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS = 2
class CellularAutomatonNeighborhood:
def __init__(self, neighbors: list, edge_rule: EdgeRule):
self._neighbors = neighbors
self.edge_rule = edge_rule
self.dimensions = []
def get_relative_neighbor_coordinates(self):
return self._neighbors
def get_neighbor_coordinates(self, cell_coordinate, dimensions):
self.dimensions = dimensions
if not self._does_ignore_edge_cell_rule_apply(cell_coordinate):
return self._apply_edge_rule_to_neighbours_of(cell_coordinate)
def _does_ignore_edge_cell_rule_apply(self, coordinate):
if self.edge_rule == EdgeRule.IGNORE_EDGE_CELLS and self._is_coordinate_on_an_edge(coordinate):
return True
return False
def _is_coordinate_on_an_edge(self, coordinate):
for nd, d in zip(coordinate, self.dimensions):
if nd == 0 or nd == d - 1:
return True
return False
def _apply_edge_rule_to_neighbours_of(self, cell_coordinate):
remaining_neighbours = []
for neighbour in self._neighbors:
if not self._does_ignore_edge_cell_neighbours_rule_apply(neighbour, cell_coordinate):
remaining_neighbours.append(self._calculate_neighbour_coordinate(neighbour, cell_coordinate))
def _does_ignore_edge_cell_neighbours_rule_apply(self, neighbour, cell_coordinate):
if self.edge_rule == EdgeRule.IGNORE_MISSING_NEIGHBORS_OF_EDGE_CELLS:
for rel_nd, cd, d in zip(neighbour, cell_coordinate, self.dimensions):
nd = cd + rel_nd
if nd < 0 or nd >= d:
return True
return False
def _calculate_neighbour_coordinate(self, neighbour, cell_coordinate):
for rel_nd, cd, d in zip(neighbour, cell_coordinate, self.dimensions):
nd = cd + rel_nd
if nd < 0:
nd = d - 1
elif nd >= d:
nd = 0
return nd
class MooreNeighborhood(CellularAutomatonNeighborhood):
def __init__(self, edge_rule: EdgeRule):
super().__init__([[-1, -1], [0, -1], [1, -1],
[-1, 0], [0, 0], [1, 0],
[-1, 1], [0, 1], [1, 1]],
edge_rule)

View File

@ -1,13 +1,9 @@
import abc from cellular_automaton.ca_cell import Cell
class CARule(abc.ABC): class Rule:
@abc.abstractmethod def __init__(self):
def evolve_cell(self, cell, neighbors): pass
"""
Evolves a cell and returns its new state as list of states. def evolve_cell(self, cell: Cell, neighbours: list):
:param cell: The cell to evolve.
:param neighbors: A list of its neighbors.
:return: The new state list.
"""
pass pass

View File

@ -1,50 +1,66 @@
import threading import threading
import time import time
from cellular_automaton.ca_cell import CACell from cellular_automaton.ca_grid import Grid
from cellular_automaton.ca_grid import CAGrid from cellular_automaton.ca_rule import Rule
class CellularAutomaton: class CellularAutomaton:
def __init__(self, threads=1): def __init__(self, dimension: list, rule_: Rule=None, thread_count: int=4):
assert threads > 0 self.grid = Grid(dimension)
self._thread_count = threads self._rule = rule_
self._thread_count=thread_count
def evolve(self, grid, neighborhood, rule): def set_rule(self, rule: Rule):
range_ = int(len(grid) / self._thread_count) self._rule = rule
def set_thread_count(self, thread_count: int):
self._thread_count = thread_count
def evolve(self):
cell_lists_for_threats = self.create_cell_lists_for_threads()
threads = self._start_treads_to_evolve_grid(cell_lists_for_threats)
self._wait_for_all_threads_to_finish(threads)
def create_cell_lists_for_threads(self):
active_cells = self.grid.get_active_cells()
cell_count_per_thread = int(len(active_cells) / self._thread_count)
return self.divide_active_cells(cell_count_per_thread, active_cells)
@staticmethod
def divide_active_cells(cell_count_per_thread, active_cells):
return [active_cells[i:i + cell_count_per_thread]
for i in range(0, len(active_cells), cell_count_per_thread)]
def _start_treads_to_evolve_grid(self, cell_lists_for_threats):
threads = [] threads = []
for t in range(self._thread_count): for t in range(self._thread_count):
new_thread = EvolutionThread(grid, neighborhood, rule, [t * range_, t * range_ + range_]) new_thread = _EvolutionThread(self.grid, self._rule, cell_lists_for_threats[t])
threads.append(new_thread) threads.append(new_thread)
new_thread.start() new_thread.start()
return threads
new_grid_state = [] @staticmethod
def _wait_for_all_threads_to_finish(threads):
for thread in threads: for thread in threads:
while not thread.is_finished(): while not thread.is_finished():
time.sleep(0.01) time.sleep(0.01)
new_grid_state.extend(thread.get_new_cell_states())
thread.join() thread.join()
return CAGrid(grid.get_dimensions(), new_grid_state)
class _EvolutionThread(threading.Thread):
class EvolutionThread(threading.Thread): def __init__(self, grid: Grid, rule: Rule, cell_list: list):
def __init__(self, grid, neighborhood, rule, range_): super(_EvolutionThread, self).__init__()
super(EvolutionThread, self).__init__()
self._grid = grid self._grid = grid
self._neighborhood = neighborhood
self._rule = rule self._rule = rule
self._range = range_ self._cell_list = cell_list
self._next_state = [] self._next_state = []
self._finished = False self._finished = False
def run(self): def run(self):
for cell_id in range(*self._range): for cell in self._cell_list:
neighbors = self._grid.get_all_neighbour_cells(cell_id, self._neighborhood) self._rule.evolve_cell(*self._grid.get_cell_and_neighbors(cell))
cell = self._grid[cell_id]
self._next_state.append(CACell(self._rule.evolve_cell(cell, neighbors)))
self._finished = True self._finished = True
def get_new_cell_states(self): def get_new_cell_states(self):

View File

@ -1,90 +0,0 @@
#!/usr/bin/env python3
import tkinter
from cellular_automaton.cellular_automaton import CellularAutomaton
from cellular_automaton.ca_neighberhood import Neighborhood
from cellular_automaton.ca_rule import CARule
from cellular_automaton.ca_grid import CAGrid
from cellular_automaton.ca_cell import CACell
import datetime
import pygame
UPDATE_RATE = 10
class TkGUI:
def __init__(self):
self.root = tkinter.Tk()
self.canvas = tkinter.Canvas(self.root)
self.canvas.pack(padx=5, pady=5)
self.ca = None
self.current_state = None
self.dimensions = None
self.rule_ = None
self.grid_image = None
def run(self):
self.root.after(10, self.update_state())
self.root.mainloop()
def update_state(self):
a = datetime.datetime.now()
self.current_state = self.ca.evolve(self.current_state, Neighborhood.MOOR_2_X_2, self.rule_)
b = datetime.datetime.now()
self.draw_current_state()
c = datetime.datetime.now()
print(b-a)
print(c-b)
self.root.after(UPDATE_RATE, self.update_state)
def draw_current_state(self):
for idx in range(self.dimensions[0]):
for idy in range(self.dimensions[1]):
cell = self.current_state[idy * self.dimensions[0] + idx]
if cell[0] == 0:
cell_color = "#ffffff"
else:
cell_color = "#444444"
self.grid_image.put(cell_color, (idx, idy))
def start(self, ca_, grid, dimensions, rule_):
self.grid_image = tkinter.PhotoImage(width=dimensions[0], height=dimensions[1])
self.ca = ca_
self.current_state = grid
self.dimensions = dimensions
self.rule_ = rule_
# Clear the canvas (remove all shapes)
self.canvas.delete(tkinter.ALL)
self.draw_current_state()
self.canvas.create_image(self.dimensions, image=self.grid_image, state="normal")
# Draw the canvas
self.draw_current_state()
class TestRule(CARule):
def evolve_cell(self, cell, neighbors):
if neighbors[1][0] != 0:
return [1]
else:
return [0]
#if __name__ == '__main__':
# gui = TkGUI()
# dim = [200, 500]
# ca = CellularAutomaton(2)
#
# new_grid = CAGrid(dim)
# new_grid.set_cell_by_coordinate([1, 1], CACell([1]))
# rule = TestRule()
# gui.start(ca, new_grid, dim, rule)##
#
# gui.run()