second multiprocessing try, better but still bad
This commit is contained in:
parent
be57f89e78
commit
d1820ebc58
@ -10,6 +10,7 @@ class TestRule(Rule):
|
||||
try:
|
||||
return last_neighbour_states[0]
|
||||
except IndexError:
|
||||
print("damn neighbours")
|
||||
pass
|
||||
return False
|
||||
|
||||
@ -21,7 +22,7 @@ class MyState(CellState):
|
||||
if rand > 99:
|
||||
init = 1
|
||||
|
||||
super().__init__(init, draw_first_state=False)
|
||||
super().__init__((float(init),), draw_first_state=False)
|
||||
|
||||
def get_state_draw_color(self, iteration):
|
||||
red = 0
|
||||
@ -41,7 +42,7 @@ if __name__ == "__main__":
|
||||
freeze_support()
|
||||
random.seed(1000)
|
||||
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),
|
||||
state_class=MyState)
|
||||
ca = CellularAutomaton(grid, rule)
|
||||
|
@ -1,61 +1,44 @@
|
||||
import multiprocessing
|
||||
|
||||
from cellular_automaton.ca_cell_state import CellState
|
||||
|
||||
|
||||
class Cell:
|
||||
def __init__(self, name, state_class: CellState.__class__, coordinate: list):
|
||||
self._name = name
|
||||
def __init__(self, state_class: CellState.__class__, coordinate: list):
|
||||
self._coordinate = coordinate
|
||||
self._state = state_class()
|
||||
self._neighbours = []
|
||||
self._active = multiprocessing.Value('i', 1)
|
||||
self._age = multiprocessing.Value('i', 0)
|
||||
|
||||
def set_neighbours(self, neighbours):
|
||||
self._neighbours = neighbours
|
||||
|
||||
def get_state(self):
|
||||
return self._state
|
||||
|
||||
def get_coordinate(self):
|
||||
return self._coordinate
|
||||
|
||||
def evolve_if_ready(self, rule):
|
||||
if self._neighbours_are_younger():
|
||||
if self._is_active():
|
||||
new_state = rule(self.get_current_state(), self.get_neighbour_states())
|
||||
if self._state.is_active():
|
||||
new_state = rule(self._state.get_current_state(), self.get_neighbour_states())
|
||||
self.set_new_state_and_activate(new_state)
|
||||
|
||||
self.increase_age()
|
||||
self._state.increase_age()
|
||||
|
||||
def _neighbours_are_younger(self):
|
||||
for n in self._neighbours:
|
||||
if n.get_age() < self.get_age():
|
||||
if n.get_age() < self._state.get_age():
|
||||
return False
|
||||
|
||||
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)
|
||||
return True
|
||||
|
||||
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):
|
||||
changed = self._state.set_current_state(new_state, self._age.value + 1)
|
||||
changed = self._state.set_current_state(new_state)
|
||||
if changed:
|
||||
self._set_active()
|
||||
|
||||
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:
|
||||
n.set_active_for_next_iteration(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 += 1
|
||||
n.set_active_for_next_iteration(self._state.get_age() + 1)
|
||||
|
@ -2,32 +2,51 @@ from multiprocessing import Array, Value
|
||||
|
||||
|
||||
class CellState:
|
||||
_state_save_slot_count = 2
|
||||
"""
|
||||
This is the base class for all cell states.
|
||||
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):
|
||||
self._state_save_slot_count = state_save_slot_count
|
||||
self._state_slots = [Array('d', initial_state)] * state_save_slot_count
|
||||
def __init__(self, initial_state=(0., ), draw_first_state=True):
|
||||
self._state_slots = [Array('d', initial_state) for i in range(self.__class__._state_save_slot_count)]
|
||||
self._active = Value('i', 1)
|
||||
self._age = Value('i', 0)
|
||||
if draw_first_state:
|
||||
self._dirty = Value('i', 1)
|
||||
else:
|
||||
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):
|
||||
return self._dirty != 1
|
||||
|
||||
def get_state_changes(self):
|
||||
return self._dirty
|
||||
|
||||
def set_for_redraw(self):
|
||||
self._dirty = 1
|
||||
|
||||
def was_redrawn(self):
|
||||
self._dirty = 0
|
||||
|
||||
def set_current_state(self, new_state, current_iteration_index):
|
||||
return self.set_state_of_iteration(new_state, current_iteration_index)
|
||||
def set_current_state(self, new_state):
|
||||
return self.set_state_of_iteration(new_state, self.get_age() + 1)
|
||||
|
||||
def get_state_of_last_iteration(self, current_iteration_index):
|
||||
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.
|
||||
: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
|
||||
for i in range(len(current_state)):
|
||||
@ -53,16 +70,9 @@ class CellState:
|
||||
except IndexError:
|
||||
raise IndexError("New State length or type is invalid!")
|
||||
|
||||
self._dirty |= changed
|
||||
self._dirty.value |= 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):
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -74,13 +74,13 @@ class PyGameFor2D:
|
||||
|
||||
def _cell_redraw_rectangles(cells, evolution_index, display_info):
|
||||
for cell in cells:
|
||||
if cell.state.is_set_for_redraw():
|
||||
cell_color = cell.state.get_state_draw_color(evolution_index)
|
||||
if cell.get_state().is_set_for_redraw():
|
||||
cell_color = cell.get_state().get_state_draw_color(evolution_index)
|
||||
cell_pos = _calculate_cell_position(display_info.cell_size, cell)
|
||||
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))
|
||||
cell.state.was_redrawn()
|
||||
cell.get_state().was_redrawn()
|
||||
|
||||
|
||||
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._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):
|
||||
""" 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.
|
||||
@ -59,7 +29,7 @@ class Grid:
|
||||
self._recursive_step_down_dimensions(coordinate, dimension_index, self._create_cells)
|
||||
except IndexError:
|
||||
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):
|
||||
""" For the range of the current dimension, recalls the recursion method.
|
||||
@ -75,10 +45,26 @@ class Grid:
|
||||
for cell in self._cells.values():
|
||||
neighbours_coordinates = neighborhood.calculate_cell_neighbor_coordinates(cell.get_coordinate(),
|
||||
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):
|
||||
return self._cells[_join_coordinate(coordinate)]
|
||||
def _get_cell_state_by_coordinate(self, 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):
|
||||
|
@ -35,5 +35,5 @@ class CellularAutomatonProcessor:
|
||||
def _process_routine(cells, rule, active):
|
||||
while active.value == 1:
|
||||
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):
|
||||
|
||||
def test_get_state_of_iteration(self):
|
||||
cell_state = cs.CellState(0, state_save_slot_count=3)
|
||||
cell_state.set_status_of_iteration(1, 0)
|
||||
self.assertEqual(cell_state.get_status_of_iteration(3), 1)
|
||||
def test_get_state_with_overflow(self):
|
||||
cell_state = cs.CellState(initial_state=(0,))
|
||||
cell_state.set_state_of_iteration(new_state=(1,), iteration=0)
|
||||
self.assertEqual(tuple(cell_state.get_state_of_iteration(2)), (1,))
|
||||
|
||||
def test_set_state_applies_overflow(self):
|
||||
cell_state = cs.CellState(0, state_save_slot_count=4)
|
||||
cell_state.set_status_of_iteration(1, 4)
|
||||
self.assertEqual(cell_state.get_status_of_iteration(0), 1)
|
||||
def test_set_state_with_overflow(self):
|
||||
cell_state = cs.CellState(initial_state=(0,))
|
||||
cell_state.set_state_of_iteration(new_state=(1,), iteration=2)
|
||||
self.assertEqual(tuple(cell_state.get_state_of_iteration(0)), (1,))
|
||||
|
||||
def test_set_state_only_applies_to_iteration_slot(self):
|
||||
cell_state = cs.CellState(0, state_save_slot_count=2)
|
||||
cell_state.set_status_of_iteration(1, 0)
|
||||
self.assertEqual(cell_state.get_status_of_iteration(1), 0)
|
||||
def test_set_state_does_not_effect_all_slots(self):
|
||||
cell_state = cs.CellState(initial_state=(0,))
|
||||
cell_state.set_state_of_iteration(new_state=(1,), iteration=0)
|
||||
self.assertEqual(tuple(cell_state.get_state_of_iteration(1)), (0,))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
Loading…
Reference in New Issue
Block a user