소스 검색

engulf: prey and predator

Bastien Sevajol 7 년 전
부모
커밋
f98fbd3f78
6개의 변경된 파일106개의 추가작업 그리고 34개의 파일을 삭제
  1. 42 12
      sandbox/engulf/behaviour.py
  2. 2 0
      sandbox/engulf/const.py
  3. 24 6
      sandbox/engulf/gui.py
  4. BIN
      sandbox/engulf/resources/cellp.png
  5. 25 12
      sandbox/engulf/run.py
  6. 13 4
      sandbox/engulf/subject.py

+ 42 - 12
sandbox/engulf/behaviour.py 파일 보기

@@ -2,18 +2,18 @@
2 2
 import typing
3 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 6
 from sandbox.engulf.exceptions import NotFoundWhereToGo
7 7
 from synergine2.simulation import SubjectBehaviour, SimulationMechanism, SimulationBehaviour, SubjectBehaviourSelector
8 8
 from synergine2.simulation import Event
9 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 13
 # Import for typing hint
15 14
 if False:
16 15
     from sandbox.engulf.subject import Grass
16
+    from sandbox.engulf.subject import PreyCell
17 17
 
18 18
 
19 19
 class GrassGrownUp(Event):
@@ -129,7 +129,7 @@ class GrassSpawnBehaviour(SimulationBehaviour):
129 129
         return events
130 130
 
131 131
 
132
-class EatableDirectProximityMechanism(ProximitySubjectMechanism):
132
+class GrassEatableDirectProximityMechanism(ProximitySubjectMechanism):
133 133
     distance = 1.41  # distance when on angle
134 134
     feel_collections = [COLLECTION_GRASS]
135 135
 
@@ -137,6 +137,16 @@ class EatableDirectProximityMechanism(ProximitySubjectMechanism):
137 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 150
 class MoveTo(Event):
141 151
     def __init__(self, subject_id: int, position: tuple, *args, **kwargs):
142 152
         super().__init__(*args, **kwargs)
@@ -159,20 +169,27 @@ class EatEvent(Event):
159 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 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 185
     def run(self, data):
169 186
         if self.subject.appetite < self.config.simulation.search_food_appetite_required:
170 187
             return False
171 188
 
172
-        if not data[EatableDirectProximityMechanism]:
189
+        if not data[GrassEatableDirectProximityMechanism]:
173 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 193
         return get_direction_from_north_degree(choice(direction_degrees))
177 194
 
178 195
     def action(self, data) -> [Event]:
@@ -184,7 +201,7 @@ class SearchFood(SubjectBehaviour):
184 201
         return [MoveTo(self.subject.id, position)]
185 202
 
186 203
 
187
-class Eat(SubjectBehaviour):
204
+class EatGrass(SubjectBehaviour):
188 205
     """
189 206
     Prduit un immobilisme si sur une case de nourriture, dans le cas ou la cellule n'est as rassasié.
190 207
     """
@@ -269,12 +286,25 @@ class Hungry(SubjectBehaviour):
269 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 302
 class CellBehaviourSelector(SubjectBehaviourSelector):
273 303
     # If behaviour in sublist, only one be kept in sublist
274 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 308
             Explore,
279 309
         ),
280 310
     )

+ 2 - 0
sandbox/engulf/const.py 파일 보기

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

+ 24 - 6
sandbox/engulf/gui.py 파일 보기

@@ -4,8 +4,8 @@ from random import randint
4 4
 import cocos
5 5
 from cocos.actions import MoveTo, Repeat, ScaleBy, Reverse, RotateTo
6 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 9
 from synergine2.terminals import TerminalPackage
10 10
 from synergine2_cocos2d.gui import Gui, GridLayerMixin
11 11
 from synergine2_cocos2d.gui import MainLayer as BaseMainLayer
@@ -28,8 +28,10 @@ class CellsLayer(GridLayerMixin, BaseMainLayer):
28 28
     def fake_move_rotate_duration(self):
29 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 35
         cell.rotation = randint(0, 360)
34 36
         self.grid_manager.scale_sprite(cell)
35 37
         self.grid_manager.position_sprite(cell, grid_position)
@@ -49,6 +51,10 @@ class CellsLayer(GridLayerMixin, BaseMainLayer):
49 51
         cell.do(move_action)
50 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 59
 class GrassLayer(GridLayerMixin, BaseMainLayer):
54 60
     def __init__(self, game: 'Game', *args, **kwargs):
@@ -105,6 +111,10 @@ class Game(Gui):
105 111
             EatEvent,
106 112
             self.on_eat,
107 113
         )
114
+        self.terminal.register_event_handler(
115
+            AttackEvent,
116
+            self.on_attack,
117
+        )
108 118
 
109 119
     def get_main_scene(self):
110 120
         return self.main_scene
@@ -112,8 +122,10 @@ class Game(Gui):
112 122
     def before_received(self, package: TerminalPackage):
113 123
         if package.subjects:  # It's thirst package
114 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 129
                 if isinstance(subject, Grass):
118 130
                     self.main_layer.grasses.born(
119 131
                         subject.id,
@@ -145,3 +157,9 @@ class Game(Gui):
145 157
             event.eaten_id,
146 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 파일 보기


+ 25 - 12
sandbox/engulf/run.py 파일 보기

@@ -21,16 +21,17 @@ Engulf is simulation containing:
21 21
 import logging
22 22
 import os
23 23
 import sys
24
+import typing
24 25
 
25 26
 synergine2_ath = os.path.abspath(os.path.join(os.path.dirname(os.path.abspath(__file__)), '../../'))
26 27
 sys.path.append(synergine2_ath)
27 28
 
28 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 32
 from synergine2.config import Config
32 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 35
 from synergine2.core import Core
35 36
 from synergine2.cycle import CycleManager
36 37
 from synergine2.terminals import TerminalManager, Terminal, TerminalPackage
@@ -44,6 +45,7 @@ class GameTerminal(Terminal):
44 45
         GrassSpawn,
45 46
         MoveTo,
46 47
         EatEvent,
48
+        AttackEvent,
47 49
     ]
48 50
 
49 51
     def __init__(self, *args, **kwargs):
@@ -68,6 +70,7 @@ def fill_with_random_cells(
68 70
     count: int,
69 71
     start_position: tuple,
70 72
     end_position: tuple,
73
+    cell_class: typing.Type[PreyCell],
71 74
 ) -> None:
72 75
     cells = []
73 76
 
@@ -78,7 +81,7 @@ def fill_with_random_cells(
78 81
             0,
79 82
         )
80 83
         if position not in subjects.cell_xyz:
81
-            cell = Cell(
84
+            cell = cell_class(
82 85
                 config,
83 86
                 simulation=subjects.simulation,
84 87
                 position=position,
@@ -116,18 +119,19 @@ def fill_with_random_grass(
116 119
     for grass in grasses:
117 120
         for around in get_around_positions_of(grass.position, distance=density):
118 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 133
 def main():
130
-    seed(0)
134
+    seed(42)
131 135
 
132 136
     config = Config()
133 137
     config.load_files(['sandbox/engulf/config.yaml'])
@@ -141,6 +145,15 @@ def main():
141 145
         30,
142 146
         (-34, -34, 0),
143 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 158
     fill_with_random_grass(
146 159
         config,

+ 13 - 4
sandbox/engulf/subject.py 파일 보기

@@ -1,6 +1,7 @@
1 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 5
 from synergine2.simulation import Subject
5 6
 from synergine2.xyz import XYZSubjectMixin
6 7
 
@@ -13,8 +14,6 @@ class Cell(XYZSubjectMixin, Subject):
13 14
     ]
14 15
     # TODO: Mettre en place la "selection/choix": car il y a deux move possible chaque cycle ci-dessous.
15 16
     behaviours_classes = [
16
-        SearchFood,
17
-        Eat,
18 17
         Explore,
19 18
         Hungry,
20 19
     ]
@@ -38,6 +37,16 @@ class Cell(XYZSubjectMixin, Subject):
38 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 50
 class Grass(XYZSubjectMixin, Subject):
42 51
     collections = [
43 52
         COLLECTION_EATABLE,