Bastien Sevajol 8 лет назад
Родитель
Сommit
559ead41cf
7 измененных файлов: 139 добавлений и 17 удалений
  1. 58 5
      sandbox/engulf/behaviour.py
  2. 7 0
      sandbox/engulf/config.yaml
  3. 11 1
      sandbox/engulf/gui.py
  4. 15 6
      sandbox/engulf/run.py
  5. 19 1
      sandbox/engulf/subject.py
  6. 25 3
      synergine2/simulation.py
  7. 4 1
      synergine2/xyz.py

+ 58 - 5
sandbox/engulf/behaviour.py Просмотреть файл

10
     get_direction_from_north_degree
10
     get_direction_from_north_degree
11
 from synergine2.xyz_utils import get_around_positions_of_positions, get_around_positions_of, get_position_for_direction
11
 from synergine2.xyz_utils import get_around_positions_of_positions, get_around_positions_of, get_position_for_direction
12
 
12
 
13
+# Import for typing hint
14
+if False:
15
+    from sandbox.engulf.subject import Grass
16
+
13
 
17
 
14
 class GrassGrownUp(Event):
18
 class GrassGrownUp(Event):
15
     def __init__(self, subject_id, density, *args, **kwargs):
19
     def __init__(self, subject_id, density, *args, **kwargs):
127
     distance = 1.41  # distance when on angle
131
     distance = 1.41  # distance when on angle
128
     feel_collections = [COLLECTION_GRASS]
132
     feel_collections = [COLLECTION_GRASS]
129
 
133
 
134
+    def acceptable_subject(self, subject: 'Grass') -> bool:
135
+        return subject.density >= self.config.simulation.eat_grass_required_density
136
+
130
 
137
 
131
 class MoveTo(Event):
138
 class MoveTo(Event):
132
     def __init__(self, subject_id: int, position: tuple, *args, **kwargs):
139
     def __init__(self, subject_id: int, position: tuple, *args, **kwargs):
142
         )
149
         )
143
 
150
 
144
 
151
 
152
+class EatEvent(Event):
153
+    def __init__(self, eaten_id: int, eater_id: int, eaten_new_density: float, *args, **kwargs):
154
+        super().__init__(*args, **kwargs)
155
+        self.eaten_id = eaten_id
156
+        self.eater_id = eater_id
157
+        self.eaten_new_density = eaten_new_density
158
+
159
+
145
 class SearchFood(SubjectBehaviour):
160
 class SearchFood(SubjectBehaviour):
146
     """
161
     """
147
     Si une nourriture a une case de distance et cellule non rassasié, move dans sa direction.
162
     Si une nourriture a une case de distance et cellule non rassasié, move dans sa direction.
149
     use = [EatableDirectProximityMechanism]
164
     use = [EatableDirectProximityMechanism]
150
 
165
 
151
     def run(self, data):
166
     def run(self, data):
167
+        if self.subject.appetite < self.config.simulation.search_food_appetite_required:
168
+            return False
169
+
152
         if not data[EatableDirectProximityMechanism]:
170
         if not data[EatableDirectProximityMechanism]:
153
             return False
171
             return False
154
 
172
 
169
     Prduit un immobilisme si sur une case de nourriture, dans le cas ou la cellule n'est as rassasié.
187
     Prduit un immobilisme si sur une case de nourriture, dans le cas ou la cellule n'est as rassasié.
170
     """
188
     """
171
     def run(self, data):
189
     def run(self, data):
172
-        pass
190
+        if self.subject.appetite < self.config.simulation.eat_grass_required_density:
191
+            return False
192
+
193
+        for grass in self.simulation.collections.get(COLLECTION_GRASS, []):
194
+            if grass.position == self.subject.position:
195
+                return grass.id
196
+
197
+    def action(self, data) -> [Event]:
198
+        subject_id = data
199
+
200
+        grass = self.simulation.subjects.index[subject_id]  # TODO: cas ou grass disparu ?
201
+        grass.density -= self.config.simulation.eat_grass_density_reduction
202
+
203
+        self.subject.appetite -= self.config.simulation.eat_grass_appetite_reduction
204
+
205
+        # TODO: Comment mettre des logs (ne peuvent pas être passé aux subprocess)?
206
+
207
+        return [EatEvent(
208
+            eater_id=self.subject.id,
209
+            eaten_id=subject_id,
210
+            eaten_new_density=grass.density,
211
+        )]
173
 
212
 
174
 
213
 
175
 class Explore(SubjectBehaviour):
214
 class Explore(SubjectBehaviour):
195
         return choice(DIRECTION_SLIGHTLY[self.subject.previous_direction])
234
         return choice(DIRECTION_SLIGHTLY[self.subject.previous_direction])
196
 
235
 
197
 
236
 
237
+class Hungry(SubjectBehaviour):
238
+    def run(self, data):
239
+        return True
240
+
241
+    def action(self, data) -> [Event]:
242
+        self.subject.appetite += self.config.simulation.hungry_reduction
243
+        return []
244
+
245
+    def get_random_direction(self):
246
+        if not self.subject.previous_direction:
247
+            return choice(DIRECTIONS)
248
+        return choice(DIRECTION_SLIGHTLY[self.subject.previous_direction])
249
+
250
+
198
 class CellBehaviourSelector(SubjectBehaviourSelector):
251
 class CellBehaviourSelector(SubjectBehaviourSelector):
199
     # If behaviour in sublist, only one be kept in sublist
252
     # If behaviour in sublist, only one be kept in sublist
200
     behaviour_hierarchy = (  # TODO: refact it
253
     behaviour_hierarchy = (  # TODO: refact it
201
         (
254
         (
202
-            Eat,
203
-            SearchFood,
255
+            Eat,  # TODO: Introduce priority with appetite
256
+            SearchFood,  # TODO: Introduce priority with appetite
204
             Explore,
257
             Explore,
205
         ),
258
         ),
206
     )
259
     )
253
             if behaviour_class != exclude_behaviour_class:
306
             if behaviour_class != exclude_behaviour_class:
254
                 try:
307
                 try:
255
                     behaviour_position = sublist.index(behaviour_class)
308
                     behaviour_position = sublist.index(behaviour_class)
309
+                    if position is None or behaviour_position < position:
310
+                        position = behaviour_position
256
                 except ValueError:
311
                 except ValueError:
257
                     pass
312
                     pass
258
-                if position is None or behaviour_position < position:
259
-                    position = behaviour_position
260
 
313
 
261
         return position
314
         return position

+ 7 - 0
sandbox/engulf/config.yaml Просмотреть файл

2
     cycle_duration: 0.5
2
     cycle_duration: 0.5
3
 terminals:
3
 terminals:
4
     sync: True
4
     sync: True
5
+simulation:
6
+    start_appetite: 50
7
+    hungry_reduction: 0.25
8
+    search_food_appetite_required: 30
9
+    eat_grass_required_density: 25
10
+    eat_grass_appetite_reduction: 5
11
+    eat_grass_density_reduction: 25

+ 11 - 1
sandbox/engulf/gui.py Просмотреть файл

4
 import cocos
4
 import cocos
5
 from cocos.actions import MoveTo, Repeat, ScaleBy, Reverse, RotateTo
5
 from cocos.actions import MoveTo, Repeat, ScaleBy, Reverse, RotateTo
6
 from cocos.sprite import Sprite
6
 from cocos.sprite import Sprite
7
-from sandbox.engulf.behaviour import GrassGrownUp, GrassSpawn, MoveTo as MoveToEvent
7
+from sandbox.engulf.behaviour import GrassGrownUp, GrassSpawn, MoveTo as MoveToEvent, EatEvent
8
 from sandbox.engulf.subject import Cell, Grass
8
 from sandbox.engulf.subject import Cell, Grass
9
 from synergine2.terminals import TerminalPackage
9
 from synergine2.terminals import TerminalPackage
10
 from synergine2_cocos2d.gui import Gui, GridLayerMixin
10
 from synergine2_cocos2d.gui import Gui, GridLayerMixin
101
             MoveToEvent,
101
             MoveToEvent,
102
             self.on_move_to,
102
             self.on_move_to,
103
         )
103
         )
104
+        self.terminal.register_event_handler(
105
+            EatEvent,
106
+            self.on_eat,
107
+        )
104
 
108
 
105
     def get_main_scene(self):
109
     def get_main_scene(self):
106
         return self.main_scene
110
         return self.main_scene
135
             event.subject_id,
139
             event.subject_id,
136
             event.position,
140
             event.position,
137
         )
141
         )
142
+
143
+    def on_eat(self, event: EatEvent):
144
+        self.main_layer.grasses.set_density(
145
+            event.eaten_id,
146
+            event.eaten_new_density,
147
+        )

+ 15 - 6
sandbox/engulf/run.py Просмотреть файл

27
 sys.path.append(synergine2_ath)
27
 sys.path.append(synergine2_ath)
28
 
28
 
29
 from random import randint, seed
29
 from random import randint, seed
30
-from sandbox.engulf.behaviour import GrassGrownUp, GrassSpawn, GrassSpawnBehaviour, MoveTo
30
+from sandbox.engulf.behaviour import GrassGrownUp, GrassSpawn, GrassSpawnBehaviour, MoveTo, EatEvent
31
 
31
 
32
 from synergine2.config import Config
32
 from synergine2.config import Config
33
 from synergine2.log import get_default_logger
33
 from synergine2.log import get_default_logger
52
         GrassGrownUp,
52
         GrassGrownUp,
53
         GrassSpawn,
53
         GrassSpawn,
54
         MoveTo,
54
         MoveTo,
55
+        EatEvent,
55
     ]
56
     ]
56
 
57
 
57
     def __init__(self, *args, **kwargs):
58
     def __init__(self, *args, **kwargs):
71
 
72
 
72
 
73
 
73
 def fill_with_random_cells(
74
 def fill_with_random_cells(
75
+    config,
74
     subjects: EngulfSubjects,
76
     subjects: EngulfSubjects,
75
     count: int,
77
     count: int,
76
     start_position: tuple,
78
     start_position: tuple,
86
         )
88
         )
87
         if position not in subjects.cell_xyz:
89
         if position not in subjects.cell_xyz:
88
             cell = Cell(
90
             cell = Cell(
91
+                config,
89
                 simulation=subjects.simulation,
92
                 simulation=subjects.simulation,
90
                 position=position,
93
                 position=position,
91
             )
94
             )
94
 
97
 
95
 
98
 
96
 def fill_with_random_grass(
99
 def fill_with_random_grass(
100
+    config,
97
     subjects: EngulfSubjects,
101
     subjects: EngulfSubjects,
98
     start_count: int,
102
     start_count: int,
99
     start_position: tuple,
103
     start_position: tuple,
111
 
115
 
112
         if position not in subjects.grass_xyz:
116
         if position not in subjects.grass_xyz:
113
             grass = Grass(
117
             grass = Grass(
118
+                config,
114
                 simulation=subjects.simulation,
119
                 simulation=subjects.simulation,
115
                 position=position,
120
                 position=position,
116
             )
121
             )
121
         for around in get_around_positions_of(grass.position, distance=density):
126
         for around in get_around_positions_of(grass.position, distance=density):
122
             if around not in subjects.grass_xyz:
127
             if around not in subjects.grass_xyz:
123
                 new_grass = Grass(
128
                 new_grass = Grass(
129
+                    config,
124
                     simulation=subjects.simulation,
130
                     simulation=subjects.simulation,
125
                     position=around,
131
                     position=around,
126
                 )
132
                 )
131
 
137
 
132
 def main():
138
 def main():
133
     seed(0)
139
     seed(0)
134
-    simulation = Engulf()
140
+
141
+    config = Config()
142
+    config.load_files(['sandbox/engulf/config.yaml'])
143
+    logger = get_default_logger(level=logging.DEBUG)
144
+
145
+    simulation = Engulf(config)
135
     subjects = EngulfSubjects(simulation=simulation)
146
     subjects = EngulfSubjects(simulation=simulation)
136
     fill_with_random_cells(
147
     fill_with_random_cells(
148
+        config,
137
         subjects,
149
         subjects,
138
         30,
150
         30,
139
         (-34, -34, 0),
151
         (-34, -34, 0),
140
         (34, 34, 0),
152
         (34, 34, 0),
141
     )
153
     )
142
     fill_with_random_grass(
154
     fill_with_random_grass(
155
+        config,
143
         subjects,
156
         subjects,
144
         5,
157
         5,
145
         (-34, -34, 0),
158
         (-34, -34, 0),
147
     )
160
     )
148
     simulation.subjects = subjects
161
     simulation.subjects = subjects
149
 
162
 
150
-    config = Config()
151
-    config.load_files(['sandbox/engulf/config.yaml'])
152
-    logger = get_default_logger(level=logging.DEBUG)
153
-
154
     core = Core(
163
     core = Core(
155
         config=config,
164
         config=config,
156
         logger=logger,
165
         logger=logger,

+ 19 - 1
sandbox/engulf/subject.py Просмотреть файл

1
 # coding: utf-8
1
 # coding: utf-8
2
-from sandbox.engulf.behaviour import GrowUp, SearchFood, Eat, Explore, CellBehaviourSelector
2
+from sandbox.engulf.behaviour import GrowUp, SearchFood, Eat, Explore, CellBehaviourSelector, Hungry
3
 from sandbox.engulf.const import COLLECTION_CELL, COLLECTION_ALIVE, COLLECTION_EATABLE, COLLECTION_GRASS
3
 from sandbox.engulf.const import COLLECTION_CELL, COLLECTION_ALIVE, COLLECTION_EATABLE, COLLECTION_GRASS
4
 from synergine2.simulation import Subject
4
 from synergine2.simulation import Subject
5
 from synergine2.xyz import XYZSubjectMixin
5
 from synergine2.xyz import XYZSubjectMixin
16
         SearchFood,
16
         SearchFood,
17
         Eat,
17
         Eat,
18
         Explore,
18
         Explore,
19
+        Hungry,
19
     ]
20
     ]
20
     behaviour_selector_class = CellBehaviourSelector
21
     behaviour_selector_class = CellBehaviourSelector
21
 
22
 
23
+    def __init__(self, *args, **kwargs):
24
+        super().__init__(*args, **kwargs)
25
+        self._appetite = self.config.simulation.start_appetite  # /100
26
+
27
+    @property
28
+    def appetite(self) -> float:
29
+        return self._appetite
30
+
31
+    @appetite.setter
32
+    def appetite(self, value) -> None:
33
+        if value > 100:
34
+            self._appetite = 100
35
+        elif value < 0:
36
+            self._appetite = 0
37
+        else:
38
+            self._appetite = value
39
+
22
 
40
 
23
 class Grass(XYZSubjectMixin, Subject):
41
 class Grass(XYZSubjectMixin, Subject):
24
     collections = [
42
     collections = [

+ 25 - 3
synergine2/simulation.py Просмотреть файл

3
 import typing
3
 import typing
4
 
4
 
5
 from synergine2.base import BaseObject
5
 from synergine2.base import BaseObject
6
+from synergine2.config import Config
6
 from synergine2.utils import get_mechanisms_classes
7
 from synergine2.utils import get_mechanisms_classes
7
 
8
 
8
 
9
 
11
     behaviours_classes = []
12
     behaviours_classes = []
12
     behaviour_selector_class = None  # type: typing.Type[SubjectBehaviourSelector]
13
     behaviour_selector_class = None  # type: typing.Type[SubjectBehaviourSelector]
13
 
14
 
14
-    def __init__(self, simulation: 'Simulation'):
15
+    def __init__(
16
+        self,
17
+        config: Config,
18
+        simulation: 'Simulation',
19
+    ):
20
+        self.config = config
15
         self.id = id(self)  # We store object id because it's lost between process
21
         self.id = id(self)  # We store object id because it's lost between process
16
         self.simulation = simulation
22
         self.simulation = simulation
17
         self.behaviours = {}
23
         self.behaviours = {}
37
     def initialize(self):
43
     def initialize(self):
38
         for mechanism_class in get_mechanisms_classes(self):
44
         for mechanism_class in get_mechanisms_classes(self):
39
             self.mechanisms[mechanism_class] = mechanism_class(
45
             self.mechanisms[mechanism_class] = mechanism_class(
46
+                config=self.config,
40
                 simulation=self.simulation,
47
                 simulation=self.simulation,
41
                 subject=self,
48
                 subject=self,
42
             )
49
             )
43
 
50
 
44
         for behaviour_class in self.behaviours_classes:
51
         for behaviour_class in self.behaviours_classes:
45
             self.behaviours[behaviour_class] = behaviour_class(
52
             self.behaviours[behaviour_class] = behaviour_class(
53
+                config=self.config,
46
                 simulation=self.simulation,
54
                 simulation=self.simulation,
47
                 subject=self,
55
                 subject=self,
48
             )
56
             )
86
     accepted_subject_class = Subjects
94
     accepted_subject_class = Subjects
87
     behaviours_classes = []
95
     behaviours_classes = []
88
 
96
 
89
-    def __init__(self):
97
+    def __init__(
98
+        self,
99
+        config: Config,
100
+    ):
101
+        self.config = config
90
         self.collections = collections.defaultdict(list)
102
         self.collections = collections.defaultdict(list)
91
         self._subjects = None
103
         self._subjects = None
92
         self.behaviours = {}
104
         self.behaviours = {}
109
     def initialize(self):
121
     def initialize(self):
110
         for mechanism_class in get_mechanisms_classes(self):
122
         for mechanism_class in get_mechanisms_classes(self):
111
             self.mechanisms[mechanism_class] = mechanism_class(
123
             self.mechanisms[mechanism_class] = mechanism_class(
124
+                config=self.config,
112
                 simulation=self,
125
                 simulation=self,
113
             )
126
             )
114
 
127
 
115
         for behaviour_class in self.behaviours_classes:
128
         for behaviour_class in self.behaviours_classes:
116
             self.behaviours[behaviour_class] = behaviour_class(
129
             self.behaviours[behaviour_class] = behaviour_class(
130
+                config=self.config,
117
                 simulation=self,
131
                 simulation=self,
118
             )
132
             )
119
 
133
 
121
 class SubjectMechanism(BaseObject):
135
 class SubjectMechanism(BaseObject):
122
     def __init__(
136
     def __init__(
123
             self,
137
             self,
138
+            config: Config,
124
             simulation: Simulation,
139
             simulation: Simulation,
125
             subject: Subject,
140
             subject: Subject,
126
     ):
141
     ):
142
+        self.config = config
127
         self.simulation = simulation
143
         self.simulation = simulation
128
         self.subject = subject
144
         self.subject = subject
129
 
145
 
137
 
153
 
138
     def __init__(
154
     def __init__(
139
             self,
155
             self,
156
+            config: Config,
140
             simulation: Simulation,
157
             simulation: Simulation,
141
     ):
158
     ):
159
+        self.config = config
142
         self.simulation = simulation
160
         self.simulation = simulation
143
 
161
 
144
     def repr_debug(self) -> str:
162
     def repr_debug(self) -> str:
162
 
180
 
163
     def __init__(
181
     def __init__(
164
             self,
182
             self,
183
+            config: Config,
165
             simulation: Simulation,
184
             simulation: Simulation,
166
             subject: Subject,
185
             subject: Subject,
167
     ):
186
     ):
187
+        self.config = config
168
         self.simulation = simulation
188
         self.simulation = simulation
169
         self.subject = subject
189
         self.subject = subject
170
 
190
 
177
         Note: Returned data will be transfered from sub processes.
197
         Note: Returned data will be transfered from sub processes.
178
               Prefer scalar types.
198
               Prefer scalar types.
179
         """
199
         """
180
-        raise NotImplementedError()
200
+        raise NotImplementedError()  # TODO Test it and change to strictly False
181
 
201
 
182
     def action(self, data) -> [Event]:
202
     def action(self, data) -> [Event]:
183
         """
203
         """
193
 
213
 
194
     def __init__(
214
     def __init__(
195
             self,
215
             self,
216
+            config: Config,
196
             simulation: Simulation,
217
             simulation: Simulation,
197
     ):
218
     ):
219
+        self.config = config
198
         self.simulation = simulation
220
         self.simulation = simulation
199
 
221
 
200
     def run(self, data):
222
     def run(self, data):

+ 4 - 1
synergine2/xyz.py Просмотреть файл

152
                     ),
152
                     ),
153
                     self.distance_round_decimals,
153
                     self.distance_round_decimals,
154
                 )
154
                 )
155
-                if distance <= self.distance:
155
+                if distance <= self.distance and self.acceptable_subject(subject):
156
                     direction = round(
156
                     direction = round(
157
                         get_degree_from_north(
157
                         get_degree_from_north(
158
                             position,
158
                             position,
176
             subject.position,
176
             subject.position,
177
         )
177
         )
178
 
178
 
179
+    def acceptable_subject(self, subject: Subject) -> bool:
180
+        pass
181
+
179
 
182
 
180
 class ProximitySubjectMechanism(ProximityMixin, SubjectMechanism):
183
 class ProximitySubjectMechanism(ProximityMixin, SubjectMechanism):
181
     def run(self):
184
     def run(self):