Browse Source

Life game with real print terminal

Bastien Sevajol 7 years ago
parent
commit
2fb1e8f9d4

+ 0 - 1
sandbox/life_game/__init__.py View File

@@ -1 +0,0 @@
1
-__author__ = 'bux'

+ 84 - 0
sandbox/life_game/run.py View File

@@ -0,0 +1,84 @@
1
+import sys
2
+import collections
3
+from sandbox.life_game.simulation import CellBornBehaviour, CellDieBehaviour, Cell, Empty
4
+
5
+from sandbox.life_game.utils import get_subjects_from_str_representation
6
+from synergine2.core import Core
7
+from synergine2.cycle import CycleManager
8
+from synergine2.simulation import Simulation
9
+from synergine2.terminals import Terminal
10
+from synergine2.terminals import TerminalPackage
11
+from synergine2.terminals import TerminalManager
12
+from synergine2.xyz_utils import get_str_representation_from_positions
13
+
14
+
15
+class SimplePrintTerminal(Terminal):
16
+    def __init__(self):
17
+        super().__init__()
18
+        self.subjects = None
19
+
20
+    def receive(self, value):
21
+        self.update_with_package(value)
22
+        self.print_str_representation()
23
+
24
+    def update_with_package(self, package: TerminalPackage):
25
+        self.subjects = package.subjects if package.subjects else self.subjects
26
+        for subject_id, actions in package.actions.items():
27
+            for action, value in actions.items():
28
+                if action == CellBornBehaviour:
29
+                    # Remove Empty subject
30
+                    self.subjects = [s for s in self.subjects[:] if s.id != subject_id]
31
+                    # Add born subject
32
+                    self.subjects.append(value)
33
+                if action == CellDieBehaviour:
34
+                    # Remove Cell subject
35
+                    self.subjects = [s for s in self.subjects[:] if s.id != subject_id]
36
+                    # Add Empty subject
37
+                    self.subjects.append(value)
38
+
39
+    def print_str_representation(self):
40
+        items_positions = collections.defaultdict(list)
41
+        for subject in self.subjects:
42
+            if type(subject) == Cell:
43
+                items_positions['1'].append(subject.position)
44
+            if type(subject) == Empty:
45
+                items_positions['0'].append(subject.position)
46
+        print(get_str_representation_from_positions(
47
+            items_positions,
48
+            separator=' ',
49
+            #force_items_as=(('0', ' '),),
50
+        ))
51
+        print()
52
+
53
+
54
+def main():
55
+    start_str_representation = """
56
+        0 0 0 0 0 0 0 0 0 0 0
57
+        0 0 0 1 1 1 1 0 0 0 0
58
+        0 0 0 1 0 0 1 0 0 0 0
59
+        0 1 1 1 0 0 1 1 1 0 0
60
+        0 1 0 0 0 0 0 0 1 0 0
61
+        0 1 0 0 0 0 0 0 1 0 0
62
+        0 1 1 1 0 0 1 1 1 0 0
63
+        0 0 0 1 0 0 1 0 0 0 0
64
+        0 0 0 1 1 1 1 0 0 0 0
65
+        0 0 0 0 0 0 0 0 0 0 0
66
+        0 0 0 0 0 0 0 0 0 0 0
67
+    """
68
+    simulation = Simulation()
69
+    subjects = get_subjects_from_str_representation(
70
+        start_str_representation,
71
+        simulation,
72
+    )
73
+    simulation.subjects = subjects
74
+
75
+    core = Core(
76
+        simulation=simulation,
77
+        cycle_manager=CycleManager(subjects=subjects),
78
+        terminal_manager=TerminalManager([SimplePrintTerminal()]),
79
+    )
80
+    core.run()
81
+
82
+
83
+if __name__ == '__main__':
84
+    main()

+ 2 - 0
sandbox/life_game/simulation.py View File

@@ -29,6 +29,7 @@ class CellDieBehaviour(Behaviour):
29 29
         )
30 30
         self.simulation.subjects.remove(self.subject)
31 31
         self.simulation.subjects.append(new_empty)
32
+        return new_empty
32 33
 
33 34
 
34 35
 class CellBornBehaviour(Behaviour):
@@ -47,6 +48,7 @@ class CellBornBehaviour(Behaviour):
47 48
         )
48 49
         self.simulation.subjects.remove(self.subject)
49 50
         self.simulation.subjects.append(new_cell)
51
+        return new_cell
50 52
 
51 53
 
52 54
 class Cell(XYZSubjectMixin, Subject):

+ 25 - 0
sandbox/life_game/utils.py View File

@@ -0,0 +1,25 @@
1
+from sandbox.life_game.simulation import Empty
2
+from sandbox.life_game.simulation import Cell
3
+from synergine2.simulation import Subjects, Simulation
4
+from synergine2.xyz_utils import get_positions_from_str_representation
5
+
6
+
7
+def get_subjects_from_str_representation(
8
+    str_representations: str,
9
+    simulation: Simulation,
10
+) -> [Cell, Empty]:
11
+    subjects = Subjects(simulation=simulation)
12
+    items_positions = get_positions_from_str_representation(str_representations)
13
+    for item, positions in items_positions.items():
14
+        for position in positions:
15
+            if item == '0':
16
+                subjects.append(Empty(
17
+                    simulation=simulation,
18
+                    position=position,
19
+                ))
20
+            if item == '1':
21
+                subjects.append(Cell(
22
+                    simulation=simulation,
23
+                    position=position,
24
+                ))
25
+    return subjects

+ 35 - 0
synergine2/core.py View File

@@ -0,0 +1,35 @@
1
+from synergine2.cycle import CycleManager
2
+from synergine2.simulation import Simulation
3
+from synergine2.terminals import TerminalManager
4
+from synergine2.terminals import TerminalPackage
5
+
6
+
7
+class Core(object):
8
+    def __init__(
9
+        self,
10
+        simulation: Simulation,
11
+        cycle_manager: CycleManager,
12
+        terminal_manager: TerminalManager=None,
13
+
14
+    ):
15
+        self.simulation = simulation
16
+        self.cycle_manager = cycle_manager
17
+        self.terminal_manager = terminal_manager or TerminalManager([])
18
+
19
+    def run(self):
20
+        try:
21
+            self.terminal_manager.start()
22
+
23
+            start_package = TerminalPackage(subjects=self.simulation.subjects)
24
+            self.terminal_manager.send(start_package)
25
+
26
+            while True:
27
+                # TODO: receive from terminals
28
+                actions = self.cycle_manager.next()
29
+                cycle_package = TerminalPackage(actions=actions)
30
+                self.terminal_manager.send(cycle_package)
31
+                import time
32
+                time.sleep(1)  # TODO: tick control
33
+        except KeyboardInterrupt:
34
+            pass  # Just stop while
35
+        self.terminal_manager.stop()

+ 9 - 4
synergine2/cycle.py View File

@@ -1,4 +1,5 @@
1 1
 import multiprocessing
2
+import collections
2 3
 
3 4
 from synergine2.processing import ProcessManager
4 5
 from synergine2.simulation import Subject, Behaviour, Mechanism
@@ -19,19 +20,23 @@ class CycleManager(object):
19 20
             )
20 21
 
21 22
         self.subjects = subjects
22
-        self._process_manager = process_manager
23
-        self._current_cycle = 0
23
+        self.process_manager = process_manager
24
+        self.current_cycle = 0
24 25
 
25 26
     def next(self):
26 27
         results = {}
27
-        results_by_processes = self._process_manager.execute_jobs(self.subjects)
28
+        results_by_processes = self.process_manager.execute_jobs(self.subjects)
29
+        actions = collections.defaultdict(dict)
28 30
         for process_results in results_by_processes:
29 31
             results.update(process_results)
30 32
         for subject in self.subjects[:]:  # Duplicate list to prevent conflicts with behaviours subjects manipulations
31 33
             for behaviour_class in results[subject.id]:
32 34
                 # TODO: Ajouter une etape de selection des actions a faire (genre neuronnal)
33 35
                 # TODO: les behaviour_class ont le même uniqueid apres le process ?
34
-                subject.behaviours[behaviour_class].action(results[subject.id][behaviour_class])
36
+                action_result = subject.behaviours[behaviour_class].action(results[subject.id][behaviour_class])
37
+                actions[subject.id][behaviour_class] = action_result
38
+
39
+        return actions
35 40
 
36 41
     def computing(self, subjects):
37 42
         # compute mechanisms (prepare check to not compute slienced or not used mechanisms)

+ 2 - 1
synergine2/simulation.py View File

@@ -82,8 +82,9 @@ class Behaviour(object):
82 82
         """
83 83
         raise NotImplementedError()
84 84
 
85
-    def action(self, data):
85
+    def action(self, data) -> object:
86 86
         """
87 87
         Method called in main process
88
+        Return value will be give to terminals
88 89
         """
89 90
         raise NotImplementedError()

+ 21 - 16
synergine2/terminals.py View File

@@ -4,17 +4,19 @@ from multiprocessing import Process
4 4
 from queue import Empty
5 5
 
6 6
 import time
7
+from synergine2.simulation import Simulation, Subject
7 8
 
8 9
 STOP_SIGNAL = '__STOP_SIGNAL__'
9 10
 
10 11
 
11 12
 class TerminalPackage(object):
12
-    def __init__(self, value):
13
-        self._value = value
14
-
15
-    @property
16
-    def value(self):
17
-        return self._value
13
+    def __init__(
14
+            self,
15
+            subjects: [Subject]=None,
16
+            actions: ['TODO']=None,
17
+    ):
18
+        self.subjects = subjects
19
+        self.actions = actions or {}
18 20
 
19 21
 
20 22
 class Terminal(object):
@@ -36,8 +38,11 @@ class Terminal(object):
36 38
         """
37 39
         Override this method to create your daemon terminal
38 40
         """
39
-        while self.read():
40
-            time.sleep(self.DEFAULT_SLEEP)
41
+        try:
42
+            while self.read():
43
+                time.sleep(self.DEFAULT_SLEEP)
44
+        except KeyboardInterrupt:
45
+            pass
41 46
 
42 47
     def read(self):
43 48
         while True:
@@ -51,11 +56,11 @@ class Terminal(object):
51 56
             except Empty:
52 57
                 return True  # Finished to read Queue
53 58
 
54
-    def receive(self, package: TerminalPackage):
59
+    def receive(self, value):
55 60
         raise NotImplementedError()
56 61
 
57
-    def send(self, package: TerminalPackage):
58
-        self._output_queue.put(package)
62
+    def send(self, value):
63
+        self._output_queue.put(value)
59 64
 
60 65
 
61 66
 class TerminalManager(object):
@@ -82,17 +87,17 @@ class TerminalManager(object):
82 87
         for output_queue in self._outputs_queues:
83 88
             output_queue.put(STOP_SIGNAL)
84 89
 
85
-    def send(self, package: TerminalPackage):
90
+    def send(self, value):
86 91
         for output_queue in self._outputs_queues:
87
-            output_queue.put(package)
92
+            output_queue.put(value)
88 93
 
89 94
     def receive(self) -> []:
90
-        packages = []
95
+        values = []
91 96
         for input_queue in self._inputs_queues:
92 97
             try:
93 98
                 while True:
94
-                    packages.append(input_queue.get(block=False, timeout=None))
99
+                    values.append(input_queue.get(block=False, timeout=None))
95 100
             except Empty:
96 101
                 pass  # Queue is empty
97 102
 
98
-        return packages
103
+        return values

+ 9 - 1
synergine2/xyz_utils.py View File

@@ -48,6 +48,7 @@ def get_str_representation_from_positions(
48 48
     tabulation='',
49 49
     start_with='',
50 50
     end_with='',
51
+    force_items_as=None,
51 52
 ) -> str:
52 53
     positions = []
53 54
     for item_positions in items_positions.values():
@@ -76,7 +77,14 @@ def get_str_representation_from_positions(
76 77
         if position[2] != current_z:
77 78
             str_representation += '----' + "\n" + tabulation
78 79
 
79
-        added_value = item
80
+        str_item = item
81
+        if force_items_as:
82
+            for force_item_as in force_items_as:
83
+                if force_item_as[0] == item:
84
+                    str_item = force_item_as[1]
85
+                    break
86
+
87
+        added_value = str_item
80 88
         if position[0] != start_x:
81 89
             added_value = separator + added_value
82 90
 

+ 122 - 15
tests/test_life_game.py View File

@@ -1,13 +1,32 @@
1 1
 import collections
2
-from sandbox.life_game.simulation import Cell, Empty
2
+from sandbox.life_game.simulation import Cell
3
+from sandbox.life_game.simulation import Empty
4
+from sandbox.life_game.utils import get_subjects_from_str_representation
3 5
 from synergine2.cycle import CycleManager
4
-from synergine2.simulation import Simulation, Subjects
5
-from synergine2.utils import initialize_subject
6
+from synergine2.simulation import Simulation
7
+from synergine2.simulation import Subjects
6 8
 from synergine2.xyz_utils import get_str_representation_from_positions
7
-from tests import BaseTest, str_kwargs
9
+from tests import BaseTest
10
+from tests import str_kwargs
8 11
 
9 12
 
10
-class TestSimpleSimulation(BaseTest):
13
+class LifeGameBaseTest(BaseTest):
14
+    def _get_str_representation_of_subjects(self, subjects: list):
15
+        items_positions = collections.defaultdict(list)
16
+
17
+        for subject in subjects:
18
+            if type(subject) == Cell:
19
+                items_positions['1'].append(subject.position)
20
+            if type(subject) == Empty:
21
+                items_positions['0'].append(subject.position)
22
+
23
+        return get_str_representation_from_positions(
24
+            items_positions,
25
+            **str_kwargs
26
+        )
27
+
28
+
29
+class TestSimpleSimulation(LifeGameBaseTest):
11 30
     def test_cycles_evolution(self):
12 31
         simulation = Simulation()
13 32
         subjects = self._get_subjects(simulation)
@@ -78,16 +97,104 @@ class TestSimpleSimulation(BaseTest):
78 97
             ))
79 98
         return cells
80 99
 
81
-    def _get_str_representation_of_subjects(self, subjects: list):
82
-        items_positions = collections.defaultdict(list)
83 100
 
84
-        for subject in subjects:
85
-            if type(subject) == Cell:
86
-                items_positions['1'].append(subject.position)
87
-            if type(subject) == Empty:
88
-                items_positions['0'].append(subject.position)
101
+class TestMultipleSimulations(LifeGameBaseTest):
102
+    def test_cross(self):
103
+        str_representations = [
104
+            """
105
+            0 0 0 0 0 0 0 0 0 0 0
106
+            0 0 0 1 1 1 1 0 0 0 0
107
+            0 0 0 1 0 0 1 0 0 0 0
108
+            0 1 1 1 0 0 1 1 1 0 0
109
+            0 1 0 0 0 0 0 0 1 0 0
110
+            0 1 0 0 0 0 0 0 1 0 0
111
+            0 1 1 1 0 0 1 1 1 0 0
112
+            0 0 0 1 0 0 1 0 0 0 0
113
+            0 0 0 1 1 1 1 0 0 0 0
114
+            0 0 0 0 0 0 0 0 0 0 0
115
+            0 0 0 0 0 0 0 0 0 0 0
116
+        """,
117
+            """
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
129
+        """,
130
+            """
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
155
+        """,
156
+            """
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
168
+        """,
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
+        ]
89 183
 
90
-        return get_str_representation_from_positions(
91
-            items_positions,
92
-            **str_kwargs
184
+        simulation = Simulation()
185
+        subjects = get_subjects_from_str_representation(
186
+            str_representations[0],
187
+            simulation,
188
+        )
189
+        simulation.subjects = subjects
190
+
191
+        cycle_manager = CycleManager(
192
+            subjects=subjects,
93 193
         )
194
+
195
+        for str_representation in str_representations:
196
+            assert str_representation == \
197
+               self._get_str_representation_of_subjects(
198
+                    subjects,
199
+                )
200
+            cycle_manager.next()

+ 18 - 19
tests/test_terminals.py View File

@@ -1,21 +1,20 @@
1 1
 import time
2 2
 
3 3
 from synergine2.terminals import Terminal
4
-from synergine2.terminals import TerminalPackage
5 4
 from synergine2.terminals import TerminalManager
6 5
 from tests import BaseTest
7 6
 
8 7
 
9 8
 class MultiplyTerminal(Terminal):
10
-    def receive(self, package: TerminalPackage):
11
-        self.send(TerminalPackage(package.value * 2))
12
-        self.send(TerminalPackage(package.value * 4))
9
+    def receive(self, value):
10
+        self.send(value * 2)
11
+        self.send(value * 4)
13 12
 
14 13
 
15 14
 class DivideTerminal(Terminal):
16
-    def receive(self, package: TerminalPackage):
17
-        self.send(TerminalPackage(package.value / 2))
18
-        self.send(TerminalPackage(package.value / 4))
15
+    def receive(self, value):
16
+        self.send(value / 2)
17
+        self.send(value / 4)
19 18
 
20 19
 
21 20
 class TestTerminals(BaseTest):
@@ -26,19 +25,19 @@ class TestTerminals(BaseTest):
26 25
             ]
27 26
         )
28 27
         terminals_manager.start()
29
-        terminals_manager.send(TerminalPackage(42))
28
+        terminals_manager.send(42)
30 29
 
31 30
         # We wait max 2s (see time.sleep) to consider
32 31
         # process have finished. If not, it will fail
33
-        packages = []
32
+        values = []
34 33
         for i in range(200):
35
-            packages.extend(terminals_manager.receive())
36
-            if len(packages) == 2:
34
+            values.extend(terminals_manager.receive())
35
+            if len(values) == 2:
37 36
                 break
38 37
             time.sleep(0.01)
39 38
 
40
-        assert 2 == len(packages)
41
-        values = [p.value for p in packages]
39
+        assert 2 == len(values)
40
+        values = [v for v in values]
42 41
         assert 84 in values
43 42
         assert 168 in values
44 43
 
@@ -52,19 +51,19 @@ class TestTerminals(BaseTest):
52 51
             ]
53 52
         )
54 53
         terminals_manager.start()
55
-        terminals_manager.send(TerminalPackage(42))
54
+        terminals_manager.send(42)
56 55
 
57 56
         # We wait max 2s (see time.sleep) to consider
58 57
         # process have finished. If not, it will fail
59
-        packages = []
58
+        values = []
60 59
         for i in range(200):
61
-            packages.extend(terminals_manager.receive())
62
-            if len(packages) == 4:
60
+            values.extend(terminals_manager.receive())
61
+            if len(values) == 4:
63 62
                 break
64 63
             time.sleep(0.01)
65 64
 
66
-        assert 4 == len(packages)
67
-        values = [p.value for p in packages]
65
+        assert 4 == len(values)
66
+        values = [v for v in values]
68 67
         assert 84 in values
69 68
         assert 168 in values
70 69
         assert 21 in values