2019-02-23 15:20:48 +00:00
|
|
|
"""
|
|
|
|
Copyright 2019 Richard Feistenauer
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
"""
|
|
|
|
|
2019-02-23 16:37:18 +00:00
|
|
|
from multiprocessing.sharedctypes import RawArray, RawValue
|
2019-02-03 16:17:57 +00:00
|
|
|
from ctypes import c_float, c_bool
|
2019-01-31 12:38:48 +00:00
|
|
|
|
|
|
|
|
2018-12-09 10:20:16 +00:00
|
|
|
class CellState:
|
|
|
|
"""
|
|
|
|
This is the base class for all cell states.
|
|
|
|
When using the cellular automaton display, inherit this class and implement get_state_draw_color.
|
|
|
|
"""
|
2019-02-10 10:54:42 +00:00
|
|
|
|
|
|
|
_state_save_slot_count = 2
|
|
|
|
|
2019-02-02 08:11:48 +00:00
|
|
|
def __init__(self, initial_state=(0., ), draw_first_state=True):
|
2019-02-10 10:54:42 +00:00
|
|
|
self._state_slots = [list(initial_state) for i in range(self.__class__._state_save_slot_count)]
|
|
|
|
self._active = [False for i in range(self.__class__._state_save_slot_count)]
|
|
|
|
self._active[0] = True
|
|
|
|
self._dirty = draw_first_state
|
2019-02-02 08:11:48 +00:00
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def is_active(self, current_evolution_step):
|
|
|
|
""" Returns the active status for the requested evolution_step
|
|
|
|
:param current_evolution_step: The evolution_step of interest.
|
2019-02-24 11:37:26 +00:00
|
|
|
:return: True if the cell or one of its neighbours changed in the last evolution step.
|
2019-02-16 17:05:26 +00:00
|
|
|
"""
|
|
|
|
return self._active[self._calculate_slot(current_evolution_step)]
|
2019-01-31 12:38:48 +00:00
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def set_active_for_next_evolution_step(self, current_evolution_step):
|
|
|
|
""" Sets the cell active for the next evolution_step, so it will be evolved.
|
|
|
|
:param current_evolution_step: The current evolution_step index.
|
|
|
|
"""
|
|
|
|
self._active[self._calculate_slot(current_evolution_step + 1)] = True
|
2019-02-02 08:11:48 +00:00
|
|
|
|
|
|
|
def is_set_for_redraw(self):
|
2019-02-16 17:05:26 +00:00
|
|
|
""" States if this state should be redrawn.
|
2019-02-24 11:37:26 +00:00
|
|
|
:return: True if state changed since last call of 'was_redrawn'.
|
2019-02-16 17:05:26 +00:00
|
|
|
"""
|
2019-02-10 10:54:42 +00:00
|
|
|
return self._dirty
|
2018-12-09 10:20:16 +00:00
|
|
|
|
2019-01-31 12:38:48 +00:00
|
|
|
def was_redrawn(self):
|
2019-02-16 17:05:26 +00:00
|
|
|
""" Remove the state from redraw cycle until next state change """
|
2019-02-10 10:54:42 +00:00
|
|
|
self._dirty = False
|
2019-01-31 12:38:48 +00:00
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def get_state_of_last_evolution_step(self, current_evolution_step):
|
|
|
|
return self.get_state_of_evolution_step(current_evolution_step - 1)
|
2019-01-31 12:38:48 +00:00
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def get_state_of_evolution_step(self, evolution_step):
|
|
|
|
""" Returns the state of the evolution_step.
|
|
|
|
:param evolution_step: Uses the evolution_step index, to differ between concurrent states.
|
2019-02-24 11:37:26 +00:00
|
|
|
:return The state of the requested evolution_step.
|
2019-02-09 18:06:18 +00:00
|
|
|
"""
|
2019-02-16 17:05:26 +00:00
|
|
|
return self._state_slots[self._calculate_slot(evolution_step)]
|
2019-02-09 18:06:18 +00:00
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def set_state_of_evolution_step(self, new_state, evolution_step):
|
|
|
|
""" Sets the new state for the evolution_step.
|
2019-01-31 12:38:48 +00:00
|
|
|
:param new_state: The new state to set.
|
2019-02-16 17:05:26 +00:00
|
|
|
:param evolution_step: The evolution_step index, to differ between concurrent states.
|
|
|
|
:return True if the state really changed.
|
2019-02-24 11:37:26 +00:00
|
|
|
:raises IndexError: If the state length changed.
|
2018-12-09 10:20:16 +00:00
|
|
|
"""
|
2019-02-16 17:05:26 +00:00
|
|
|
changed = self._set_new_state_if_valid(new_state, evolution_step)
|
2019-02-10 10:54:42 +00:00
|
|
|
self._dirty |= changed
|
2019-02-16 17:05:26 +00:00
|
|
|
self._active[self._calculate_slot(evolution_step)] = False
|
2019-02-10 10:54:42 +00:00
|
|
|
return changed
|
2018-12-09 10:20:16 +00:00
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def _set_new_state_if_valid(self, new_state, evolution_step):
|
|
|
|
current_state = self.get_state_of_evolution_step(evolution_step)
|
2019-02-15 18:33:15 +00:00
|
|
|
if len(new_state) != len(current_state):
|
|
|
|
raise IndexError("State length may not change!")
|
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
self.__change_current_state_values(current_state, new_state)
|
|
|
|
return self.__did_state_change(evolution_step)
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def __change_current_state_values(current_state, new_state):
|
2019-02-10 10:54:42 +00:00
|
|
|
for i, ns in enumerate(new_state):
|
2019-02-16 17:05:26 +00:00
|
|
|
if current_state[i] != ns:
|
|
|
|
current_state[i] = ns
|
|
|
|
|
|
|
|
def __did_state_change(self, evolution_step):
|
|
|
|
for a, b in zip(self.get_state_of_evolution_step(evolution_step),
|
|
|
|
self.get_state_of_last_evolution_step(evolution_step)):
|
|
|
|
if a != b:
|
|
|
|
return True
|
|
|
|
return False
|
2018-12-09 10:20:16 +00:00
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def get_state_draw_color(self, evolution_step):
|
|
|
|
""" When implemented should return the color representing the requested state.
|
|
|
|
:param evolution_step: Requested evolution_step.
|
|
|
|
:return: Color of the state as rgb tuple
|
|
|
|
"""
|
2018-12-09 10:20:16 +00:00
|
|
|
raise NotImplementedError
|
2019-02-09 18:06:18 +00:00
|
|
|
|
|
|
|
@classmethod
|
2019-02-16 17:05:26 +00:00
|
|
|
def _calculate_slot(cls, evolution_step):
|
|
|
|
return evolution_step % cls._state_save_slot_count
|
2019-02-10 10:54:42 +00:00
|
|
|
|
|
|
|
|
|
|
|
class SynchronousCellState(CellState):
|
2019-02-24 11:37:26 +00:00
|
|
|
""" CellState version using shared values for multi processing purpose. """
|
|
|
|
|
2019-02-10 10:54:42 +00:00
|
|
|
def __init__(self, initial_state=(0., ), draw_first_state=True):
|
|
|
|
super().__init__(initial_state, draw_first_state)
|
|
|
|
self._state_slots = [RawArray(c_float, initial_state) for i in range(self.__class__._state_save_slot_count)]
|
|
|
|
self._active = [RawValue(c_bool, False) for i in range(self.__class__._state_save_slot_count)]
|
|
|
|
self._active[0].value = True
|
|
|
|
self._dirty = RawValue(c_bool, draw_first_state)
|
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def set_active_for_next_evolution_step(self, current_evolution_step):
|
|
|
|
self._active[self._calculate_slot(current_evolution_step + 1)].value = True
|
2019-02-10 10:54:42 +00:00
|
|
|
|
|
|
|
def is_set_for_redraw(self):
|
|
|
|
return self._dirty.value
|
|
|
|
|
|
|
|
def was_redrawn(self):
|
|
|
|
self._dirty.value = False
|
|
|
|
|
2019-02-16 17:05:26 +00:00
|
|
|
def set_state_of_evolution_step(self, new_state, evolution_step):
|
|
|
|
changed = self._set_new_state_if_valid(new_state, evolution_step)
|
2019-02-10 10:54:42 +00:00
|
|
|
self._dirty.value |= changed
|
2019-02-16 17:05:26 +00:00
|
|
|
self._active[self._calculate_slot(evolution_step)].value = False
|
2019-02-10 10:54:42 +00:00
|
|
|
return changed
|