second multiprocessing try, better but still bad
This commit is contained in:
parent
be57f89e78
commit
d1820ebc58
@ -10,6 +10,7 @@ class TestRule(Rule):
|
|||||||
try:
|
try:
|
||||||
return last_neighbour_states[0]
|
return last_neighbour_states[0]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
|
print("damn neighbours")
|
||||||
pass
|
pass
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -21,7 +22,7 @@ class MyState(CellState):
|
|||||||
if rand > 99:
|
if rand > 99:
|
||||||
init = 1
|
init = 1
|
||||||
|
|
||||||
super().__init__(init, draw_first_state=False)
|
super().__init__((float(init),), draw_first_state=False)
|
||||||
|
|
||||||
def get_state_draw_color(self, iteration):
|
def get_state_draw_color(self, iteration):
|
||||||
red = 0
|
red = 0
|
||||||
@ -41,7 +42,7 @@ if __name__ == "__main__":
|
|||||||
freeze_support()
|
freeze_support()
|
||||||
random.seed(1000)
|
random.seed(1000)
|
||||||
rule = TestRule()
|
rule = TestRule()
|
||||||
grid = Grid(dimension=[400, 400],
|
grid = Grid(dimension=[200, 200], # best is 400/400 with 0,2 ca speed and 0,09 redraw
|
||||||
neighborhood=MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS),
|
neighborhood=MooreNeighborhood(EdgeRule.FIRST_AND_LAST_CELL_OF_DIMENSION_ARE_NEIGHBORS),
|
||||||
state_class=MyState)
|
state_class=MyState)
|
||||||
ca = CellularAutomaton(grid, rule)
|
ca = CellularAutomaton(grid, rule)
|
||||||
|
@ -1,61 +1,44 @@
|
|||||||
import multiprocessing
|
|
||||||
|
|
||||||
from cellular_automaton.ca_cell_state import CellState
|
from cellular_automaton.ca_cell_state import CellState
|
||||||
|
|
||||||
|
|
||||||
class Cell:
|
class Cell:
|
||||||
def __init__(self, name, state_class: CellState.__class__, coordinate: list):
|
def __init__(self, state_class: CellState.__class__, coordinate: list):
|
||||||
self._name = name
|
|
||||||
self._coordinate = coordinate
|
self._coordinate = coordinate
|
||||||
self._state = state_class()
|
self._state = state_class()
|
||||||
self._neighbours = []
|
self._neighbours = []
|
||||||
self._active = multiprocessing.Value('i', 1)
|
|
||||||
self._age = multiprocessing.Value('i', 0)
|
|
||||||
|
|
||||||
def set_neighbours(self, neighbours):
|
def set_neighbours(self, neighbours):
|
||||||
self._neighbours = neighbours
|
self._neighbours = neighbours
|
||||||
|
|
||||||
|
def get_state(self):
|
||||||
|
return self._state
|
||||||
|
|
||||||
def get_coordinate(self):
|
def get_coordinate(self):
|
||||||
return self._coordinate
|
return self._coordinate
|
||||||
|
|
||||||
def evolve_if_ready(self, rule):
|
def evolve_if_ready(self, rule):
|
||||||
if self._neighbours_are_younger():
|
if self._neighbours_are_younger():
|
||||||
if self._is_active():
|
if self._state.is_active():
|
||||||
new_state = rule(self.get_current_state(), self.get_neighbour_states())
|
new_state = rule(self._state.get_current_state(), self.get_neighbour_states())
|
||||||
self.set_new_state_and_activate(new_state)
|
self.set_new_state_and_activate(new_state)
|
||||||
|
|
||||||
self.increase_age()
|
self._state.increase_age()
|
||||||
|
|
||||||
def _neighbours_are_younger(self):
|
def _neighbours_are_younger(self):
|
||||||
for n in self._neighbours:
|
for n in self._neighbours:
|
||||||
if n.get_age() < self.get_age():
|
if n.get_age() < self._state.get_age():
|
||||||
return False
|
return False
|
||||||
|
return True
|
||||||
def get_age(self):
|
|
||||||
return self._age.value
|
|
||||||
|
|
||||||
def _is_active(self):
|
|
||||||
return self._active.value > self._age.value
|
|
||||||
|
|
||||||
def get_current_state(self):
|
|
||||||
return self._state.get_state_of_iteration(self._age.value)
|
|
||||||
|
|
||||||
def get_neighbour_states(self):
|
def get_neighbour_states(self):
|
||||||
return [n.get_state_from_iteration(self._age.value) for n in self._neighbours]
|
return [n.get_state_of_iteration(self._state.get_age()) for n in self._neighbours]
|
||||||
|
|
||||||
def set_new_state_and_activate(self, new_state: CellState):
|
def set_new_state_and_activate(self, new_state: CellState):
|
||||||
changed = self._state.set_current_state(new_state, self._age.value + 1)
|
changed = self._state.set_current_state(new_state)
|
||||||
if changed:
|
if changed:
|
||||||
self._set_active()
|
self._set_active()
|
||||||
|
|
||||||
def _set_active(self):
|
def _set_active(self):
|
||||||
self.set_active_for_next_iteration(self._age.value)
|
self._state.set_active_for_next_iteration(self._state.get_age() + 1)
|
||||||
for n in self._neighbours:
|
for n in self._neighbours:
|
||||||
n.set_active_for_next_iteration(self._age.value)
|
n.set_active_for_next_iteration(self._state.get_age() + 1)
|
||||||
|
|
||||||
def set_active_for_next_iteration(self, iteration):
|
|
||||||
self._active.value = max(self._active.value, iteration + 1)
|
|
||||||
|
|
||||||
def increase_age(self):
|
|
||||||
with self._age.get_lock():
|
|
||||||
self._age += 1
|
|
||||||
|
@ -2,32 +2,51 @@ from multiprocessing import Array, Value
|
|||||||
|
|
||||||
|
|
||||||
class CellState:
|
class CellState:
|
||||||
|
_state_save_slot_count = 2
|
||||||
"""
|
"""
|
||||||
This is the base class for all cell states.
|
This is the base class for all cell states.
|
||||||
When using the cellular automaton display, inherit this class and implement get_state_draw_color.
|
When using the cellular automaton display, inherit this class and implement get_state_draw_color.
|
||||||
"""
|
"""
|
||||||
def __init__(self, initial_state=(0., ), state_save_slot_count=2, draw_first_state=True):
|
def __init__(self, initial_state=(0., ), draw_first_state=True):
|
||||||
self._state_save_slot_count = state_save_slot_count
|
self._state_slots = [Array('d', initial_state) for i in range(self.__class__._state_save_slot_count)]
|
||||||
self._state_slots = [Array('d', initial_state)] * state_save_slot_count
|
self._active = Value('i', 1)
|
||||||
|
self._age = Value('i', 0)
|
||||||
if draw_first_state:
|
if draw_first_state:
|
||||||
self._dirty = Value('i', 1)
|
self._dirty = Value('i', 1)
|
||||||
else:
|
else:
|
||||||
self._dirty = Value('i', 0)
|
self._dirty = Value('i', 0)
|
||||||
|
|
||||||
|
def get_age(self):
|
||||||
|
return self._age.value
|
||||||
|
|
||||||
|
def get_current_state(self):
|
||||||
|
return self.get_state_of_iteration(self.get_age())
|
||||||
|
|
||||||
|
def get_state_of_iteration(self, iteration):
|
||||||
|
""" Will return the state for the iteration modulo number of saved states.
|
||||||
|
:param iteration: Uses the iteration index, to differ between concurrent states.
|
||||||
|
:return The state for this iteration.
|
||||||
|
"""
|
||||||
|
return self._state_slots[iteration % self.__class__._state_save_slot_count]
|
||||||
|
|
||||||
|
def is_active(self):
|
||||||
|
return self._active.value > self._age.value
|
||||||
|
|
||||||
|
def set_active_for_next_iteration(self, iteration):
|
||||||
|
self._active.value = max(self._active.value, iteration + 1)
|
||||||
|
|
||||||
|
def increase_age(self):
|
||||||
|
with self._age.get_lock():
|
||||||
|
self._age.value += 1
|
||||||
|
|
||||||
def is_set_for_redraw(self):
|
def is_set_for_redraw(self):
|
||||||
return self._dirty != 1
|
return self._dirty != 1
|
||||||
|
|
||||||
def get_state_changes(self):
|
|
||||||
return self._dirty
|
|
||||||
|
|
||||||
def set_for_redraw(self):
|
|
||||||
self._dirty = 1
|
|
||||||
|
|
||||||
def was_redrawn(self):
|
def was_redrawn(self):
|
||||||
self._dirty = 0
|
self._dirty = 0
|
||||||
|
|
||||||
def set_current_state(self, new_state, current_iteration_index):
|
def set_current_state(self, new_state):
|
||||||
return self.set_state_of_iteration(new_state, current_iteration_index)
|
return self.set_state_of_iteration(new_state, self.get_age() + 1)
|
||||||
|
|
||||||
def get_state_of_last_iteration(self, current_iteration_index):
|
def get_state_of_last_iteration(self, current_iteration_index):
|
||||||
return self.get_state_of_iteration(current_iteration_index - 1)
|
return self.get_state_of_iteration(current_iteration_index - 1)
|
||||||
@ -38,10 +57,8 @@ class CellState:
|
|||||||
:param iteration: Uses the iteration index, to differ between concurrent states.
|
:param iteration: Uses the iteration index, to differ between concurrent states.
|
||||||
:return True if state has changed.
|
:return True if state has changed.
|
||||||
"""
|
"""
|
||||||
slot_count = self._state_save_slot_count
|
|
||||||
states = self._state_slots
|
|
||||||
|
|
||||||
current_state = states[iteration % slot_count]
|
current_state = self.get_state_of_iteration(iteration)
|
||||||
|
|
||||||
changed = False
|
changed = False
|
||||||
for i in range(len(current_state)):
|
for i in range(len(current_state)):
|
||||||
@ -53,16 +70,9 @@ class CellState:
|
|||||||
except IndexError:
|
except IndexError:
|
||||||
raise IndexError("New State length or type is invalid!")
|
raise IndexError("New State length or type is invalid!")
|
||||||
|
|
||||||
self._dirty |= changed
|
self._dirty.value |= changed
|
||||||
return changed
|
return changed
|
||||||
|
|
||||||
def get_state_of_iteration(self, iteration):
|
|
||||||
""" Will return the state for the iteration modulo number of saved states.
|
|
||||||
:param iteration: Uses the iteration index, to differ between concurrent states.
|
|
||||||
:return The state for this iteration.
|
|
||||||
"""
|
|
||||||
return self._state_slots[iteration % self._state_save_slot_count]
|
|
||||||
|
|
||||||
def get_state_draw_color(self, iteration):
|
def get_state_draw_color(self, iteration):
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|
||||||
|
@ -74,13 +74,13 @@ class PyGameFor2D:
|
|||||||
|
|
||||||
def _cell_redraw_rectangles(cells, evolution_index, display_info):
|
def _cell_redraw_rectangles(cells, evolution_index, display_info):
|
||||||
for cell in cells:
|
for cell in cells:
|
||||||
if cell.state.is_set_for_redraw():
|
if cell.get_state().is_set_for_redraw():
|
||||||
cell_color = cell.state.get_state_draw_color(evolution_index)
|
cell_color = cell.get_state().get_state_draw_color(evolution_index)
|
||||||
cell_pos = _calculate_cell_position(display_info.cell_size, cell)
|
cell_pos = _calculate_cell_position(display_info.cell_size, cell)
|
||||||
surface_pos = list(map(operator.add, cell_pos, display_info.grid_pos))
|
surface_pos = list(map(operator.add, cell_pos, display_info.grid_pos))
|
||||||
yield display_info.screen.fill(cell_color, (surface_pos, display_info.cell_size))
|
yield display_info.screen.fill(cell_color, (surface_pos, display_info.cell_size))
|
||||||
cell.state.was_redrawn()
|
cell.get_state().was_redrawn()
|
||||||
|
|
||||||
|
|
||||||
def _calculate_cell_position(cell_size, cell):
|
def _calculate_cell_position(cell_size, cell):
|
||||||
return list(map(operator.mul, cell_size, cell.coordinate))
|
return list(map(operator.mul, cell_size, cell.get_coordinate()))
|
||||||
|
@ -17,36 +17,6 @@ class Grid:
|
|||||||
self._active_cells = self._cells.copy()
|
self._active_cells = self._cells.copy()
|
||||||
self._set_all_cells_active()
|
self._set_all_cells_active()
|
||||||
|
|
||||||
def get_active_cell_names(self):
|
|
||||||
return list(self._active_cells.keys())
|
|
||||||
|
|
||||||
def get_active_cells(self):
|
|
||||||
return self._active_cells
|
|
||||||
|
|
||||||
def get_cells(self):
|
|
||||||
return self._cells
|
|
||||||
|
|
||||||
def clear_active_cells(self):
|
|
||||||
self._active_cells = {}
|
|
||||||
|
|
||||||
def set_cells_active(self, cells: list):
|
|
||||||
""" Consider the cells in the next evolution cycle.
|
|
||||||
:param cells: A list of Cell objects, that shall be considered in the next evolution cycle.
|
|
||||||
"""
|
|
||||||
for cell in cells:
|
|
||||||
self._active_cells[cell.name] = cell
|
|
||||||
|
|
||||||
def get_cell_and_neighbors(self, cell_name):
|
|
||||||
cell = self._cells[cell_name]
|
|
||||||
return [cell, cell.neighbours]
|
|
||||||
|
|
||||||
def get_dimension(self):
|
|
||||||
return self._dimension
|
|
||||||
|
|
||||||
def _set_all_cells_active(self):
|
|
||||||
for cell_key, cell in self._cells.items():
|
|
||||||
self._active_cells[cell_key] = cell
|
|
||||||
|
|
||||||
def _create_cells(self, dimension_index=0, coordinate=None):
|
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.
|
""" 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 dimension_index: The index indicating which dimension is currently traversed.
|
||||||
@ -59,7 +29,7 @@ class Grid:
|
|||||||
self._recursive_step_down_dimensions(coordinate, dimension_index, self._create_cells)
|
self._recursive_step_down_dimensions(coordinate, dimension_index, self._create_cells)
|
||||||
except IndexError:
|
except IndexError:
|
||||||
coordinate_string = _join_coordinate(coordinate)
|
coordinate_string = _join_coordinate(coordinate)
|
||||||
self._cells[coordinate_string] = Cell(coordinate_string, self._state_class, coordinate)
|
self._cells[coordinate_string] = Cell(self._state_class, coordinate)
|
||||||
|
|
||||||
def _recursive_step_down_dimensions(self, coordinate, dimension_index, recursion_method):
|
def _recursive_step_down_dimensions(self, coordinate, dimension_index, recursion_method):
|
||||||
""" For the range of the current dimension, recalls the recursion method.
|
""" For the range of the current dimension, recalls the recursion method.
|
||||||
@ -75,10 +45,26 @@ class Grid:
|
|||||||
for cell in self._cells.values():
|
for cell in self._cells.values():
|
||||||
neighbours_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(),
|
neighbours_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(),
|
||||||
self._dimension)
|
self._dimension)
|
||||||
cell.set_neighbours(list(map(self._get_cell_by_coordinate, neighbours_coordinates)))
|
cell.set_neighbours(list(map(self._get_cell_state_by_coordinate, neighbours_coordinates)))
|
||||||
|
|
||||||
def _get_cell_by_coordinate(self, coordinate):
|
def _get_cell_state_by_coordinate(self, coordinate):
|
||||||
return self._cells[_join_coordinate(coordinate)]
|
return self._cells[_join_coordinate(coordinate)].get_state()
|
||||||
|
|
||||||
|
def _set_all_cells_active(self):
|
||||||
|
for cell_key, cell in self._cells.items():
|
||||||
|
self._active_cells[cell_key] = cell
|
||||||
|
|
||||||
|
def get_active_cell_names(self):
|
||||||
|
return list(self._active_cells.keys())
|
||||||
|
|
||||||
|
def get_active_cells(self):
|
||||||
|
return self._active_cells
|
||||||
|
|
||||||
|
def get_cells(self):
|
||||||
|
return self._cells
|
||||||
|
|
||||||
|
def get_dimension(self):
|
||||||
|
return self._dimension
|
||||||
|
|
||||||
|
|
||||||
def _instantiate_coordinate_if_necessary(coordinate):
|
def _instantiate_coordinate_if_necessary(coordinate):
|
||||||
|
@ -35,5 +35,5 @@ class CellularAutomatonProcessor:
|
|||||||
def _process_routine(cells, rule, active):
|
def _process_routine(cells, rule, active):
|
||||||
while active.value == 1:
|
while active.value == 1:
|
||||||
for cell in cells:
|
for cell in cells:
|
||||||
cell.evolve_if_ready(rule)
|
cell.evolve_if_ready(rule.evolve_cell)
|
||||||
|
|
||||||
|
13
test/test_cell.py
Normal file
13
test/test_cell.py
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import sys
|
||||||
|
sys.path.append('../src')
|
||||||
|
|
||||||
|
import cellular_automaton.ca_cell as cac
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
|
||||||
|
class TestCellState(unittest.TestCase):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
@ -7,20 +7,20 @@ import unittest
|
|||||||
|
|
||||||
class TestCellState(unittest.TestCase):
|
class TestCellState(unittest.TestCase):
|
||||||
|
|
||||||
def test_get_state_of_iteration(self):
|
def test_get_state_with_overflow(self):
|
||||||
cell_state = cs.CellState(0, state_save_slot_count=3)
|
cell_state = cs.CellState(initial_state=(0,))
|
||||||
cell_state.set_status_of_iteration(1, 0)
|
cell_state.set_state_of_iteration(new_state=(1,), iteration=0)
|
||||||
self.assertEqual(cell_state.get_status_of_iteration(3), 1)
|
self.assertEqual(tuple(cell_state.get_state_of_iteration(2)), (1,))
|
||||||
|
|
||||||
def test_set_state_applies_overflow(self):
|
def test_set_state_with_overflow(self):
|
||||||
cell_state = cs.CellState(0, state_save_slot_count=4)
|
cell_state = cs.CellState(initial_state=(0,))
|
||||||
cell_state.set_status_of_iteration(1, 4)
|
cell_state.set_state_of_iteration(new_state=(1,), iteration=2)
|
||||||
self.assertEqual(cell_state.get_status_of_iteration(0), 1)
|
self.assertEqual(tuple(cell_state.get_state_of_iteration(0)), (1,))
|
||||||
|
|
||||||
def test_set_state_only_applies_to_iteration_slot(self):
|
def test_set_state_does_not_effect_all_slots(self):
|
||||||
cell_state = cs.CellState(0, state_save_slot_count=2)
|
cell_state = cs.CellState(initial_state=(0,))
|
||||||
cell_state.set_status_of_iteration(1, 0)
|
cell_state.set_state_of_iteration(new_state=(1,), iteration=0)
|
||||||
self.assertEqual(cell_state.get_status_of_iteration(1), 0)
|
self.assertEqual(tuple(cell_state.get_state_of_iteration(1)), (0,))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
Loading…
Reference in New Issue
Block a user