|
@@ -2,7 +2,7 @@
|
2
|
2
|
import typing
|
3
|
3
|
from random import choice
|
4
|
4
|
|
5
|
|
-from sandbox.engulf.const import COLLECTION_GRASS, COLLECTION_CELL
|
|
5
|
+from sandbox.engulf.const import COLLECTION_GRASS, COLLECTION_CELL, COLLECTION_ALIVE, COLLECTION_PREY
|
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
|
|
@@ -169,6 +169,12 @@ class EatEvent(Event):
|
169
|
169
|
self.eaten_new_density = eaten_new_density
|
170
|
170
|
|
171
|
171
|
|
|
172
|
+class EatenEvent(Event):
|
|
173
|
+ def __init__(self, eaten_id: int, *args, **kwargs):
|
|
174
|
+ super().__init__(*args, **kwargs)
|
|
175
|
+ self.eaten_id = eaten_id
|
|
176
|
+
|
|
177
|
+
|
172
|
178
|
class AttackEvent(Event):
|
173
|
179
|
def __init__(self, attacker_id: int, attacked_id: int, *args, **kwargs):
|
174
|
180
|
super().__init__(*args, **kwargs)
|
|
@@ -176,20 +182,20 @@ class AttackEvent(Event):
|
176
|
182
|
self.attacked_id = attacked_id
|
177
|
183
|
|
178
|
184
|
|
179
|
|
-class SearchGrass(SubjectBehaviour):
|
180
|
|
- """
|
181
|
|
- Si une nourriture a une case de distance et cellule non rassasié, move dans sa direction.
|
182
|
|
- """
|
183
|
|
- use = [GrassEatableDirectProximityMechanism]
|
|
185
|
+class SearchFood(SubjectBehaviour):
|
|
186
|
+ mechanism_data_class = NotImplemented
|
|
187
|
+
|
|
188
|
+ def get_required_appetite(self) -> float:
|
|
189
|
+ raise NotImplementedError()
|
184
|
190
|
|
185
|
191
|
def run(self, data):
|
186
|
|
- if self.subject.appetite < self.config.simulation.search_food_appetite_required:
|
|
192
|
+ if self.subject.appetite < self.get_required_appetite():
|
187
|
193
|
return False
|
188
|
194
|
|
189
|
|
- if not data[GrassEatableDirectProximityMechanism]:
|
|
195
|
+ if not data[self.mechanism_data_class]:
|
190
|
196
|
return False
|
191
|
197
|
|
192
|
|
- direction_degrees = [d['direction'] for d in data[GrassEatableDirectProximityMechanism]]
|
|
198
|
+ direction_degrees = [d['direction'] for d in data[self.mechanism_data_class]]
|
193
|
199
|
return get_direction_from_north_degree(choice(direction_degrees))
|
194
|
200
|
|
195
|
201
|
def action(self, data) -> [Event]:
|
|
@@ -201,10 +207,29 @@ class SearchGrass(SubjectBehaviour):
|
201
|
207
|
return [MoveTo(self.subject.id, position)]
|
202
|
208
|
|
203
|
209
|
|
204
|
|
-class EatGrass(SubjectBehaviour):
|
|
210
|
+class SearchGrass(SearchFood):
|
|
211
|
+ """
|
|
212
|
+ Si une nourriture a une case de distance et cellule non rassasié, move dans sa direction.
|
205
|
213
|
"""
|
206
|
|
- Prduit un immobilisme si sur une case de nourriture, dans le cas ou la cellule n'est as rassasié.
|
|
214
|
+ use = [GrassEatableDirectProximityMechanism]
|
|
215
|
+ mechanism_data_class = use[0]
|
|
216
|
+
|
|
217
|
+ def get_required_appetite(self) -> float:
|
|
218
|
+ return self.config.simulation.search_food_appetite_required
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+class SearchPrey(SearchFood):
|
|
222
|
+ """
|
|
223
|
+ Si une nourriture a une case de distance et cellule non rassasié, move dans sa direction.
|
207
|
224
|
"""
|
|
225
|
+ use = [PreyEatableDirectProximityMechanism]
|
|
226
|
+ mechanism_data_class = use[0]
|
|
227
|
+
|
|
228
|
+ def get_required_appetite(self) -> float:
|
|
229
|
+ return self.config.simulation.search_and_attack_prey_apetite_required
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+class EatGrass(SubjectBehaviour):
|
208
|
233
|
def run(self, data):
|
209
|
234
|
if self.subject.appetite < self.config.simulation.eat_grass_required_density:
|
210
|
235
|
return False
|
|
@@ -231,6 +256,32 @@ class EatGrass(SubjectBehaviour):
|
231
|
256
|
)]
|
232
|
257
|
|
233
|
258
|
|
|
259
|
+class EatPrey(SubjectBehaviour):
|
|
260
|
+ def run(self, data):
|
|
261
|
+ if self.subject.appetite < self.config.simulation.search_and_attack_prey_apetite_required:
|
|
262
|
+ return False
|
|
263
|
+
|
|
264
|
+ for prey in self.simulation.collections.get(COLLECTION_PREY, []):
|
|
265
|
+ # TODO: Use simulation/xyz pre calculated indexes
|
|
266
|
+ if prey.position == self.subject.position:
|
|
267
|
+ return prey.id
|
|
268
|
+
|
|
269
|
+ def action(self, data) -> [Event]:
|
|
270
|
+ subject_id = data
|
|
271
|
+
|
|
272
|
+ # Cell already eaten ?
|
|
273
|
+ if subject_id not in self.simulation.subjects.index:
|
|
274
|
+ return []
|
|
275
|
+
|
|
276
|
+ prey = self.simulation.subjects.index[subject_id]
|
|
277
|
+ self.simulation.subjects.remove(prey)
|
|
278
|
+ self.subject.appetite -= self.config.simulation.eat_prey_required_density
|
|
279
|
+
|
|
280
|
+ return [EatenEvent(
|
|
281
|
+ eaten_id=prey.id,
|
|
282
|
+ )]
|
|
283
|
+
|
|
284
|
+
|
234
|
285
|
class Explore(SubjectBehaviour):
|
235
|
286
|
"""
|
236
|
287
|
Produit un mouvement au hasard (ou un immobilisme)
|
|
@@ -238,7 +289,11 @@ class Explore(SubjectBehaviour):
|
238
|
289
|
use = []
|
239
|
290
|
|
240
|
291
|
def run(self, data):
|
241
|
|
- return True # for now, want move every time
|
|
292
|
+ # TODO: Il faut pouvoir dire tel behaviour concerne que tel collections
|
|
293
|
+ if COLLECTION_ALIVE not in self.subject.collections:
|
|
294
|
+ return False
|
|
295
|
+
|
|
296
|
+ return True
|
242
|
297
|
|
243
|
298
|
def action(self, data) -> [Event]:
|
244
|
299
|
try:
|
|
@@ -290,12 +345,30 @@ class Attack(SubjectBehaviour):
|
290
|
345
|
use = [PreyEatableDirectProximityMechanism]
|
291
|
346
|
|
292
|
347
|
def run(self, data):
|
293
|
|
- if data[PreyEatableDirectProximityMechanism]:
|
294
|
|
- return choice(data[PreyEatableDirectProximityMechanism])
|
|
348
|
+ if self.subject.appetite < self.config.simulation.search_and_attack_prey_apetite_required:
|
|
349
|
+ return False
|
|
350
|
+
|
|
351
|
+ eatable_datas = data[PreyEatableDirectProximityMechanism]
|
|
352
|
+ attackable_datas = []
|
|
353
|
+
|
|
354
|
+ for eatable_data in eatable_datas:
|
|
355
|
+ if COLLECTION_ALIVE in eatable_data['subject'].collections:
|
|
356
|
+ attackable_datas.append(eatable_data)
|
|
357
|
+
|
|
358
|
+ if attackable_datas:
|
|
359
|
+ return choice(attackable_datas)
|
295
|
360
|
return False
|
296
|
361
|
|
297
|
362
|
def action(self, data) -> [Event]:
|
298
|
|
- # TODO: Dommages / mort
|
|
363
|
+ attacked = self.simulation.subjects.index[data['subject'].id]
|
|
364
|
+
|
|
365
|
+ try:
|
|
366
|
+ # TODO il faut automatiser/Refactoriser le fait de retirer/ajouter un collection
|
|
367
|
+ self.simulation.collections[COLLECTION_ALIVE].remove(attacked)
|
|
368
|
+ attacked.collections.remove(COLLECTION_ALIVE)
|
|
369
|
+ except ValueError:
|
|
370
|
+ pass # On considere qu'il a ete tué par une autre, TODO: en être sur ?
|
|
371
|
+
|
299
|
372
|
return [AttackEvent(attacker_id=self.subject.id, attacked_id=data['subject'].id)]
|
300
|
373
|
|
301
|
374
|
|
|
@@ -303,7 +376,10 @@ class CellBehaviourSelector(SubjectBehaviourSelector):
|
303
|
376
|
# If behaviour in sublist, only one be kept in sublist
|
304
|
377
|
behaviour_hierarchy = ( # TODO: refact it
|
305
|
378
|
(
|
|
379
|
+ EatPrey,
|
306
|
380
|
EatGrass, # TODO: Introduce priority with appetite
|
|
381
|
+ Attack,
|
|
382
|
+ SearchPrey,
|
307
|
383
|
SearchGrass, # TODO: Introduce priority with appetite
|
308
|
384
|
Explore,
|
309
|
385
|
),
|