simulation.py 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. from synergine2.simulation import Subject, SimulationMechanism, Simulation
  2. from synergine2.simulation import SimulationBehaviour
  3. from synergine2.simulation import Event
  4. from synergine2.simulation import SubjectBehaviour
  5. from synergine2.utils import ChunkManager
  6. from synergine2.xyz import ProximitySubjectMechanism, ProximityMixin
  7. from synergine2.xyz import XYZSubjectMixin
  8. from synergine2.xyz_utils import get_around_positions_of_positions, get_min_and_max
  9. COLLECTION_CELL = 'COLLECTION_CELL' # Collections of Cell type
  10. class CellDieEvent(Event):
  11. def __init__(self, subject_id, *args, **kwargs):
  12. super().__init__(*args, **kwargs)
  13. self.subject_id = subject_id
  14. class CellBornEvent(Event):
  15. def __init__(self, subject_id, *args, **kwargs):
  16. super().__init__(*args, **kwargs)
  17. self.subject_id = subject_id
  18. class EmptyPositionWithLotOfCellAroundEvent(Event):
  19. def __init__(self, position, *args, **kwargs):
  20. super().__init__(*args, **kwargs)
  21. self.position = position
  22. class CellProximityMechanism(ProximitySubjectMechanism):
  23. distance = 1.41 # distance when on angle
  24. feel_collections = [COLLECTION_CELL]
  25. class CellAroundAnEmptyPositionMechanism(ProximityMixin, SimulationMechanism):
  26. distance = 1.41 # distance when on angle
  27. feel_collections = [COLLECTION_CELL]
  28. parallelizable = True
  29. def run(self, process_number: int=None, process_count: int=None):
  30. chunk_manager = ChunkManager(process_count)
  31. positions = self.simulation.subjects.xyz.keys()
  32. min_x, max_x, min_y, max_y, min_z, max_z = get_min_and_max(positions)
  33. xs = list(range(min_x, max_x+1))
  34. xs_chunks = chunk_manager.make_chunks(xs)
  35. results = {}
  36. for z in range(min_z, max_z+1):
  37. for y in range(min_y, max_y+1):
  38. for x in xs_chunks[process_number]:
  39. subject_here = self.simulation.subjects.xyz.get((x, y, z))
  40. if not subject_here or isinstance(subject_here, Empty):
  41. subjects = self.get_for_position(
  42. position=(x, y, z),
  43. simulation=self.simulation,
  44. )
  45. results[(x, y, z)] = subjects
  46. return results
  47. class CellDieBehaviour(SubjectBehaviour):
  48. use = [CellProximityMechanism]
  49. def run(self, data):
  50. around_count = len(data[CellProximityMechanism])
  51. if around_count in [2, 3]:
  52. return False
  53. # If we return around_count, when around_count is 0,
  54. # cycle manager will consider as False
  55. return True
  56. def action(self, data):
  57. new_empty = Empty(
  58. simulation=self.simulation,
  59. position=self.subject.position,
  60. )
  61. self.simulation.subjects.remove(self.subject)
  62. self.simulation.subjects.append(new_empty)
  63. return [CellDieEvent(self.subject.id)]
  64. class CellBornBehaviour(SubjectBehaviour):
  65. use = [CellProximityMechanism]
  66. def run(self, data):
  67. around_count = len(data[CellProximityMechanism])
  68. if around_count == 3:
  69. return 3
  70. return False
  71. def action(self, data):
  72. new_cell = Cell(
  73. simulation=self.simulation,
  74. position=self.subject.position,
  75. )
  76. positions_to_complete = get_around_positions_of_positions(self.subject.position)
  77. for position in positions_to_complete:
  78. if position not in self.simulation.subjects.xyz:
  79. new_empty = Empty(
  80. simulation=self.simulation,
  81. position=position,
  82. )
  83. # Ici on casse le SimplePrintTerminal (car on créer des ligne avec des espaces manquants ?)
  84. self.simulation.subjects.append(new_empty)
  85. self.simulation.subjects.remove(self.subject)
  86. self.simulation.subjects.append(new_cell)
  87. return [CellBornEvent(new_cell.id)]
  88. class InvertCellStateBehaviour(SimulationBehaviour):
  89. def run(self, data):
  90. pass # This behaviour is designed to be launch by terminal
  91. def action(self, data) -> [Event]:
  92. position = data['position']
  93. cell_at_position = self.simulation.subjects.xyz.get(position, None)
  94. if not cell_at_position or isinstance(cell_at_position, Empty):
  95. new_cell = Cell(
  96. simulation=self.simulation,
  97. position=position,
  98. )
  99. if cell_at_position:
  100. self.simulation.subjects.remove(cell_at_position)
  101. self.simulation.subjects.append(new_cell)
  102. return [CellBornEvent(new_cell.id)]
  103. new_empty = Empty(
  104. simulation=self.simulation,
  105. position=position,
  106. )
  107. self.simulation.subjects.remove(cell_at_position)
  108. self.simulation.subjects.append(new_empty)
  109. return [CellDieEvent(new_empty)]
  110. class LotOfCellsSignalBehaviour(SimulationBehaviour):
  111. use = [CellAroundAnEmptyPositionMechanism]
  112. def run(self, data):
  113. positions = []
  114. for position, subjects in data[CellAroundAnEmptyPositionMechanism].items():
  115. if len(subjects) >= 4:
  116. positions.append(position)
  117. return positions
  118. @classmethod
  119. def merge_data(cls, new_data, start_data=None):
  120. start_data = start_data or []
  121. start_data.extend(new_data)
  122. return start_data
  123. def action(self, data) -> [Event]:
  124. events = []
  125. for position in data:
  126. events.append(EmptyPositionWithLotOfCellAroundEvent(position))
  127. return events
  128. class Cell(XYZSubjectMixin, Subject):
  129. collections = Subject.collections[:]
  130. collections.extend([COLLECTION_CELL])
  131. behaviours_classes = [CellDieBehaviour]
  132. class Empty(XYZSubjectMixin, Subject):
  133. """Represent empty position where cell can spawn"""
  134. behaviours_classes = [CellBornBehaviour]
  135. class LifeGame(Simulation):
  136. behaviours_classes = [LotOfCellsSignalBehaviour]