Browse Source

Introduce simulation events and implement them in lifegame

Bastien Sevajol 7 years ago
parent
commit
0918f9e5cf

+ 41 - 12
sandbox/life_game/gui.py View File

6
 from pyglet.window import key as wkey
6
 from pyglet.window import key as wkey
7
 from random import randint
7
 from random import randint
8
 
8
 
9
-from sandbox.life_game.simulation import CellDieEvent, Cell
9
+from sandbox.life_game.simulation import CellDieEvent, Cell, InvertCellStateBehaviour, \
10
+    EmptyPositionWithLotOfCellAroundEvent
10
 from sandbox.life_game.simulation import CellBornEvent
11
 from sandbox.life_game.simulation import CellBornEvent
11
 from synergine2.gui import Gui
12
 from synergine2.gui import Gui
12
 from synergine2.terminals import TerminalPackage
13
 from synergine2.terminals import TerminalPackage
14
 
15
 
15
 cell_scale = ScaleBy(1.1, duration=0.25)
16
 cell_scale = ScaleBy(1.1, duration=0.25)
16
 cell_rotate = RotateBy(360, duration=30)
17
 cell_rotate = RotateBy(360, duration=30)
18
+flash_flash = ScaleBy(8, duration=0.5)
19
+flash_rotate = RotateBy(360, duration=6)
17
 
20
 
18
 
21
 
19
 class GridManager(object):
22
 class GridManager(object):
65
     def __init__(self):
68
     def __init__(self):
66
         super().__init__()
69
         super().__init__()
67
         self.cells = {}
70
         self.cells = {}
71
+        self.flashs = []
68
         self.grid_manager = GridManager(self, 32, border=2)
72
         self.grid_manager = GridManager(self, 32, border=2)
69
 
73
 
70
     def born(self, grid_position):
74
     def born(self, grid_position):
87
         except KeyError:
91
         except KeyError:
88
             pass  # Cell can be removed by gui
92
             pass  # Cell can be removed by gui
89
 
93
 
94
+    def flash(self, position):
95
+        flash = Sprite('resources/flash.png')
96
+        flash.opacity = 40
97
+        flash.scale = 0.1
98
+        flash.rotation = randint(0, 360)
99
+        flash.do(flash_flash + Reverse(flash_flash))
100
+        flash.do(Repeat(flash_rotate + Reverse(flash_rotate)))
101
+        self.grid_manager.position_sprite(flash, position)
102
+        self.flashs.append(flash)
103
+        self.add(flash)
104
+
90
 
105
 
91
 class MainLayer(ScrollableLayer):
106
 class MainLayer(ScrollableLayer):
92
     is_event_handler = True
107
     is_event_handler = True
93
 
108
 
94
-    def __init__(self):
109
+    def __init__(self, terminal: Terminal):
95
         super().__init__()
110
         super().__init__()
96
 
111
 
112
+        self.terminal = terminal
97
         self.scroll_step = 100
113
         self.scroll_step = 100
98
         self.grid_manager = GridManager(self, 32, border=2)
114
         self.grid_manager = GridManager(self, 32, border=2)
99
 
115
 
140
         x, y = director.get_virtual_coordinates(x, y)
156
         x, y = director.get_virtual_coordinates(x, y)
141
         grid_position = self.grid_manager.get_grid_position(x, y)
157
         grid_position = self.grid_manager.get_grid_position(x, y)
142
 
158
 
143
-        # TODO: Have to inject in simulation ...
144
-        if self.cells.cells.get(grid_position):
145
-            self.cells.die(grid_position)
146
-        else:
147
-            self.cells.born(grid_position)
159
+        self.terminal.send(TerminalPackage(
160
+            simulation_actions=[(InvertCellStateBehaviour, {'position': grid_position})],
161
+        ))
148
 
162
 
149
     def on_mouse_motion(self, x, y, dx, dy):
163
     def on_mouse_motion(self, x, y, dx, dy):
150
         x, y = director.get_virtual_coordinates(x, y)
164
         x, y = director.get_virtual_coordinates(x, y)
158
     def __init__(
172
     def __init__(
159
             self,
173
             self,
160
             terminal: Terminal,
174
             terminal: Terminal,
161
-            read_queue_interval: float = 1 / 60.0,
175
+            read_queue_interval: float=1 / 60.0,
162
     ):
176
     ):
163
         super().__init__(terminal, read_queue_interval)
177
         super().__init__(terminal, read_queue_interval)
164
 
178
 
165
-        self.main_layer = MainLayer()
179
+        self.main_layer = MainLayer(terminal=self.terminal)
166
         self.main_scene = cocos.scene.Scene(self.main_layer)
180
         self.main_scene = cocos.scene.Scene(self.main_layer)
167
         self.positions = {}
181
         self.positions = {}
168
 
182
 
169
         self.terminal.register_event_handler(CellDieEvent, self.on_cell_die)
183
         self.terminal.register_event_handler(CellDieEvent, self.on_cell_die)
170
         self.terminal.register_event_handler(CellBornEvent, self.on_cell_born)
184
         self.terminal.register_event_handler(CellBornEvent, self.on_cell_born)
185
+        self.terminal.register_event_handler(
186
+            EmptyPositionWithLotOfCellAroundEvent,
187
+            self.on_empty_cell_with_lot_of_cell_around,
188
+        )
171
 
189
 
172
     def get_main_scene(self):
190
     def get_main_scene(self):
173
         return self.main_scene
191
         return self.main_scene
179
                     self.positions[subject.id] = subject.position
197
                     self.positions[subject.id] = subject.position
180
                     self.main_layer.cells.born(subject.position)
198
                     self.main_layer.cells.born(subject.position)
181
 
199
 
200
+        for flash in self.main_layer.cells.flashs[:]:
201
+            self.main_layer.cells.flashs.remove(flash)
202
+            self.main_layer.cells.remove(flash)
203
+
182
     def on_cell_die(self, event: CellDieEvent):
204
     def on_cell_die(self, event: CellDieEvent):
183
-        self.main_layer.cells.die(self.positions[event.subject_id])
205
+        try:
206
+            self.main_layer.cells.die(self.positions[event.subject_id])
207
+        except KeyError:
208
+            pass
184
 
209
 
185
     def on_cell_born(self, event: CellBornEvent):
210
     def on_cell_born(self, event: CellBornEvent):
186
-        # TODO: La position peut evoluer dans un autre programme
187
-        # resoudre cette problematique de données subjects qui évolue
211
+        if event.subject_id not in self.terminal.subjects:
212
+            return
213
+
188
         subject = self.terminal.subjects.get(event.subject_id)
214
         subject = self.terminal.subjects.get(event.subject_id)
189
         self.positions[event.subject_id] = subject.position
215
         self.positions[event.subject_id] = subject.position
190
         self.main_layer.cells.born(subject.position)
216
         self.main_layer.cells.born(subject.position)
217
+
218
+    def on_empty_cell_with_lot_of_cell_around(self, event: EmptyPositionWithLotOfCellAroundEvent):
219
+        self.main_layer.cells.flash(event.position)

BIN
sandbox/life_game/resources/flash.png View File


+ 28 - 14
sandbox/life_game/run.py View File

1
 import collections
1
 import collections
2
 
2
 
3
-from sandbox.life_game.simulation import Cell
3
+from sandbox.life_game.simulation import Cell, LotOfCellsSignalBehaviour, LifeGame, \
4
+    EmptyPositionWithLotOfCellAroundEvent
4
 from sandbox.life_game.simulation import Empty
5
 from sandbox.life_game.simulation import Empty
5
 from sandbox.life_game.simulation import CellDieEvent
6
 from sandbox.life_game.simulation import CellDieEvent
6
 from sandbox.life_game.simulation import CellBornEvent
7
 from sandbox.life_game.simulation import CellBornEvent
51
             items_positions,
52
             items_positions,
52
             separator=' ',
53
             separator=' ',
53
             force_items_as=(('0', ' '),),
54
             force_items_as=(('0', ' '),),
55
+            # force_positions_as=(
56
+            #     ((-3, -10, 0), 'V'),
57
+            #     ((-2, -9, 0), 'X'),
58
+            # )
54
         ))
59
         ))
55
 
60
 
56
         # Display current cycle events
61
         # Display current cycle events
66
     subscribed_events = [
71
     subscribed_events = [
67
         CellDieEvent,
72
         CellDieEvent,
68
         CellBornEvent,
73
         CellBornEvent,
74
+        EmptyPositionWithLotOfCellAroundEvent,
69
     ]
75
     ]
70
 
76
 
71
     def __init__(self):
77
     def __init__(self):
86
 
92
 
87
 def main():
93
 def main():
88
     start_str_representation = """
94
     start_str_representation = """
89
-        0 0 0 0 0 0 0 0 0 0 0
90
-        0 0 0 1 1 1 1 0 0 0 0
91
-        0 0 0 1 0 0 1 0 0 0 0
92
-        0 1 1 1 0 0 1 1 1 0 0
93
-        0 1 0 0 0 0 0 0 1 0 0
94
-        0 1 0 0 0 0 0 0 1 0 0
95
-        0 1 1 1 0 0 1 1 1 0 0
96
-        0 0 0 1 0 0 1 0 0 0 0
97
-        0 0 0 1 1 1 1 0 0 0 0
98
-        0 0 0 0 0 0 0 0 0 0 0
99
-        0 0 0 0 0 0 0 0 0 0 0
95
+        0 0 0 0 0 0 0 0 0 0 0 0 0
96
+        0 0 0 1 1 1 1 0 0 0 0 0 0
97
+        0 0 0 1 0 0 1 0 0 0 0 0 0
98
+        0 1 1 1 0 0 1 1 1 0 0 0 0
99
+        0 1 0 0 0 0 0 0 1 0 0 0 0
100
+        0 1 0 0 0 0 0 0 1 0 0 0 0
101
+        0 1 1 1 0 0 1 1 1 0 0 0 0
102
+        0 0 0 1 0 0 1 0 0 0 0 0 0
103
+        0 0 0 1 1 1 1 0 0 0 0 0 0
104
+        0 0 0 0 0 0 0 0 0 0 0 0 0
105
+        0 0 0 0 0 0 0 0 0 0 0 0 0
106
+        0 0 0 0 0 0 0 0 0 0 0 0 0
107
+        0 0 0 0 0 0 0 0 0 0 0 0 0
108
+        0 0 0 0 0 0 0 0 0 0 0 0 0
109
+        0 0 0 0 0 1 1 1 0 0 0 0 0
110
+        0 0 0 0 0 0 0 1 0 0 0 0 0
111
+        0 0 0 0 0 0 1 0 0 0 0 0 0
112
+        0 0 0 0 0 0 0 0 0 0 0 0 0
113
+        0 0 0 0 0 0 0 0 0 0 0 0 0
100
     """
114
     """
101
-    simulation = Simulation()
115
+    simulation = LifeGame()
102
     subjects = get_subjects_from_str_representation(
116
     subjects = get_subjects_from_str_representation(
103
         start_str_representation,
117
         start_str_representation,
104
         simulation,
118
         simulation,
107
 
121
 
108
     core = Core(
122
     core = Core(
109
         simulation=simulation,
123
         simulation=simulation,
110
-        cycle_manager=CycleManager(subjects=subjects),
124
+        cycle_manager=CycleManager(simulation=simulation),
111
         terminal_manager=TerminalManager([CocosTerminal(), SimplePrintTerminal()]),
125
         terminal_manager=TerminalManager([CocosTerminal(), SimplePrintTerminal()]),
112
     )
126
     )
113
     core.run()
127
     core.run()

+ 112 - 6
sandbox/life_game/simulation.py View File

1
-from synergine2.simulation import Subject
1
+from synergine2.simulation import Subject, SimulationMechanism, Simulation
2
+from synergine2.simulation import SimulationBehaviour
2
 from synergine2.simulation import Event
3
 from synergine2.simulation import Event
3
-from synergine2.simulation import Behaviour
4
-from synergine2.xyz import ProximityMechanism
4
+from synergine2.simulation import SubjectBehaviour
5
+from synergine2.utils import ChunkManager
6
+from synergine2.xyz import ProximitySubjectMechanism, ProximityMixin
5
 from synergine2.xyz import XYZSubjectMixin
7
 from synergine2.xyz import XYZSubjectMixin
8
+from synergine2.xyz_utils import get_around_positions_of_positions, get_min_and_max
6
 
9
 
7
 COLLECTION_CELL = 'COLLECTION_CELL'  # Collections of Cell type
10
 COLLECTION_CELL = 'COLLECTION_CELL'  # Collections of Cell type
8
 
11
 
19
         self.subject_id = subject_id
22
         self.subject_id = subject_id
20
 
23
 
21
 
24
 
22
-class CellProximityMechanism(ProximityMechanism):
25
+class EmptyPositionWithLotOfCellAroundEvent(Event):
26
+    def __init__(self, position, *args, **kwargs):
27
+        super().__init__(*args, **kwargs)
28
+        self.position = position
29
+
30
+
31
+class CellProximityMechanism(ProximitySubjectMechanism):
23
     distance = 1.41  # distance when on angle
32
     distance = 1.41  # distance when on angle
24
     feel_collections = [COLLECTION_CELL]
33
     feel_collections = [COLLECTION_CELL]
25
 
34
 
26
 
35
 
27
-class CellDieBehaviour(Behaviour):
36
+class CellAroundAnEmptyPositionMechanism(ProximityMixin, SimulationMechanism):
37
+    distance = 1.41  # distance when on angle
38
+    feel_collections = [COLLECTION_CELL]
39
+    parallelizable = True
40
+
41
+    def run(self, process_number: int=None, process_count: int=None):
42
+        chunk_manager = ChunkManager(process_count)
43
+        positions = self.simulation.subjects.xyz.keys()
44
+        min_x, max_x, min_y, max_y, min_z, max_z = get_min_and_max(positions)
45
+        xs = list(range(min_x, max_x+1))
46
+        xs_chunks = chunk_manager.make_chunks(xs)
47
+
48
+        results = {}
49
+        for z in range(min_z, max_z+1):
50
+            for y in range(min_y, max_y+1):
51
+                for x in xs_chunks[process_number]:
52
+                    subject_here = self.simulation.subjects.xyz.get((x, y, z))
53
+                    if not subject_here or isinstance(subject_here, Empty):
54
+                        subjects = self.get_for_position(
55
+                            position=(x, y, z),
56
+                            simulation=self.simulation,
57
+                        )
58
+                        results[(x, y, z)] = subjects
59
+
60
+        return results
61
+
62
+
63
+class CellDieBehaviour(SubjectBehaviour):
28
     use = [CellProximityMechanism]
64
     use = [CellProximityMechanism]
29
 
65
 
30
     def run(self, data):
66
     def run(self, data):
45
         return [CellDieEvent(self.subject.id)]
81
         return [CellDieEvent(self.subject.id)]
46
 
82
 
47
 
83
 
48
-class CellBornBehaviour(Behaviour):
84
+class CellBornBehaviour(SubjectBehaviour):
49
     use = [CellProximityMechanism]
85
     use = [CellProximityMechanism]
50
 
86
 
51
     def run(self, data):
87
     def run(self, data):
59
             simulation=self.simulation,
95
             simulation=self.simulation,
60
             position=self.subject.position,
96
             position=self.subject.position,
61
         )
97
         )
98
+
99
+        positions_to_complete = get_around_positions_of_positions(self.subject.position)
100
+        for position in positions_to_complete:
101
+            if position not in self.simulation.subjects.xyz:
102
+                new_empty = Empty(
103
+                    simulation=self.simulation,
104
+                    position=position,
105
+                )
106
+                # Ici on casse le SimplePrintTerminal (car on créer des ligne avec des espaces manquants ?)
107
+                self.simulation.subjects.append(new_empty)
108
+
62
         self.simulation.subjects.remove(self.subject)
109
         self.simulation.subjects.remove(self.subject)
63
         self.simulation.subjects.append(new_cell)
110
         self.simulation.subjects.append(new_cell)
64
         return [CellBornEvent(new_cell.id)]
111
         return [CellBornEvent(new_cell.id)]
65
 
112
 
66
 
113
 
114
+class InvertCellStateBehaviour(SimulationBehaviour):
115
+    def run(self, data):
116
+        pass  # This behaviour is designed to be launch by terminal
117
+
118
+    def action(self, data) -> [Event]:
119
+        position = data['position']
120
+        cell_at_position = self.simulation.subjects.xyz.get(position, None)
121
+
122
+        if not cell_at_position or isinstance(cell_at_position, Empty):
123
+            new_cell = Cell(
124
+                simulation=self.simulation,
125
+                position=position,
126
+            )
127
+            if cell_at_position:
128
+                self.simulation.subjects.remove(cell_at_position)
129
+            self.simulation.subjects.append(new_cell)
130
+            return [CellBornEvent(new_cell.id)]
131
+
132
+        new_empty = Empty(
133
+            simulation=self.simulation,
134
+            position=position,
135
+        )
136
+
137
+        self.simulation.subjects.remove(cell_at_position)
138
+        self.simulation.subjects.append(new_empty)
139
+        return [CellDieEvent(new_empty)]
140
+
141
+
142
+class LotOfCellsSignalBehaviour(SimulationBehaviour):
143
+    use = [CellAroundAnEmptyPositionMechanism]
144
+
145
+    def run(self, data):
146
+        positions = []
147
+
148
+        for position, subjects in data[CellAroundAnEmptyPositionMechanism].items():
149
+            if len(subjects) >= 4:
150
+                positions.append(position)
151
+
152
+        return positions
153
+
154
+    @classmethod
155
+    def merge_data(cls, new_data, start_data=None):
156
+        start_data = start_data or []
157
+        start_data.extend(new_data)
158
+        return start_data
159
+
160
+    def action(self, data) -> [Event]:
161
+        events = []
162
+
163
+        for position in data:
164
+            events.append(EmptyPositionWithLotOfCellAroundEvent(position))
165
+
166
+        return events
167
+
168
+
67
 class Cell(XYZSubjectMixin, Subject):
169
 class Cell(XYZSubjectMixin, Subject):
68
     collections = Subject.collections[:]
170
     collections = Subject.collections[:]
69
     collections.extend([COLLECTION_CELL])
171
     collections.extend([COLLECTION_CELL])
73
 class Empty(XYZSubjectMixin, Subject):
175
 class Empty(XYZSubjectMixin, Subject):
74
     """Represent empty position where cell can spawn"""
176
     """Represent empty position where cell can spawn"""
75
     behaviours_classes = [CellBornBehaviour]
177
     behaviours_classes = [CellBornBehaviour]
178
+
179
+
180
+class LifeGame(Simulation):
181
+    behaviours_classes = [LotOfCellsSignalBehaviour]

+ 3 - 2
sandbox/life_game/utils.py View File

1
 from sandbox.life_game.simulation import Empty
1
 from sandbox.life_game.simulation import Empty
2
 from sandbox.life_game.simulation import Cell
2
 from sandbox.life_game.simulation import Cell
3
-from synergine2.simulation import Subjects, Simulation
3
+from synergine2.simulation import Simulation
4
+from synergine2.xyz import XYZSubjects
4
 from synergine2.xyz_utils import get_positions_from_str_representation
5
 from synergine2.xyz_utils import get_positions_from_str_representation
5
 
6
 
6
 
7
 
8
     str_representations: str,
9
     str_representations: str,
9
     simulation: Simulation,
10
     simulation: Simulation,
10
 ) -> [Cell, Empty]:
11
 ) -> [Cell, Empty]:
11
-    subjects = Subjects(simulation=simulation)
12
+    subjects = XYZSubjects(simulation=simulation)
12
     items_positions = get_positions_from_str_representation(str_representations)
13
     items_positions = get_positions_from_str_representation(str_representations)
13
     for item, positions in items_positions.items():
14
     for item, positions in items_positions.items():
14
         for position in positions:
15
         for position in positions:

+ 13 - 3
synergine2/core.py View File

20
         try:
20
         try:
21
             self.terminal_manager.start()
21
             self.terminal_manager.start()
22
 
22
 
23
-            start_package = TerminalPackage(subjects=self.simulation.subjects)
23
+            start_package = TerminalPackage(
24
+                subjects=self.simulation.subjects,
25
+            )
24
             self.terminal_manager.send(start_package)
26
             self.terminal_manager.send(start_package)
25
 
27
 
26
             while True:
28
             while True:
27
-                # TODO: receive from terminals
28
-                events = self.cycle_manager.next()
29
+                events = []
30
+                packages = self.terminal_manager.receive()
31
+                for package in packages:
32
+                    events.extend(self.cycle_manager.apply_actions(
33
+                        simulation_actions=package.simulation_actions,
34
+                        subject_actions=package.subject_actions,
35
+                    ))
36
+
37
+                events.extend(self.cycle_manager.next())
29
                 cycle_package = TerminalPackage(
38
                 cycle_package = TerminalPackage(
30
                     events=events,
39
                     events=events,
31
                     add_subjects=self.simulation.subjects.adds,
40
                     add_subjects=self.simulation.subjects.adds,
32
                     remove_subjects=self.simulation.subjects.removes,
41
                     remove_subjects=self.simulation.subjects.removes,
42
+                    is_cycle=True,
33
                 )
43
                 )
34
                 self.terminal_manager.send(cycle_package)
44
                 self.terminal_manager.send(cycle_package)
35
 
45
 

+ 110 - 23
synergine2/cycle.py View File

1
 import multiprocessing
1
 import multiprocessing
2
 
2
 
3
 from synergine2.processing import ProcessManager
3
 from synergine2.processing import ProcessManager
4
-from synergine2.simulation import Subject
5
-from synergine2.simulation import Behaviour
6
-from synergine2.simulation import Mechanism
4
+from synergine2.simulation import Subject, SimulationMechanism, SimulationBehaviour
5
+from synergine2.simulation import Simulation
6
+from synergine2.simulation import SubjectBehaviour
7
+from synergine2.simulation import SubjectMechanism
7
 from synergine2.simulation import Event
8
 from synergine2.simulation import Event
8
-from synergine2.simulation import Subjects
9
 from synergine2.utils import ChunkManager
9
 from synergine2.utils import ChunkManager
10
 
10
 
11
 
11
 
12
 class CycleManager(object):
12
 class CycleManager(object):
13
     def __init__(
13
     def __init__(
14
             self,
14
             self,
15
-            subjects: Subjects,
15
+            simulation: Simulation,
16
             process_manager: ProcessManager=None,
16
             process_manager: ProcessManager=None,
17
     ):
17
     ):
18
         if process_manager is None:
18
         if process_manager is None:
19
             process_manager = ProcessManager(
19
             process_manager = ProcessManager(
20
                 process_count=multiprocessing.cpu_count(),
20
                 process_count=multiprocessing.cpu_count(),
21
                 chunk_manager=ChunkManager(multiprocessing.cpu_count()),
21
                 chunk_manager=ChunkManager(multiprocessing.cpu_count()),
22
-                job_maker=self.computing,
23
             )
22
             )
24
 
23
 
25
-        self.subjects = subjects
24
+        self.simulation = simulation
26
         self.process_manager = process_manager
25
         self.process_manager = process_manager
27
         self.current_cycle = 0
26
         self.current_cycle = 0
28
         self.first_cycle = True
27
         self.first_cycle = True
30
     def next(self) -> [Event]:
29
     def next(self) -> [Event]:
31
         if self.first_cycle:
30
         if self.first_cycle:
32
             # To dispatch subjects add/removes, enable track on them
31
             # To dispatch subjects add/removes, enable track on them
33
-            self.subjects.track_changes = True
32
+            self.simulation.subjects.track_changes = True
34
             self.first_cycle = False
33
             self.first_cycle = False
35
 
34
 
35
+        events = []
36
+        # TODO: gestion des behaviours non parallelisables
37
+        # TODO: Proposer des ordres d'execution
38
+        events.extend(self._get_subjects_events())
39
+        events.extend(self._get_simulation_events())
40
+
41
+        return events
42
+
43
+    def _get_simulation_events(self) -> [Event]:
44
+        events = []
36
         results = {}
45
         results = {}
37
-        results_by_processes = self.process_manager.execute_jobs(self.subjects)
46
+        results_by_processes = self.process_manager.execute_jobs(
47
+            data=self.simulation,
48
+            job_maker=self.simulation_computing,
49
+        )
50
+
51
+        for process_result in results_by_processes:
52
+            for behaviour_class, behaviour_result in process_result.items():
53
+                results[behaviour_class] = behaviour_class.merge_data(
54
+                    behaviour_result,
55
+                    results.get(behaviour_class),
56
+                )
57
+
58
+        # Make events
59
+        for behaviour_class, behaviour_data in results.items():
60
+            events.extend(self.simulation.behaviours[behaviour_class].action(behaviour_data))
61
+
62
+        return events
63
+
64
+    def _get_subjects_events(self) -> [Event]:
38
         events = []
65
         events = []
66
+        results_by_processes = self.process_manager.chunk_and_execute_jobs(
67
+            data=self.simulation.subjects,
68
+            job_maker=self.subjects_computing,
69
+        )
70
+        results = {}
39
         for process_results in results_by_processes:
71
         for process_results in results_by_processes:
40
             results.update(process_results)
72
             results.update(process_results)
41
-        for subject in self.subjects[:]:  # Duplicate list to prevent conflicts with behaviours subjects manipulations
42
-            for behaviour_class in results[subject.id]:
73
+        for subject in self.simulation.subjects[:]:  # Duplicate list to prevent conflicts with behaviours subjects manipulations
74
+            for behaviour_class, behaviour_data in results[subject.id].items():
43
                 # TODO: Ajouter une etape de selection des actions a faire (genre neuronnal)
75
                 # TODO: Ajouter une etape de selection des actions a faire (genre neuronnal)
44
                 # (genre se cacher et fuir son pas compatibles)
76
                 # (genre se cacher et fuir son pas compatibles)
45
-                actions_events = subject.behaviours[behaviour_class].action(results[subject.id][behaviour_class])
77
+                actions_events = subject.behaviours[behaviour_class].action(behaviour_data)
46
                 events.extend(actions_events)
78
                 events.extend(actions_events)
47
 
79
 
48
         return events
80
         return events
49
 
81
 
50
-    def computing(self, subjects):
51
-        # compute mechanisms (prepare check to not compute slienced or not used mechanisms)
52
-        # compute behaviours with mechanisms data
53
-        # return list with per subject: [behaviour: return, behaviour: return] if behaviour return True something
82
+    def simulation_computing(
83
+            self,
84
+            simulation,
85
+            process_number,
86
+            process_count,
87
+    ):
88
+        # TODO: necessaire de passer simulation ?
89
+        mechanisms = self.get_mechanisms_to_compute(simulation)
90
+        mechanisms_data = {}
91
+        behaviours_data = {}
92
+
93
+        for mechanism in mechanisms:
94
+            mechanisms_data[type(mechanism)] = mechanism.run(
95
+                process_number=process_number,
96
+                process_count=process_count,
97
+            )
98
+
99
+        for behaviour in self.get_behaviours_to_compute(simulation):
100
+            behaviour_data = behaviour.run(mechanisms_data)  # TODO: Behaviours dependencies
101
+            if behaviour_data:
102
+                behaviours_data[type(behaviour)] = behaviour_data
103
+
104
+        return behaviours_data
105
+
106
+    def subjects_computing(
107
+            self,
108
+            subjects,
109
+            process_number=None,
110
+            process_count=None,
111
+    ):
54
         results = {}
112
         results = {}
55
         for subject in subjects:
113
         for subject in subjects:
56
             mechanisms = self.get_mechanisms_to_compute(subject)
114
             mechanisms = self.get_mechanisms_to_compute(subject)
62
 
120
 
63
             for behaviour in self.get_behaviours_to_compute(subject):
121
             for behaviour in self.get_behaviours_to_compute(subject):
64
                 # We identify behaviour data with it's class to be able to intersect it after subprocess data collect
122
                 # We identify behaviour data with it's class to be able to intersect it after subprocess data collect
65
-                behaviour_data = behaviour.run(mechanisms_data)
123
+                behaviour_data = behaviour.run(mechanisms_data)  # TODO: Behaviours dependencies
66
                 if behaviour_data:
124
                 if behaviour_data:
67
                     behaviours_data[type(behaviour)] = behaviour_data
125
                     behaviours_data[type(behaviour)] = behaviour_data
68
 
126
 
69
             results[subject.id] = behaviours_data
127
             results[subject.id] = behaviours_data
70
-            # TODO: Tester les performances si on utilise un index numerique pour les results[subject]
71
-            # et behaviours_data[type(behaviours_data)]
72
         return results
128
         return results
73
 
129
 
74
-    def get_mechanisms_to_compute(self, subject: Subject) -> [Mechanism]:
130
+    def get_mechanisms_to_compute(self, mechanisable) -> [SubjectMechanism, SimulationMechanism]:
75
         # TODO: Implementer un systeme qui inhibe des mechanisme (ex. someil inhibe l'ouie)
131
         # TODO: Implementer un systeme qui inhibe des mechanisme (ex. someil inhibe l'ouie)
76
-        return subject.mechanisms.values()
132
+        return mechanisable.mechanisms.values()
77
 
133
 
78
-    def get_behaviours_to_compute(self, subject: Subject) -> [Behaviour]:
134
+    def get_behaviours_to_compute(self, mechanisable) -> [SubjectBehaviour, SimulationBehaviour]:
79
         # TODO: Implementer un systeme qui inhibe des behaviours (ex. someil inhibe avoir faim)
135
         # TODO: Implementer un systeme qui inhibe des behaviours (ex. someil inhibe avoir faim)
80
-        return subject.behaviours.values()
136
+        return mechanisable.behaviours.values()
137
+
138
+    def apply_actions(
139
+            self,
140
+            simulation_actions: [tuple]=None,
141
+            subject_actions: [tuple]=None,
142
+    ) -> [Event]:
143
+        """
144
+        TODO: bien specifier la forme des parametres
145
+        simulation_actions = [(class, {'data': 'foo'})]
146
+        subject_actions = [(subject_id, [(class, {'data': 'foo'}])]
147
+        """
148
+        simulation_actions = simulation_actions or []
149
+        subject_actions = subject_actions or []
150
+        events = []
151
+
152
+        for subject_id, behaviours_and_data in subject_actions:
153
+            subject = self.simulation.subjects.index.get(subject_id)
154
+            for behaviour_class, behaviour_data in behaviours_and_data:
155
+                behaviour = behaviour_class(
156
+                    simulation=self.simulation,
157
+                    subject=subject,
158
+                )
159
+                events.extend(behaviour.action(behaviour_data))
160
+
161
+        for behaviour_class, behaviour_data in simulation_actions:
162
+            behaviour = behaviour_class(
163
+                simulation=self.simulation,
164
+            )
165
+            events.extend(behaviour.action(behaviour_data))
166
+
167
+        return events

+ 29 - 5
synergine2/processing.py View File

10
             self,
10
             self,
11
             process_count: int,
11
             process_count: int,
12
             chunk_manager: ChunkManager,
12
             chunk_manager: ChunkManager,
13
-            job_maker: types.FunctionType,
14
     ):
13
     ):
15
         self._process_count = process_count
14
         self._process_count = process_count
16
         self._chunk_manager = chunk_manager
15
         self._chunk_manager = chunk_manager
17
-        self._job_maker = job_maker
18
 
16
 
19
-    def execute_jobs(self, data: list) -> tuple:
17
+    def chunk_and_execute_jobs(self, data: list, job_maker: types.FunctionType) -> list:
20
         with Manager() as manager:
18
         with Manager() as manager:
21
             processes = list()
19
             processes = list()
22
             chunks = self._chunk_manager.make_chunks(data)
20
             chunks = self._chunk_manager.make_chunks(data)
29
                         process_number,
27
                         process_number,
30
                         chunks[process_number],
28
                         chunks[process_number],
31
                         results,
29
                         results,
30
+                        job_maker,
31
+                    )
32
+                ))
33
+
34
+            for process in processes:
35
+                process.start()
36
+
37
+            for process in processes:
38
+                process.join()
39
+
40
+            return results.values()
41
+
42
+    def execute_jobs(self, data: object, job_maker: types.FunctionType) -> list:
43
+        with Manager() as manager:
44
+            processes = list()
45
+            results = manager.dict()
46
+
47
+            for process_number in range(self._process_count):
48
+                processes.append(Process(
49
+                    target=self._job_maker_wrapper,
50
+                    args=(
51
+                        process_number,
52
+                        data,
53
+                        results,
54
+                        job_maker,
32
                     )
55
                     )
33
                 ))
56
                 ))
34
 
57
 
43
     def _job_maker_wrapper(
66
     def _job_maker_wrapper(
44
             self,
67
             self,
45
             process_number: int,
68
             process_number: int,
46
-            chunk: list,
69
+            data: list,
47
             results: dict,
70
             results: dict,
71
+            job_maker: types.FunctionType,
48
     ):
72
     ):
49
-        results[process_number] = self._job_maker(chunk)
73
+        results[process_number] = job_maker(data, process_number, self._process_count)

+ 103 - 25
synergine2/simulation.py View File

1
 import collections
1
 import collections
2
 
2
 
3
-from synergine2.utils import initialize_subject
4
-
5
-
6
-class Simulation(object):
7
-    def __init__(self):
8
-        self.collections = collections.defaultdict(list)
9
-        self._subjects = None
10
-
11
-    @property
12
-    def subjects(self):
13
-        return self._subjects
14
-
15
-    @subjects.setter
16
-    def subjects(self, value: 'Subjects'):
17
-        if not isinstance(value, Subjects):
18
-            raise Exception('Simulation.subjects must be Subjects type')
19
-        self._subjects = value
3
+from synergine2.utils import get_mechanisms_classes
20
 
4
 
21
 
5
 
22
 class Subject(object):
6
 class Subject(object):
23
     collections = []
7
     collections = []
24
     behaviours_classes = []
8
     behaviours_classes = []
25
 
9
 
26
-    def __init__(self, simulation: Simulation):
10
+    def __init__(self, simulation: 'Simulation'):
27
         self.id = id(self)  # We store object id because it's lost between process
11
         self.id = id(self)  # We store object id because it's lost between process
28
         self.simulation = simulation
12
         self.simulation = simulation
29
         self.behaviours = {}
13
         self.behaviours = {}
32
         for collection in self.collections:
16
         for collection in self.collections:
33
             self.simulation.collections[collection].append(self)
17
             self.simulation.collections[collection].append(self)
34
 
18
 
35
-        initialize_subject(
36
-            simulation=simulation,
37
-            subject=self,
38
-        )
19
+        self.initialize()
20
+
21
+    def initialize(self):
22
+        for mechanism_class in get_mechanisms_classes(self):
23
+            self.mechanisms[mechanism_class] = mechanism_class(
24
+                simulation=self.simulation,
25
+                subject=self,
26
+            )
27
+
28
+        for behaviour_class in self.behaviours_classes:
29
+            self.behaviours[behaviour_class] = behaviour_class(
30
+                simulation=self.simulation,
31
+                subject=self,
32
+            )
39
 
33
 
40
 
34
 
41
 class Subjects(list):
35
 class Subjects(list):
47
         self.removes = []
41
         self.removes = []
48
         self.adds = []
42
         self.adds = []
49
         self.track_changes = False
43
         self.track_changes = False
44
+        self.index = {}
50
         super().__init__(*args, **kwargs)
45
         super().__init__(*args, **kwargs)
51
 
46
 
52
     def remove(self, value: Subject):
47
     def remove(self, value: Subject):
48
+        # Remove from index
49
+        del self.index[value.id]
53
         # Remove from subjects list
50
         # Remove from subjects list
54
         super().remove(value)
51
         super().remove(value)
55
         # Remove from collections
52
         # Remove from collections
60
             self.removes.append(value)
57
             self.removes.append(value)
61
 
58
 
62
     def append(self, p_object):
59
     def append(self, p_object):
60
+        # Add to index
61
+        self.index[p_object.id] = p_object
63
         # Add to subjects list
62
         # Add to subjects list
64
         super().append(p_object)
63
         super().append(p_object)
65
         # Add to adds list
64
         # Add to adds list
67
             self.adds.append(p_object)
66
             self.adds.append(p_object)
68
 
67
 
69
 
68
 
70
-class Mechanism(object):
69
+class Simulation(object):
70
+    accepted_subject_class = Subjects
71
+    behaviours_classes = []
72
+
73
+    def __init__(self):
74
+        self.collections = collections.defaultdict(list)
75
+        self._subjects = None
76
+        self.behaviours = {}
77
+        self.mechanisms = {}
78
+
79
+        self.initialize()
80
+
81
+    @property
82
+    def subjects(self):
83
+        return self._subjects
84
+
85
+    @subjects.setter
86
+    def subjects(self, value: 'Subjects'):
87
+        if not isinstance(value, self.accepted_subject_class):
88
+            raise Exception('Simulation.subjects must be {0} type'.format(
89
+                self.accepted_subject_class,
90
+            ))
91
+        self._subjects = value
92
+
93
+    def initialize(self):
94
+        for mechanism_class in get_mechanisms_classes(self):
95
+            self.mechanisms[mechanism_class] = mechanism_class(
96
+                simulation=self,
97
+            )
98
+
99
+        for behaviour_class in self.behaviours_classes:
100
+            self.behaviours[behaviour_class] = behaviour_class(
101
+                simulation=self,
102
+            )
103
+
104
+
105
+class SubjectMechanism(object):
71
     def __init__(
106
     def __init__(
72
             self,
107
             self,
73
             simulation: Simulation,
108
             simulation: Simulation,
80
         raise NotImplementedError()
115
         raise NotImplementedError()
81
 
116
 
82
 
117
 
118
+class SimulationMechanism(object):
119
+    """If parallelizable behaviour, call """
120
+    parallelizable = False
121
+
122
+    def __init__(
123
+            self,
124
+            simulation: Simulation,
125
+    ):
126
+        self.simulation = simulation
127
+
128
+    def run(self, process_id: int=None, process_count: int=None):
129
+        raise NotImplementedError()
130
+
131
+
83
 class Event(object):
132
 class Event(object):
84
     def __init__(self, *args, **kwargs):
133
     def __init__(self, *args, **kwargs):
85
         pass
134
         pass
86
 
135
 
87
 
136
 
88
-class Behaviour(object):
137
+class SubjectBehaviour(object):
89
     def __init__(
138
     def __init__(
90
             self,
139
             self,
91
             simulation: Simulation,
140
             simulation: Simulation,
108
     def action(self, data) -> [Event]:
157
     def action(self, data) -> [Event]:
109
         """
158
         """
110
         Method called in main process
159
         Method called in main process
111
-        Return value will be give to terminals
160
+        Return events will be give to terminals
161
+        """
162
+        raise NotImplementedError()
163
+
164
+
165
+class SimulationBehaviour(object):
166
+    def __init__(
167
+            self,
168
+            simulation: Simulation,
169
+    ):
170
+        self.simulation = simulation
171
+
172
+    def run(self, data):
173
+        """
174
+        Method called in subprocess if mechanisms are
175
+        parallelizable, in main process if not.
176
+        """
177
+        raise NotImplementedError()
178
+
179
+    @classmethod
180
+    def merge_data(cls, new_data, start_data=None):
181
+        """
182
+        Called if behaviour executed in subprocess
183
+        """
184
+        raise NotImplementedError()
185
+
186
+    def action(self, data) -> [Event]:
187
+        """
188
+        Method called in main process
189
+        Return events will be give to terminals
112
         """
190
         """
113
         raise NotImplementedError()
191
         raise NotImplementedError()

+ 6 - 2
synergine2/terminals.py View File

19
             add_subjects: [Subject]=None,
19
             add_subjects: [Subject]=None,
20
             remove_subjects: [Subject]=None,
20
             remove_subjects: [Subject]=None,
21
             events: [Event]=None,
21
             events: [Event]=None,
22
-            is_cycle: bool=True,
22
+            simulation_actions: [tuple]=None,
23
+            subject_actions: [tuple]=None,
24
+            is_cycle: bool=False,
23
             *args,
25
             *args,
24
             **kwargs
26
             **kwargs
25
     ):
27
     ):
27
         self.add_subjects = add_subjects or []
29
         self.add_subjects = add_subjects or []
28
         self.remove_subjects = remove_subjects or []
30
         self.remove_subjects = remove_subjects or []
29
         self.events = events or []
31
         self.events = events or []
32
+        self.simulation_actions = simulation_actions or []
33
+        self.subject_actions = subject_actions or []
30
         self.is_cycle = is_cycle
34
         self.is_cycle = is_cycle
31
 
35
 
32
 
36
 
147
 
151
 
148
             output_queue.put(terminal_adapted_package)
152
             output_queue.put(terminal_adapted_package)
149
 
153
 
150
-    def receive(self) -> []:
154
+    def receive(self) -> [TerminalPackage]:
151
         packages = []
155
         packages = []
152
         for input_queue in self.inputs_queues.values():
156
         for input_queue in self.inputs_queues.values():
153
             try:
157
             try:

+ 2 - 19
synergine2/utils.py View File

12
         return x
12
         return x
13
 
13
 
14
 
14
 
15
-def get_mechanisms_classes(subject: 'Subject') -> ['Mechanisms']:
15
+def get_mechanisms_classes(mechanized) -> ['Mechanisms']:
16
     mechanisms_classes = []
16
     mechanisms_classes = []
17
-    for behaviour_class in subject.behaviours_classes:
17
+    for behaviour_class in mechanized.behaviours_classes:
18
         mechanisms_classes.extend(behaviour_class.use)
18
         mechanisms_classes.extend(behaviour_class.use)
19
     return list(set(mechanisms_classes))  # Remove duplicates
19
     return list(set(mechanisms_classes))  # Remove duplicates
20
-
21
-
22
-def initialize_subject(
23
-        simulation: 'Simulation',
24
-        subject: 'Subject',
25
-) -> None:
26
-    for mechanism_class in get_mechanisms_classes(subject):
27
-        subject.mechanisms[mechanism_class] = mechanism_class(
28
-            simulation=simulation,
29
-            subject=subject,
30
-        )
31
-
32
-    for behaviour_class in subject.behaviours_classes:
33
-        subject.behaviours[behaviour_class] = behaviour_class(
34
-            simulation=simulation,
35
-            subject=subject,
36
-        )

+ 48 - 14
synergine2/xyz.py View File

2
 from math import degrees
2
 from math import degrees
3
 from math import acos
3
 from math import acos
4
 
4
 
5
-from synergine2.simulation import Mechanism
5
+from synergine2.simulation import SubjectMechanism, Subjects, Subject
6
 from synergine2.simulation import Simulation as BaseSimulation
6
 from synergine2.simulation import Simulation as BaseSimulation
7
 
7
 
8
 
8
 
63
         super().__init__(*args, **kwargs)
63
         super().__init__(*args, **kwargs)
64
 
64
 
65
 
65
 
66
-class ProximityMechanism(Mechanism):
66
+class ProximityMixin(object):
67
     distance = 1
67
     distance = 1
68
     feel_collections = [COLLECTION_XYZ]
68
     feel_collections = [COLLECTION_XYZ]
69
     direction_round_decimals = 0
69
     direction_round_decimals = 0
70
     distance_round_decimals = 2
70
     distance_round_decimals = 2
71
 
71
 
72
-    def run(self):
72
+    def get_for_position(
73
+            self,
74
+            position,
75
+            simulation: 'Simulation',
76
+            exclude_subject: Subject=None,
77
+    ):
73
         subjects = []
78
         subjects = []
74
         for feel_collection in self.feel_collections:
79
         for feel_collection in self.feel_collections:
75
-            for subject in self.simulation.collections.get(feel_collection, []):
76
-                if subject == self.subject:
80
+            # TODO: Optimiser en calculant directement les positions alentours et
81
+            # en regardant si elles sont occupés dans subjects.xyz par un subject
82
+            # etant dans fell_collection
83
+            for subject in simulation.collections.get(feel_collection, []):
84
+                if subject == exclude_subject:
77
                     continue
85
                     continue
78
 
86
 
79
                 distance = round(
87
                 distance = round(
80
-                    self.get_distance_of(subject),
88
+                    self.get_distance_of(
89
+                        position=position,
90
+                        subject=subject,
91
+                    ),
81
                     self.distance_round_decimals,
92
                     self.distance_round_decimals,
82
                 )
93
                 )
83
-                if subject != self.subject and distance <= self.distance:
94
+                if distance <= self.distance:
84
                     direction = round(
95
                     direction = round(
85
                         get_degree_from_north(
96
                         get_degree_from_north(
86
-                            self.subject.position,
97
+                            position,
87
                             subject.position,
98
                             subject.position,
88
                         ),
99
                         ),
89
                         self.direction_round_decimals,
100
                         self.direction_round_decimals,
96
 
107
 
97
         return subjects
108
         return subjects
98
 
109
 
99
-    def get_distance_of(self, subject: XYZSubjectMixin):
110
+    @classmethod
111
+    def get_distance_of(cls, position, subject: XYZSubjectMixin):
100
         return get_distance_between_points(
112
         return get_distance_between_points(
101
-            self.subject.position,
113
+            position,
102
             subject.position,
114
             subject.position,
103
         )
115
         )
104
 
116
 
105
 
117
 
118
+class ProximitySubjectMechanism(ProximityMixin, SubjectMechanism):
119
+    def run(self):
120
+        return self.get_for_position(
121
+            position=self.subject.position,
122
+            simulation=self.simulation,
123
+            exclude_subject=self.subject,
124
+        )
125
+
126
+
127
+class XYZSubjects(Subjects):
128
+    def __init__(self, *args, **kwargs):
129
+        super().__init__(*args, **kwargs)
130
+        # TODO: accept multiple subjects as same position
131
+        # TODO: init xyz with given list
132
+        self.xyz = {}
133
+
134
+    def remove(self, value: XYZSubjectMixin):
135
+        super().remove(value)
136
+        del self.xyz[value.position]
137
+
138
+    def append(self, p_object: XYZSubjectMixin):
139
+        super().append(p_object)
140
+        self.xyz[p_object.position] = p_object
141
+
142
+
106
 class Simulation(BaseSimulation):
143
 class Simulation(BaseSimulation):
107
-    """
108
-    TODO: xyz properties
109
-    """
110
-    pass
144
+    accepted_subject_class = XYZSubjects

+ 77 - 0
synergine2/xyz_utils.py View File

42
     return items_positions
42
     return items_positions
43
 
43
 
44
 
44
 
45
+def get_min_and_max(positions) -> (int, int, int, int, int):
46
+    max_x_position = max(positions, key=lambda p: p[0])
47
+    min_x_position = min(positions, key=lambda p: p[0])
48
+    max_y_position = max(positions, key=lambda p: p[1])
49
+    min_y_position = min(positions, key=lambda p: p[1])
50
+    max_z_position = max(positions, key=lambda p: p[2])
51
+    min_z_position = min(positions, key=lambda p: p[2])
52
+
53
+    max_x = max_x_position[0]
54
+    min_x = min_x_position[0]
55
+    max_y = max_y_position[1]
56
+    min_y = min_y_position[1]
57
+    max_z = max_z_position[2]
58
+    min_z = min_z_position[2]
59
+
60
+    return min_x, max_x, min_y, max_y, min_z, max_z
61
+
62
+
45
 def get_str_representation_from_positions(
63
 def get_str_representation_from_positions(
46
     items_positions: dict,
64
     items_positions: dict,
47
     separator='',
65
     separator='',
49
     start_with='',
67
     start_with='',
50
     end_with='',
68
     end_with='',
51
     force_items_as=None,
69
     force_items_as=None,
70
+    force_positions_as=None,
71
+    complete_lines_with=' ',
52
 ) -> str:
72
 ) -> str:
53
     positions = []
73
     positions = []
54
     for item_positions in items_positions.values():
74
     for item_positions in items_positions.values():
55
         positions.extend(item_positions)
75
         positions.extend(item_positions)
56
     positions = sorted(positions, key=lambda p: (p[2], p[1], p[0]))
76
     positions = sorted(positions, key=lambda p: (p[2], p[1], p[0]))
57
 
77
 
78
+    if complete_lines_with is not None:
79
+        min_x, max_x, min_y, max_y, min_z, max_z = get_min_and_max(positions)
80
+
81
+        all_ = []
82
+
83
+        for x in range(min_x, max_x+1):
84
+            for y in range(min_y, max_y+1):
85
+                for z in range(min_z, max_z+1):
86
+                    all_.append((x, y, z))
87
+
88
+        pass
89
+
90
+        for one_of_all in all_:
91
+            if one_of_all not in positions:
92
+                if complete_lines_with not in items_positions:
93
+                    items_positions[complete_lines_with] = []
94
+                items_positions[complete_lines_with].append(one_of_all)
95
+
96
+        positions = []
97
+        for item_positions in items_positions.values():
98
+            positions.extend(item_positions)
99
+        positions = sorted(positions, key=lambda p: (p[2], p[1], p[0]))
100
+
58
     str_representation = start_with + tabulation
101
     str_representation = start_with + tabulation
59
 
102
 
60
     start_x = positions[0][0]
103
     start_x = positions[0][0]
84
                     str_item = force_item_as[1]
127
                     str_item = force_item_as[1]
85
                     break
128
                     break
86
 
129
 
130
+        if force_positions_as:
131
+            for force_position_as in force_positions_as:
132
+                if position == force_position_as[0]:
133
+                    str_item = force_position_as[1]
134
+                    break
135
+
87
         added_value = str_item
136
         added_value = str_item
88
         if position[0] != start_x:
137
         if position[0] != start_x:
89
             added_value = separator + added_value
138
             added_value = separator + added_value
94
 
143
 
95
     return str_representation + end_with
144
     return str_representation + end_with
96
 
145
 
146
+
147
+def get_around_positions_of_positions(position, exclude_start_position=True) -> list:
148
+    """
149
+    TODO: compute with z (allow or disable with parameter)
150
+    Return positions around a point with distance of 1.
151
+
152
+    :param position: (x, y, z) tuple
153
+    :param exclude_start_position: if True, given position will not be
154
+    added to result list
155
+    :return: list of (x, y, z) positions
156
+    :rtype: list
157
+    """
158
+    pz = position[2]
159
+    px = position[0]
160
+    py = position[1]
161
+    points = [
162
+        (px-1, py-1, pz),
163
+        (px,   py-1, pz),
164
+        (px+1, py+1, pz),
165
+        (px-1, py  , pz),
166
+        (px+1, py  , pz),
167
+        (px-1, py+1, pz),
168
+        (px,   py+1, pz),
169
+        (px+1, py-1, pz)
170
+    ]
171
+    if not exclude_start_position:
172
+        points.append(position)
173
+    return points

+ 46 - 64
tests/test_life_game.py View File

4
 from sandbox.life_game.utils import get_subjects_from_str_representation
4
 from sandbox.life_game.utils import get_subjects_from_str_representation
5
 from synergine2.cycle import CycleManager
5
 from synergine2.cycle import CycleManager
6
 from synergine2.simulation import Simulation
6
 from synergine2.simulation import Simulation
7
-from synergine2.simulation import Subjects
7
+from synergine2.xyz import XYZSubjects
8
 from synergine2.xyz_utils import get_str_representation_from_positions
8
 from synergine2.xyz_utils import get_str_representation_from_positions
9
 from tests import BaseTest
9
 from tests import BaseTest
10
 from tests import str_kwargs
10
 from tests import str_kwargs
22
 
22
 
23
         return get_str_representation_from_positions(
23
         return get_str_representation_from_positions(
24
             items_positions,
24
             items_positions,
25
+            complete_lines_with='0',
25
             **str_kwargs
26
             **str_kwargs
26
         )
27
         )
27
 
28
 
33
         simulation.subjects = subjects
34
         simulation.subjects = subjects
34
 
35
 
35
         cycle_manager = CycleManager(
36
         cycle_manager = CycleManager(
36
-            subjects=subjects,
37
+            simulation=simulation,
37
         )
38
         )
38
 
39
 
39
         assert """
40
         assert """
47
         cycle_manager.next()
48
         cycle_manager.next()
48
 
49
 
49
         assert """
50
         assert """
51
+            0 0 0 0 0
50
             0 0 1 0 0
52
             0 0 1 0 0
51
             0 0 1 0 0
53
             0 0 1 0 0
52
             0 0 1 0 0
54
             0 0 1 0 0
55
+            0 0 0 0 0
53
         """ == self._get_str_representation_of_subjects(
56
         """ == self._get_str_representation_of_subjects(
54
             subjects,
57
             subjects,
55
         )
58
         )
58
 
61
 
59
         assert """
62
         assert """
60
             0 0 0 0 0
63
             0 0 0 0 0
64
+            0 0 0 0 0
61
             0 1 1 1 0
65
             0 1 1 1 0
62
             0 0 0 0 0
66
             0 0 0 0 0
67
+            0 0 0 0 0
63
         """ == self._get_str_representation_of_subjects(
68
         """ == self._get_str_representation_of_subjects(
64
             subjects,
69
             subjects,
65
         )
70
         )
66
 
71
 
67
     def _get_subjects(self, simulation: Simulation):
72
     def _get_subjects(self, simulation: Simulation):
68
-        cells = Subjects(simulation=simulation)
73
+        cells = XYZSubjects(simulation=simulation)
69
 
74
 
70
         for position in [
75
         for position in [
71
             (-1, 0, 0),
76
             (-1, 0, 0),
115
             0 0 0 0 0 0 0 0 0 0 0
120
             0 0 0 0 0 0 0 0 0 0 0
116
         """,
121
         """,
117
             """
122
             """
118
-            0 0 0 0 1 1 0 0 0 0 0
119
-            0 0 0 1 1 1 1 0 0 0 0
120
-            0 0 0 0 0 0 0 0 0 0 0
121
-            0 1 0 1 0 0 1 0 1 0 0
122
-            1 1 0 0 0 0 0 0 1 1 0
123
-            1 1 0 0 0 0 0 0 1 1 0
124
-            0 1 0 1 0 0 1 0 1 0 0
125
-            0 0 0 0 0 0 0 0 0 0 0
126
-            0 0 0 1 1 1 1 0 0 0 0
127
-            0 0 0 0 1 1 0 0 0 0 0
128
-            0 0 0 0 0 0 0 0 0 0 0
123
+            0 0 0 0 0 0 0 0 0 0 0 0
124
+            0 0 0 0 0 1 1 0 0 0 0 0
125
+            0 0 0 0 1 1 1 1 0 0 0 0
126
+            0 0 0 0 0 0 0 0 0 0 0 0
127
+            0 0 1 0 1 0 0 1 0 1 0 0
128
+            0 1 1 0 0 0 0 0 0 1 1 0
129
+            0 1 1 0 0 0 0 0 0 1 1 0
130
+            0 0 1 0 1 0 0 1 0 1 0 0
131
+            0 0 0 0 0 0 0 0 0 0 0 0
132
+            0 0 0 0 1 1 1 1 0 0 0 0
133
+            0 0 0 0 0 1 1 0 0 0 0 0
134
+            0 0 0 0 0 0 0 0 0 0 0 0
129
         """,
135
         """,
130
             """
136
             """
131
-            0 0 0 1 0 0 1 0 0 0 0
132
-            0 0 0 1 0 0 1 0 0 0 0
133
-            0 0 1 1 0 0 1 1 0 0 0
134
-            1 1 1 0 0 0 0 1 1 1 0
135
-            0 0 0 0 0 0 0 0 0 0 0
136
-            0 0 0 0 0 0 0 0 0 0 0
137
-            1 1 1 0 0 0 0 1 1 1 0
138
-            0 0 1 1 0 0 1 1 0 0 0
139
-            0 0 0 1 0 0 1 0 0 0 0
140
-            0 0 0 1 0 0 1 0 0 0 0
141
-            0 0 0 0 0 0 0 0 0 0 0
142
-        """,
143
-            """
144
-            0 0 0 0 0 0 0 0 0 0 0
145
-            0 0 0 1 1 1 1 0 0 0 0
146
-            0 0 0 1 0 0 1 0 0 0 0
147
-            0 1 1 1 0 0 1 1 1 0 0
148
-            0 1 0 0 0 0 0 0 1 0 0
149
-            0 1 0 0 0 0 0 0 1 0 0
150
-            0 1 1 1 0 0 1 1 1 0 0
151
-            0 0 0 1 0 0 1 0 0 0 0
152
-            0 0 0 1 1 1 1 0 0 0 0
153
-            0 0 0 0 0 0 0 0 0 0 0
154
-            0 0 0 0 0 0 0 0 0 0 0
137
+            0 0 0 0 0 0 0 0 0 0 0 0
138
+            0 0 0 0 1 0 0 1 0 0 0 0
139
+            0 0 0 0 1 0 0 1 0 0 0 0
140
+            0 0 0 1 1 0 0 1 1 0 0 0
141
+            0 1 1 1 0 0 0 0 1 1 1 0
142
+            0 0 0 0 0 0 0 0 0 0 0 0
143
+            0 0 0 0 0 0 0 0 0 0 0 0
144
+            0 1 1 1 0 0 0 0 1 1 1 0
145
+            0 0 0 1 1 0 0 1 1 0 0 0
146
+            0 0 0 0 1 0 0 1 0 0 0 0
147
+            0 0 0 0 1 0 0 1 0 0 0 0
148
+            0 0 0 0 0 0 0 0 0 0 0 0
155
         """,
149
         """,
156
             """
150
             """
157
-            0 0 0 0 1 1 0 0 0 0 0
158
-            0 0 0 1 1 1 1 0 0 0 0
159
-            0 0 0 0 0 0 0 0 0 0 0
160
-            0 1 0 1 0 0 1 0 1 0 0
161
-            1 1 0 0 0 0 0 0 1 1 0
162
-            1 1 0 0 0 0 0 0 1 1 0
163
-            0 1 0 1 0 0 1 0 1 0 0
164
-            0 0 0 0 0 0 0 0 0 0 0
165
-            0 0 0 1 1 1 1 0 0 0 0
166
-            0 0 0 0 1 1 0 0 0 0 0
167
-            0 0 0 0 0 0 0 0 0 0 0
151
+            0 0 0 0 0 0 0 0 0 0 0 0
152
+            0 0 0 0 0 0 0 0 0 0 0 0
153
+            0 0 0 0 1 1 1 1 0 0 0 0
154
+            0 0 0 0 1 0 0 1 0 0 0 0
155
+            0 0 1 1 1 0 0 1 1 1 0 0
156
+            0 0 1 0 0 0 0 0 0 1 0 0
157
+            0 0 1 0 0 0 0 0 0 1 0 0
158
+            0 0 1 1 1 0 0 1 1 1 0 0
159
+            0 0 0 0 1 0 0 1 0 0 0 0
160
+            0 0 0 0 1 1 1 1 0 0 0 0
161
+            0 0 0 0 0 0 0 0 0 0 0 0
162
+            0 0 0 0 0 0 0 0 0 0 0 0
168
         """,
163
         """,
169
-            """
170
-            0 0 0 1 0 0 1 0 0 0 0
171
-            0 0 0 1 0 0 1 0 0 0 0
172
-            0 0 1 1 0 0 1 1 0 0 0
173
-            1 1 1 0 0 0 0 1 1 1 0
174
-            0 0 0 0 0 0 0 0 0 0 0
175
-            0 0 0 0 0 0 0 0 0 0 0
176
-            1 1 1 0 0 0 0 1 1 1 0
177
-            0 0 1 1 0 0 1 1 0 0 0
178
-            0 0 0 1 0 0 1 0 0 0 0
179
-            0 0 0 1 0 0 1 0 0 0 0
180
-            0 0 0 0 0 0 0 0 0 0 0
181
-        """
182
         ]
164
         ]
183
 
165
 
184
         simulation = Simulation()
166
         simulation = Simulation()
189
         simulation.subjects = subjects
171
         simulation.subjects = subjects
190
 
172
 
191
         cycle_manager = CycleManager(
173
         cycle_manager = CycleManager(
192
-            subjects=subjects,
174
+            simulation=simulation,
193
         )
175
         )
194
 
176
 
195
         for str_representation in str_representations:
177
         for str_representation in str_representations:
196
             assert str_representation == \
178
             assert str_representation == \
197
                self._get_str_representation_of_subjects(
179
                self._get_str_representation_of_subjects(
198
                     subjects,
180
                     subjects,
199
-                )
181
+               )
200
             cycle_manager.next()
182
             cycle_manager.next()

+ 18 - 6
tests/test_processing.py View File

12
 
12
 
13
 class TestProcessing(BaseTest):
13
 class TestProcessing(BaseTest):
14
     @staticmethod
14
     @staticmethod
15
-    def _make_job_with_scalar(data_chunk: list) -> tuple:
15
+    def _make_job_with_scalar(
16
+            data_chunk: list,
17
+            process_number: int,
18
+            process_count: int,
19
+    ) -> tuple:
16
         current_pid = os.getpid()
20
         current_pid = os.getpid()
17
         result = sum(data_chunk)
21
         result = sum(data_chunk)
18
         return current_pid, result
22
         return current_pid, result
19
 
23
 
20
     @staticmethod
24
     @staticmethod
21
-    def _make_job_with_object(data_chunk: list) -> tuple:
25
+    def _make_job_with_object(
26
+            data_chunk: list,
27
+            process_number: int,
28
+            process_count: int,
29
+    ) -> tuple:
22
         current_pid = os.getpid()
30
         current_pid = os.getpid()
23
         data = [o.value for o in data_chunk]
31
         data = [o.value for o in data_chunk]
24
         result = sum(data)
32
         result = sum(data)
29
         process_manager = ProcessManager(
37
         process_manager = ProcessManager(
30
             process_count=4,
38
             process_count=4,
31
             chunk_manager=chunk_manager,
39
             chunk_manager=chunk_manager,
32
-            job_maker=self._make_job_with_scalar,
33
         )
40
         )
34
 
41
 
35
         data = list(range(100))
42
         data = list(range(100))
36
         process_id_list = []
43
         process_id_list = []
37
         final_result = 0
44
         final_result = 0
38
 
45
 
39
-        results = process_manager.execute_jobs(data)
46
+        results = process_manager.chunk_and_execute_jobs(
47
+            data,
48
+            job_maker=self._make_job_with_scalar,
49
+        )
40
 
50
 
41
         for process_id, result in results:
51
         for process_id, result in results:
42
             final_result += result
52
             final_result += result
54
         process_manager = ProcessManager(
64
         process_manager = ProcessManager(
55
             process_count=4,
65
             process_count=4,
56
             chunk_manager=chunk_manager,
66
             chunk_manager=chunk_manager,
57
-            job_maker=self._make_job_with_object,
58
         )
67
         )
59
 
68
 
60
         data = [MyFakeClass(v) for v in range(100)]
69
         data = [MyFakeClass(v) for v in range(100)]
61
         process_id_list = []
70
         process_id_list = []
62
         final_result = 0
71
         final_result = 0
63
 
72
 
64
-        results = process_manager.execute_jobs(data)
73
+        results = process_manager.chunk_and_execute_jobs(
74
+            data,
75
+            job_maker=self._make_job_with_object,
76
+        )
65
 
77
 
66
         for process_id, result_object in results:
78
         for process_id, result_object in results:
67
             final_result += result_object.value
79
             final_result += result_object.value

+ 62 - 56
tests/test_xyz.py View File

2
 from synergine2.simulation import Subject
2
 from synergine2.simulation import Subject
3
 from synergine2.simulation import Subjects
3
 from synergine2.simulation import Subjects
4
 from synergine2.simulation import Simulation
4
 from synergine2.simulation import Simulation
5
-from synergine2.xyz import ProximityMechanism
5
+from synergine2.xyz import ProximitySubjectMechanism
6
 from synergine2.xyz import XYZSubjectMixin
6
 from synergine2.xyz import XYZSubjectMixin
7
 from synergine2.xyz_utils import get_positions_from_str_representation
7
 from synergine2.xyz_utils import get_positions_from_str_representation
8
 from synergine2.xyz_utils import get_str_representation_from_positions
8
 from synergine2.xyz_utils import get_str_representation_from_positions
14
     pass
14
     pass
15
 
15
 
16
 
16
 
17
-class MyProximityMechanism(ProximityMechanism):
17
+class MyProximityMechanism(ProximitySubjectMechanism):
18
     distance = 10
18
     distance = 10
19
 
19
 
20
 
20
 
34
             subject=subject,
34
             subject=subject,
35
         )
35
         )
36
 
36
 
37
-        assert 5 == proximity_mechanism.get_distance_of(other_subject)
37
+        assert 5 == proximity_mechanism.get_distance_of(
38
+            position=subject.position,
39
+            subject=other_subject,
40
+        )
38
         assert [{
41
         assert [{
39
             'subject': other_subject,
42
             'subject': other_subject,
40
             'direction': 90.0,
43
             'direction': 90.0,
56
             subject=subject,
59
             subject=subject,
57
         )
60
         )
58
 
61
 
59
-        assert 11 == proximity_mechanism.get_distance_of(other_subject)
62
+        assert 11 == proximity_mechanism.get_distance_of(
63
+            position=subject.position,
64
+            subject=other_subject,
65
+        )
60
         # other_subject is to far away
66
         # other_subject is to far away
61
         assert [] == proximity_mechanism.run()
67
         assert [] == proximity_mechanism.run()
62
 
68
 
158
                 **str_kwargs
164
                 **str_kwargs
159
             )
165
             )
160
 
166
 
161
-    def test_str_representation_to_str_multi_levels(self):
162
-        expected = """
163
-            0 0 1 0 0
164
-            0 1 1 1 0
165
-            0 0 1 0 0
166
-            ----
167
-            0 0 0 0 0
168
-            0 0 1 0 0
169
-            0 0 0 0 0
170
-        """
171
-        items_positions = {
172
-            '0': [
173
-                (-2, -1, 0),
174
-                (-1, -1, 0),
175
-                (1, -1, 0),
176
-                (2, -1, 0),
177
-                (-2, 0, 0),
178
-                (2, 0, 0),
179
-                (-2, 1, 0),
180
-                (-1, 1, 0),
181
-                (1, 1, 0),
182
-                (2, 1, 0),
183
-                (-2, -1, 1),
184
-                (-1, -1, 1),
185
-                (1, -1, 1),
186
-                (2, -1, 1),
187
-                (-1, 0, 1),
188
-                (-2, 0, 1),
189
-                (2, 0, 1),
190
-                (-2, 1, 1),
191
-                (-1, 1, 1),
192
-                (1, 1, 1),
193
-                (2, 1, 1),
194
-                (2, -1, 1),
195
-                (1, 0, 1),
196
-                (2, 1, 1),
197
-            ],
198
-            '1': [
199
-                (0, -1, 0),
200
-                (-1, 0, 0),
201
-                (0, 0, 0),
202
-                (1, 0, 0),
203
-                (0, 1, 0),
204
-                (0, 0, 1),
205
-            ],
206
-        }
207
-
208
-        assert expected == \
209
-            get_str_representation_from_positions(
210
-                items_positions,
211
-                **str_kwargs
212
-            )
167
+    # def test_str_representation_to_str_multi_levels(self):
168
+    #     expected = """
169
+    #         0 0 1 0 0
170
+    #         0 1 1 1 0
171
+    #         0 0 1 0 0
172
+    #         ----
173
+    #         0 0 0 0 0
174
+    #         0 0 1 0 0
175
+    #         0 0 0 0 0
176
+    #     """
177
+    #     items_positions = {
178
+    #         '0': [
179
+    #             (-2, -1, 0),
180
+    #             (-1, -1, 0),
181
+    #             (1, -1, 0),
182
+    #             (2, -1, 0),
183
+    #             (-2, 0, 0),
184
+    #             (2, 0, 0),
185
+    #             (-2, 1, 0),
186
+    #             (-1, 1, 0),
187
+    #             (1, 1, 0),
188
+    #             (2, 1, 0),
189
+    #             (-2, -1, 1),
190
+    #             (-1, -1, 1),
191
+    #             (1, -1, 1),
192
+    #             (2, -1, 1),
193
+    #             (-1, 0, 1),
194
+    #             (-2, 0, 1),
195
+    #             (2, 0, 1),
196
+    #             (-2, 1, 1),
197
+    #             (-1, 1, 1),
198
+    #             (1, 1, 1),
199
+    #             (2, 1, 1),
200
+    #             (2, -1, 1),
201
+    #             (1, 0, 1),
202
+    #             (2, 1, 1),
203
+    #         ],
204
+    #         '1': [
205
+    #             (0, -1, 0),
206
+    #             (-1, 0, 0),
207
+    #             (0, 0, 0),
208
+    #             (1, 0, 0),
209
+    #             (0, 1, 0),
210
+    #             (0, 0, 1),
211
+    #         ],
212
+    #     }
213
+    #
214
+    #     assert expected == \
215
+    #         get_str_representation_from_positions(
216
+    #             items_positions,
217
+    #             **str_kwargs
218
+    #         )