home · contact · privacy
Focus AI on collecting and eating food.
[plomrogue2-experiments] / new / plomrogue / things.py
1 from plomrogue.errors import GameError
2
3
4
5 class ThingBase:
6     type_ = '?'
7
8     def __init__(self, world, id_=None, position=(0,0)):
9         self.world = world
10         self.position = position
11         if id_ is None:
12             self.id_ = self.world.new_thing_id()
13         else:
14             self.id_ = id_
15
16
17
18 class Thing(ThingBase):
19     blocking = False
20     in_inventory = False
21
22     def __init__(self, *args, **kwargs):
23         super().__init__(*args, **kwargs)
24         self.inventory = []
25
26     def proceed(self):
27         pass
28
29
30
31 class ThingItem(Thing):
32     pass
33
34
35
36 class ThingFood(ThingItem):
37     type_ = 'food'
38
39
40
41 class ThingAnimate(Thing):
42     blocking = True
43
44     def __init__(self, *args, **kwargs):
45         super().__init__(*args, **kwargs)
46         self.set_task('WAIT')
47         self._last_task_result = None
48         self._stencil = None
49
50     def move_on_dijkstra_map(self, targets):
51         dijkstra_map = type(self.world.map_)(self.world.map_.size)
52         n_max = 256
53         dijkstra_map.terrain = [n_max for i in range(dijkstra_map.size_i)]
54         for target in targets:
55             dijkstra_map[target] = 0
56         shrunk = True
57         visible_map = self.get_visible_map()
58         while shrunk:
59             shrunk = False
60             for pos in dijkstra_map:
61                 if visible_map[pos] != '.':
62                     continue
63                 neighbors = dijkstra_map.get_neighbors(tuple(pos))
64                 for direction in neighbors:
65                     yx = neighbors[direction]
66                     if yx is not None and dijkstra_map[yx] < dijkstra_map[pos] - 1:
67                         dijkstra_map[pos] = dijkstra_map[yx] + 1
68                         shrunk = True
69         neighbors = dijkstra_map.get_neighbors(tuple(self.position))
70         n = n_max
71         target_direction = None
72         for direction in sorted(neighbors.keys()):
73             yx = neighbors[direction]
74             if yx is not None:
75                 n_new = dijkstra_map[yx]
76                 if n_new < n:
77                     n = n_new
78                     target_direction = direction
79         return target_direction
80
81     def hunt_player(self):
82         visible_things = self.get_visible_things()
83         target = None
84         for t in visible_things:
85             if t.type_ == 'human':
86                 target = t.position
87                 break
88         if target is not None:
89             try:
90                 target_dir = self.move_on_dijkstra_map([target])
91                 if target_dir is not None:
92                     self.set_task('MOVE', (target_dir,))
93                     return True
94             except GameError:
95                 pass
96         return False
97
98     def hunt_food_satisfaction(self):
99         for id_ in self.inventory:
100             t = self.world.get_thing(id_)
101             if t.type_ == 'food':
102                 self.set_task('EAT', (id_,))
103                 return True
104         for id_ in self.get_pickable_items():
105             t = self.world.get_thing(id_)
106             if t.type_ == 'food':
107                 self.set_task('PICKUP', (id_,))
108                 return True
109         visible_things = self.get_visible_things()
110         food_targets = []
111         for t in visible_things:
112             if t.type_ == 'food':
113                 food_targets += [t.position]
114         target_dir = self.move_on_dijkstra_map(food_targets)
115         if target_dir:
116             try:
117                 self.set_task('MOVE', (target_dir,))
118                 return True
119             except GameError:
120                 pass
121         return False
122
123     def decide_task(self):
124         #if not self.hunt_player():
125         if not self.hunt_food_satisfaction():
126             self.set_task('WAIT')
127
128     def set_task(self, task_name, args=()):
129         task_class = self.world.game.tasks[task_name]
130         self.task = task_class(self, args)
131         self.task.check()  # will throw GameError if necessary
132
133     def proceed(self, is_AI=True):
134         """Further the thing in its tasks, decrease its health.
135
136         First, ensures an empty map, decrements .health and kills
137         thing if crossing zero (removes from self.world.things for AI
138         thing, or unsets self.world.player_is_alive for player thing);
139         then checks that self.task is still possible and aborts if
140         otherwise (for AI things, decides a new task).
141
142         Then decrements .task.todo; if it thus falls to <= 0, enacts
143         method whose name is 'task_' + self.task.name and sets .task =
144         None. If is_AI, calls .decide_task to decide a self.task.
145
146         """
147         self._stencil = None
148         self.health -= 1
149         if self.health <= 0:
150             if self is self.world.player:
151                 self.world.player_is_alive = False
152             else:
153                 del self.world.things[self.world.things.index(self)]
154             return
155         try:
156             self.task.check()
157         except GameError as e:
158             self.task = None
159             self._last_task_result = e
160             if is_AI:
161                 try:
162                     self.decide_task()
163                 except GameError:
164                     self.set_task('WAIT')
165             return
166         self.task.todo -= 1
167         if self.task.todo <= 0:
168             self._last_task_result = self.task.do()
169             self.task = None
170         if is_AI and self.task is None:
171             try:
172                 self.decide_task()
173             except GameError:
174                 self.set_task('WAIT')
175
176     def get_stencil(self):
177         if self._stencil is not None:
178             return self._stencil
179         self._stencil = self.world.map_.get_fov_map(self.position)
180         return self._stencil
181
182     def get_visible_map(self):
183         stencil = self.get_stencil()
184         m = self.world.map_.new_from_shape(' ')
185         for pos in m:
186             if stencil[pos] == '.':
187                 m[pos] = self.world.map_[pos]
188         return m
189
190     def get_visible_things(self):
191         stencil = self.get_stencil()
192         visible_things = []
193         for thing in self.world.things:
194             if (not thing.in_inventory) and stencil[thing.position] == '.':
195                 visible_things += [thing]
196         return visible_things
197
198     def get_pickable_items(self):
199         pickable_ids = []
200         for t in [t for t in self.get_visible_things() if
201                   isinstance(t, ThingItem) and
202                   (t.position == self.position or
203                    t.position in
204                    self.world.map_.get_neighbors(self.position).values())]:
205             pickable_ids += [t.id_]
206         return pickable_ids
207
208
209
210 class ThingHuman(ThingAnimate):
211     type_ = 'human'
212     health = 100
213
214
215
216 class ThingMonster(ThingAnimate):
217     type_ = 'monster'
218     health = 50