home · contact · privacy
Focus AI on collecting and eating food.
[plomrogue2-experiments] / new / plomrogue / things.py
index 2decc6709f747113e888edc9327f406c8e43739c..c9e95d3cbf24dc70bf8ab05bb652435071dc19b2 100644 (file)
@@ -5,14 +5,41 @@ from plomrogue.errors import GameError
 class ThingBase:
     type_ = '?'
 
-    def __init__(self, world, id_, position=[0,0]):
+    def __init__(self, world, id_=None, position=(0,0)):
         self.world = world
-        self.id_ = id_
         self.position = position
+        if id_ is None:
+            self.id_ = self.world.new_thing_id()
+        else:
+            self.id_ = id_
 
 
 
 class Thing(ThingBase):
+    blocking = False
+    in_inventory = False
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.inventory = []
+
+    def proceed(self):
+        pass
+
+
+
+class ThingItem(Thing):
+    pass
+
+
+
+class ThingFood(ThingItem):
+    type_ = 'food'
+
+
+
+class ThingAnimate(Thing):
+    blocking = True
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
@@ -20,11 +47,12 @@ class Thing(ThingBase):
         self._last_task_result = None
         self._stencil = None
 
-    def move_towards_target(self, target):
+    def move_on_dijkstra_map(self, targets):
         dijkstra_map = type(self.world.map_)(self.world.map_.size)
         n_max = 256
         dijkstra_map.terrain = [n_max for i in range(dijkstra_map.size_i)]
-        dijkstra_map[target] = 0
+        for target in targets:
+            dijkstra_map[target] = 0
         shrunk = True
         visible_map = self.get_visible_map()
         while shrunk:
@@ -41,17 +69,16 @@ class Thing(ThingBase):
         neighbors = dijkstra_map.get_neighbors(tuple(self.position))
         n = n_max
         target_direction = None
-        for direction in neighbors:
+        for direction in sorted(neighbors.keys()):
             yx = neighbors[direction]
             if yx is not None:
                 n_new = dijkstra_map[yx]
                 if n_new < n:
                     n = n_new
                     target_direction = direction
-        if target_direction:
-            self.set_task('MOVE', (target_direction,))
+        return target_direction
 
-    def decide_task(self):
+    def hunt_player(self):
         visible_things = self.get_visible_things()
         target = None
         for t in visible_things:
@@ -60,11 +87,43 @@ class Thing(ThingBase):
                 break
         if target is not None:
             try:
-                self.move_towards_target(target)
-                return
+                target_dir = self.move_on_dijkstra_map([target])
+                if target_dir is not None:
+                    self.set_task('MOVE', (target_dir,))
+                    return True
             except GameError:
                 pass
-        self.set_task('WAIT')
+        return False
+
+    def hunt_food_satisfaction(self):
+        for id_ in self.inventory:
+            t = self.world.get_thing(id_)
+            if t.type_ == 'food':
+                self.set_task('EAT', (id_,))
+                return True
+        for id_ in self.get_pickable_items():
+            t = self.world.get_thing(id_)
+            if t.type_ == 'food':
+                self.set_task('PICKUP', (id_,))
+                return True
+        visible_things = self.get_visible_things()
+        food_targets = []
+        for t in visible_things:
+            if t.type_ == 'food':
+                food_targets += [t.position]
+        target_dir = self.move_on_dijkstra_map(food_targets)
+        if target_dir:
+            try:
+                self.set_task('MOVE', (target_dir,))
+                return True
+            except GameError:
+                pass
+        return False
+
+    def decide_task(self):
+        #if not self.hunt_player():
+        if not self.hunt_food_satisfaction():
+            self.set_task('WAIT')
 
     def set_task(self, task_name, args=()):
         task_class = self.world.game.tasks[task_name]
@@ -72,18 +131,27 @@ class Thing(ThingBase):
         self.task.check()  # will throw GameError if necessary
 
     def proceed(self, is_AI=True):
-        """Further the thing in its tasks.
-
-        Decrements .task.todo; if it thus falls to <= 0, enacts method
-        whose name is 'task_' + self.task.name and sets .task =
-        None. If is_AI, calls .decide_task to decide a self.task.
+        """Further the thing in its tasks, decrease its health.
 
-        Before doing anything, ensures an empty map visibility stencil
-        and checks that task is still possible, and aborts it
+        First, ensures an empty map, decrements .health and kills
+        thing if crossing zero (removes from self.world.things for AI
+        thing, or unsets self.world.player_is_alive for player thing);
+        then checks that self.task is still possible and aborts if
         otherwise (for AI things, decides a new task).
 
+        Then decrements .task.todo; if it thus falls to <= 0, enacts
+        method whose name is 'task_' + self.task.name and sets .task =
+        None. If is_AI, calls .decide_task to decide a self.task.
+
         """
         self._stencil = None
+        self.health -= 1
+        if self.health <= 0:
+            if self is self.world.player:
+                self.world.player_is_alive = False
+            else:
+                del self.world.things[self.world.things.index(self)]
+            return
         try:
             self.task.check()
         except GameError as e:
@@ -123,16 +191,28 @@ class Thing(ThingBase):
         stencil = self.get_stencil()
         visible_things = []
         for thing in self.world.things:
-            if stencil[thing.position] == '.':
+            if (not thing.in_inventory) and stencil[thing.position] == '.':
                 visible_things += [thing]
         return visible_things
 
+    def get_pickable_items(self):
+        pickable_ids = []
+        for t in [t for t in self.get_visible_things() if
+                  isinstance(t, ThingItem) and
+                  (t.position == self.position or
+                   t.position in
+                   self.world.map_.get_neighbors(self.position).values())]:
+            pickable_ids += [t.id_]
+        return pickable_ids
+
 
 
-class ThingHuman(Thing):
+class ThingHuman(ThingAnimate):
     type_ = 'human'
+    health = 100
 
 
 
-class ThingMonster(Thing):
+class ThingMonster(ThingAnimate):
     type_ = 'monster'
+    health = 50