Browse Source

engulf: prey and predator

Bastien Sevajol 7 years ago
parent
commit
f98fbd3f78

+ 42 - 12
sandbox/engulf/behaviour.py View File

2
 import typing
2
 import typing
3
 from random import choice
3
 from random import choice
4
 
4
 
5
-from sandbox.engulf.const import COLLECTION_GRASS
5
+from sandbox.engulf.const import COLLECTION_GRASS, COLLECTION_CELL
6
 from sandbox.engulf.exceptions import NotFoundWhereToGo
6
 from sandbox.engulf.exceptions import NotFoundWhereToGo
7
 from synergine2.simulation import SubjectBehaviour, SimulationMechanism, SimulationBehaviour, SubjectBehaviourSelector
7
 from synergine2.simulation import SubjectBehaviour, SimulationMechanism, SimulationBehaviour, SubjectBehaviourSelector
8
 from synergine2.simulation import Event
8
 from synergine2.simulation import Event
9
 from synergine2.utils import ChunkManager
9
 from synergine2.utils import ChunkManager
10
-from synergine2.xyz import ProximitySubjectMechanism, DIRECTIONS, DIRECTION_SLIGHTLY, DIRECTION_FROM_NORTH_DEGREES, \
11
-    get_direction_from_north_degree
12
-from synergine2.xyz_utils import get_around_positions_of_positions, get_around_positions_of, get_position_for_direction
10
+from synergine2.xyz import ProximitySubjectMechanism, DIRECTIONS, DIRECTION_SLIGHTLY, get_direction_from_north_degree
11
+from synergine2.xyz_utils import get_around_positions_of_positions, get_position_for_direction
13
 
12
 
14
 # Import for typing hint
13
 # Import for typing hint
15
 if False:
14
 if False:
16
     from sandbox.engulf.subject import Grass
15
     from sandbox.engulf.subject import Grass
16
+    from sandbox.engulf.subject import PreyCell
17
 
17
 
18
 
18
 
19
 class GrassGrownUp(Event):
19
 class GrassGrownUp(Event):
129
         return events
129
         return events
130
 
130
 
131
 
131
 
132
-class EatableDirectProximityMechanism(ProximitySubjectMechanism):
132
+class GrassEatableDirectProximityMechanism(ProximitySubjectMechanism):
133
     distance = 1.41  # distance when on angle
133
     distance = 1.41  # distance when on angle
134
     feel_collections = [COLLECTION_GRASS]
134
     feel_collections = [COLLECTION_GRASS]
135
 
135
 
137
         return subject.density >= self.config.simulation.eat_grass_required_density
137
         return subject.density >= self.config.simulation.eat_grass_required_density
138
 
138
 
139
 
139
 
140
+class PreyEatableDirectProximityMechanism(ProximitySubjectMechanism):
141
+    distance = 1.41  # distance when on angle
142
+    feel_collections = [COLLECTION_CELL]
143
+
144
+    def acceptable_subject(self, subject: 'PreyCell') -> bool:
145
+        # TODO: N'attaquer que des "herbivores" ?
146
+        from sandbox.engulf.subject import PreyCell  # cyclic
147
+        return isinstance(subject, PreyCell)
148
+
149
+
140
 class MoveTo(Event):
150
 class MoveTo(Event):
141
     def __init__(self, subject_id: int, position: tuple, *args, **kwargs):
151
     def __init__(self, subject_id: int, position: tuple, *args, **kwargs):
142
         super().__init__(*args, **kwargs)
152
         super().__init__(*args, **kwargs)
159
         self.eaten_new_density = eaten_new_density
169
         self.eaten_new_density = eaten_new_density
160
 
170
 
161
 
171
 
162
-class SearchFood(SubjectBehaviour):
172
+class AttackEvent(Event):
173
+    def __init__(self, attacker_id: int, attacked_id: int, *args, **kwargs):
174
+        super().__init__(*args, **kwargs)
175
+        self.attacker_id = attacker_id
176
+        self.attacked_id = attacked_id
177
+
178
+
179
+class SearchGrass(SubjectBehaviour):
163
     """
180
     """
164
     Si une nourriture a une case de distance et cellule non rassasié, move dans sa direction.
181
     Si une nourriture a une case de distance et cellule non rassasié, move dans sa direction.
165
     """
182
     """
166
-    use = [EatableDirectProximityMechanism]
183
+    use = [GrassEatableDirectProximityMechanism]
167
 
184
 
168
     def run(self, data):
185
     def run(self, data):
169
         if self.subject.appetite < self.config.simulation.search_food_appetite_required:
186
         if self.subject.appetite < self.config.simulation.search_food_appetite_required:
170
             return False
187
             return False
171
 
188
 
172
-        if not data[EatableDirectProximityMechanism]:
189
+        if not data[GrassEatableDirectProximityMechanism]:
173
             return False
190
             return False
174
 
191
 
175
-        direction_degrees = [d['direction'] for d in data[EatableDirectProximityMechanism]]
192
+        direction_degrees = [d['direction'] for d in data[GrassEatableDirectProximityMechanism]]
176
         return get_direction_from_north_degree(choice(direction_degrees))
193
         return get_direction_from_north_degree(choice(direction_degrees))
177
 
194
 
178
     def action(self, data) -> [Event]:
195
     def action(self, data) -> [Event]:
184
         return [MoveTo(self.subject.id, position)]
201
         return [MoveTo(self.subject.id, position)]
185
 
202
 
186
 
203
 
187
-class Eat(SubjectBehaviour):
204
+class EatGrass(SubjectBehaviour):
188
     """
205
     """
189
     Prduit un immobilisme si sur une case de nourriture, dans le cas ou la cellule n'est as rassasié.
206
     Prduit un immobilisme si sur une case de nourriture, dans le cas ou la cellule n'est as rassasié.
190
     """
207
     """
269
         return choice(DIRECTION_SLIGHTLY[self.subject.previous_direction])
286
         return choice(DIRECTION_SLIGHTLY[self.subject.previous_direction])
270
 
287
 
271
 
288
 
289
+class Attack(SubjectBehaviour):
290
+    use = [PreyEatableDirectProximityMechanism]
291
+
292
+    def run(self, data):
293
+        if data[PreyEatableDirectProximityMechanism]:
294
+            return choice(data[PreyEatableDirectProximityMechanism])
295
+        return False
296
+
297
+    def action(self, data) -> [Event]:
298
+        # TODO: Dommages / mort
299
+        return [AttackEvent(attacker_id=self.subject.id, attacked_id=data['subject'].id)]
300
+
301
+
272
 class CellBehaviourSelector(SubjectBehaviourSelector):
302
 class CellBehaviourSelector(SubjectBehaviourSelector):
273
     # If behaviour in sublist, only one be kept in sublist
303
     # If behaviour in sublist, only one be kept in sublist
274
     behaviour_hierarchy = (  # TODO: refact it
304
     behaviour_hierarchy = (  # TODO: refact it
275
         (
305
         (
276
-            Eat,  # TODO: Introduce priority with appetite
277
-            SearchFood,  # TODO: Introduce priority with appetite
306
+            EatGrass,  # TODO: Introduce priority with appetite
307
+            SearchGrass,  # TODO: Introduce priority with appetite
278
             Explore,
308
             Explore,
279
         ),
309
         ),
280
     )
310
     )

+ 2 - 0
sandbox/engulf/const.py View File

2
 
2
 
3
 
3
 
4
 COLLECTION_CELL = 'CELL'
4
 COLLECTION_CELL = 'CELL'
5
+COLLECTION_PREY = 'PREY'
6
+COLLECTION_PREDATOR = 'PREDATOR'
5
 COLLECTION_ALIVE = 'ALIVE'
7
 COLLECTION_ALIVE = 'ALIVE'
6
 COLLECTION_EATABLE = 'EATABLE'
8
 COLLECTION_EATABLE = 'EATABLE'
7
 COLLECTION_GRASS = 'GRASS'
9
 COLLECTION_GRASS = 'GRASS'

+ 24 - 6
sandbox/engulf/gui.py View File

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, EatEvent
8
-from sandbox.engulf.subject import Cell, Grass
7
+from sandbox.engulf.behaviour import GrassGrownUp, GrassSpawn, MoveTo as MoveToEvent, EatEvent, AttackEvent
8
+from sandbox.engulf.subject import Cell, Grass, PreyCell, PredatorCell
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
11
 from synergine2_cocos2d.gui import MainLayer as BaseMainLayer
11
 from synergine2_cocos2d.gui import MainLayer as BaseMainLayer
28
     def fake_move_rotate_duration(self):
28
     def fake_move_rotate_duration(self):
29
         return self.move_duration / 3
29
         return self.move_duration / 3
30
 
30
 
31
-    def born(self, subject_id: int, grid_position):
32
-        cell = Sprite('resources/cell.png')
31
+    def born(self, subject_id: int, grid_position, cell_type):
32
+        png = 'resources/cell.png' if cell_type is PreyCell else 'resources/cellp.png'
33
+
34
+        cell = Sprite(png)
33
         cell.rotation = randint(0, 360)
35
         cell.rotation = randint(0, 360)
34
         self.grid_manager.scale_sprite(cell)
36
         self.grid_manager.scale_sprite(cell)
35
         self.grid_manager.position_sprite(cell, grid_position)
37
         self.grid_manager.position_sprite(cell, grid_position)
49
         cell.do(move_action)
51
         cell.do(move_action)
50
         cell.do(fake_rotate)
52
         cell.do(fake_rotate)
51
 
53
 
54
+    def attack(self, attacker_id: int, attacked_id: int):
55
+        attacker = self.cell_ids[attacker_id]
56
+        attacker.do(ScaleBy(1.7, duration=0.25))
57
+
52
 
58
 
53
 class GrassLayer(GridLayerMixin, BaseMainLayer):
59
 class GrassLayer(GridLayerMixin, BaseMainLayer):
54
     def __init__(self, game: 'Game', *args, **kwargs):
60
     def __init__(self, game: 'Game', *args, **kwargs):
105
             EatEvent,
111
             EatEvent,
106
             self.on_eat,
112
             self.on_eat,
107
         )
113
         )
114
+        self.terminal.register_event_handler(
115
+            AttackEvent,
116
+            self.on_attack,
117
+        )
108
 
118
 
109
     def get_main_scene(self):
119
     def get_main_scene(self):
110
         return self.main_scene
120
         return self.main_scene
112
     def before_received(self, package: TerminalPackage):
122
     def before_received(self, package: TerminalPackage):
113
         if package.subjects:  # It's thirst package
123
         if package.subjects:  # It's thirst package
114
             for subject in package.subjects:
124
             for subject in package.subjects:
115
-                if isinstance(subject, Cell):
116
-                    self.main_layer.cells.born(subject.id, subject.position)
125
+                if isinstance(subject, PreyCell):
126
+                    self.main_layer.cells.born(subject.id, subject.position, PreyCell)
127
+                if isinstance(subject, PredatorCell):
128
+                    self.main_layer.cells.born(subject.id, subject.position, PredatorCell)
117
                 if isinstance(subject, Grass):
129
                 if isinstance(subject, Grass):
118
                     self.main_layer.grasses.born(
130
                     self.main_layer.grasses.born(
119
                         subject.id,
131
                         subject.id,
145
             event.eaten_id,
157
             event.eaten_id,
146
             event.eaten_new_density,
158
             event.eaten_new_density,
147
         )
159
         )
160
+
161
+    def on_attack(self, event: AttackEvent):
162
+        self.main_layer.cells.attack(
163
+            event.attacker_id,
164
+            event.attacked_id,
165
+        )

BIN
sandbox/engulf/resources/cellp.png View File


+ 25 - 12
sandbox/engulf/run.py View File

21
 import logging
21
 import logging
22
 import os
22
 import os
23
 import sys
23
 import sys
24
+import typing
24
 
25
 
25
 synergine2_ath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../'))
26
 synergine2_ath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../'))
26
 sys.path.append(synergine2_ath)
27
 sys.path.append(synergine2_ath)
27
 
28
 
28
 from random import randint, seed
29
 from random import randint, seed
29
-from sandbox.engulf.behaviour import GrassGrownUp, GrassSpawn, MoveTo, EatEvent
30
+from sandbox.engulf.behaviour import GrassGrownUp, GrassSpawn, MoveTo, EatEvent, AttackEvent
30
 
31
 
31
 from synergine2.config import Config
32
 from synergine2.config import Config
32
 from synergine2.log import get_default_logger
33
 from synergine2.log import get_default_logger
33
-from sandbox.engulf.subject import Cell, Grass
34
+from sandbox.engulf.subject import Cell, Grass, PreyCell, PredatorCell
34
 from synergine2.core import Core
35
 from synergine2.core import Core
35
 from synergine2.cycle import CycleManager
36
 from synergine2.cycle import CycleManager
36
 from synergine2.terminals import TerminalManager, Terminal, TerminalPackage
37
 from synergine2.terminals import TerminalManager, Terminal, TerminalPackage
44
         GrassSpawn,
45
         GrassSpawn,
45
         MoveTo,
46
         MoveTo,
46
         EatEvent,
47
         EatEvent,
48
+        AttackEvent,
47
     ]
49
     ]
48
 
50
 
49
     def __init__(self, *args, **kwargs):
51
     def __init__(self, *args, **kwargs):
68
     count: int,
70
     count: int,
69
     start_position: tuple,
71
     start_position: tuple,
70
     end_position: tuple,
72
     end_position: tuple,
73
+    cell_class: typing.Type[PreyCell],
71
 ) -> None:
74
 ) -> None:
72
     cells = []
75
     cells = []
73
 
76
 
78
             0,
81
             0,
79
         )
82
         )
80
         if position not in subjects.cell_xyz:
83
         if position not in subjects.cell_xyz:
81
-            cell = Cell(
84
+            cell = cell_class(
82
                 config,
85
                 config,
83
                 simulation=subjects.simulation,
86
                 simulation=subjects.simulation,
84
                 position=position,
87
                 position=position,
116
     for grass in grasses:
119
     for grass in grasses:
117
         for around in get_around_positions_of(grass.position, distance=density):
120
         for around in get_around_positions_of(grass.position, distance=density):
118
             if around not in subjects.grass_xyz:
121
             if around not in subjects.grass_xyz:
119
-                new_grass = Grass(
120
-                    config,
121
-                    simulation=subjects.simulation,
122
-                    position=around,
123
-                )
124
-                distance = get_distance_between_points(around, grass.position)
125
-                new_grass.density = 100 - round((distance * 100) / 7)
126
-                subjects.append(new_grass)
122
+                if subjects.simulation.is_possible_position(around):
123
+                    new_grass = Grass(
124
+                        config,
125
+                        simulation=subjects.simulation,
126
+                        position=around,
127
+                    )
128
+                    distance = get_distance_between_points(around, grass.position)
129
+                    new_grass.density = 100 - round((distance * 100) / 7)
130
+                    subjects.append(new_grass)
127
 
131
 
128
 
132
 
129
 def main():
133
 def main():
130
-    seed(0)
134
+    seed(42)
131
 
135
 
132
     config = Config()
136
     config = Config()
133
     config.load_files(['sandbox/engulf/config.yaml'])
137
     config.load_files(['sandbox/engulf/config.yaml'])
141
         30,
145
         30,
142
         (-34, -34, 0),
146
         (-34, -34, 0),
143
         (34, 34, 0),
147
         (34, 34, 0),
148
+        cell_class=PreyCell,
149
+    )
150
+    fill_with_random_cells(
151
+        config,
152
+        subjects,
153
+        30,
154
+        (-34, -34, 0),
155
+        (34, 34, 0),
156
+        cell_class=PredatorCell,
144
     )
157
     )
145
     fill_with_random_grass(
158
     fill_with_random_grass(
146
         config,
159
         config,

+ 13 - 4
sandbox/engulf/subject.py View File

1
 # coding: utf-8
1
 # coding: utf-8
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
2
+from sandbox.engulf.behaviour import GrowUp, SearchGrass, EatGrass, Explore, CellBehaviourSelector, Hungry, Attack
3
+from sandbox.engulf.const import COLLECTION_CELL, COLLECTION_ALIVE, COLLECTION_EATABLE, COLLECTION_GRASS, \
4
+    COLLECTION_PREY, COLLECTION_PREDATOR
4
 from synergine2.simulation import Subject
5
 from synergine2.simulation import Subject
5
 from synergine2.xyz import XYZSubjectMixin
6
 from synergine2.xyz import XYZSubjectMixin
6
 
7
 
13
     ]
14
     ]
14
     # TODO: Mettre en place la "selection/choix": car il y a deux move possible chaque cycle ci-dessous.
15
     # TODO: Mettre en place la "selection/choix": car il y a deux move possible chaque cycle ci-dessous.
15
     behaviours_classes = [
16
     behaviours_classes = [
16
-        SearchFood,
17
-        Eat,
18
         Explore,
17
         Explore,
19
         Hungry,
18
         Hungry,
20
     ]
19
     ]
38
             self._appetite = value
37
             self._appetite = value
39
 
38
 
40
 
39
 
40
+class PreyCell(Cell):
41
+    collections = Cell.collections[:] + [COLLECTION_PREY]
42
+    behaviours_classes = Cell.behaviours_classes[:] + [SearchGrass, EatGrass]
43
+
44
+
45
+class PredatorCell(Cell):
46
+    collections = Cell.collections[:] + [COLLECTION_PREDATOR]
47
+    behaviours_classes = Cell.behaviours_classes[:] + [Attack]
48
+
49
+
41
 class Grass(XYZSubjectMixin, Subject):
50
 class Grass(XYZSubjectMixin, Subject):
42
     collections = [
51
     collections = [
43
         COLLECTION_EATABLE,
52
         COLLECTION_EATABLE,