1 /* Functions common to all AIs.
2    Copyright (C) 2004 Eric A. McDonald
3 
4 Xconq is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.  See the file COPYING.  */
8 
9 /* This file contains code used by all types of AIs and intelligent
10    UIs. While AI code is officially outside the kernel, the functions
11    here we need some definitions from kernel.h and so must include it. */
12 
13 /* The functions contained herein should contain simple calculations
14    which produce strictly objective, factual results, but are not used
15    at the referee level. Functions that rank, score, or apply
16    probability do not belong here. */
17 
18 #include "conq.h"
19 #include "kpublic.h"
20 #include "aiutil.h"
21 #include "aiunit.h"
22 
23 /* Minimum dice roll result. */
24 int
dice_roll_min(int dicerep)25 dice_roll_min(int dicerep)
26 {
27     int dicenum = 0, dicespots = 0, dicebonus = 0;
28 
29     DICE(dicerep, dicenum, dicespots, dicebonus);
30     return dicebonus;
31 }
32 
33 /* Mean dice roll result. */
34 int
dice_roll_mean(int dicerep)35 dice_roll_mean(int dicerep)
36 {
37     int dicenum = 0, dicespots = 0, dicebonus = 0;
38 
39     DICE(dicerep, dicenum, dicespots, dicebonus);
40     return dicebonus + ((dicenum * dicespots) / 2);
41 }
42 
43 /* Maximum dice roll result. */
44 int
dice_roll_max(int dicerep)45 dice_roll_max(int dicerep)
46 {
47     int dicenum = 0, dicespots = 0, dicebonus = 0;
48 
49     DICE(dicerep, dicenum, dicespots, dicebonus);
50     return dicebonus + (dicenum * (dicespots - 1));
51 }
52 
53 /* Could u destroy u2 by attacking or firing? */
54 int
could_destroy(int u,int u2)55 could_destroy(int u, int u2)
56 {
57     return (could_damage(u, u2) && !uu_hp_min(u, u2));
58 }
59 
60 /* What is the maximum damage that u can do to u2 by attacking? */
61 int
type_attack_damage_max(int u,int u2)62 type_attack_damage_max(int u, int u2)
63 {
64     if (!could_damage_by_attacks(u, u2))
65       return 0;
66     return dice_roll_max(uu_damage(u, u2));
67 }
68 
69 /* What is the minimum damage that u can do to u2 by attacking? */
70 int
type_attack_damage_min(int u,int u2)71 type_attack_damage_min(int u, int u2)
72 {
73     if (!could_damage_by_attacks(u, u2))
74       return 0;
75     return dice_roll_min(uu_damage(u, u2));
76 }
77 
78 /* What is the maximum damage that u can do to u2 by firing? */
79 int
type_fire_damage_max(int u,int u2)80 type_fire_damage_max(int u, int u2)
81 {
82     if (!could_damage_by_fire(u, u2))
83       return 0;
84     return dice_roll_max(fire_damage(u, u2));
85 }
86 
87 /* What is the minimum damage that u can do to u2 by firing? */
88 int
type_fire_damage_min(int u,int u2)89 type_fire_damage_min(int u, int u2)
90 {
91     if (!could_damage_by_fire(u, u2))
92       return 0;
93     return dice_roll_min(fire_damage(u, u2));
94 }
95 
96 /* What is the maximum damage that u can do to u2 by attacking or
97    firing? */
98 int
type_damage_max(int u,int u2)99 type_damage_max(int u, int u2)
100 {
101     return max(type_attack_damage_max(u, u2),
102                type_fire_damage_max(u, u2));
103 }
104 
105 /* What is the maximum striking range of u versus u2 when attacking? */
106 int
type_attack_range_max(int u,int u2)107 type_attack_range_max(int u, int u2)
108 {
109     if (g_combat_model() == 0)
110       return uu_attack_range(u, u2);
111     return 1;
112 }
113 
114 /* What is the maximum striking range of u versus u2 when firing? */
115 int
type_fire_range_max(int u,int u2)116 type_fire_range_max(int u, int u2)
117 {
118     if (g_combat_model() == 0)
119       return u_range(u);
120     return 0;
121 }
122 
123 /* What is the maximum striking range of u versus u2 when attacking or
124    firing? */
125 int
type_hit_range_max(int u,int u2)126 type_hit_range_max(int u, int u2)
127 {
128     return max(type_attack_range_max(u, u2), type_fire_range_max(u, u2));
129 }
130 
131 /* What is the ideal maximum striking range of u versus u2 when attacking? */
132 int
type_ideal_attack_range_max(int u,int u2)133 type_ideal_attack_range_max(int u, int u2)
134 {
135     return type_attack_range_max(u, u2);
136 }
137 
138 /* What is the ideal maximum striking range of u versus u2 when firing? */
139 int
type_ideal_fire_range_max(int u,int u2)140 type_ideal_fire_range_max(int u, int u2)
141 {
142     if (g_combat_model() == 0) {
143         if (u_hit_falloff_range(u) < u_range(u)) {
144             if (u_hit_falloff_range(u) >= u_range_min(u))
145               return u_hit_falloff_range(u);
146             else
147               return u_range_min(u);
148         }
149         else
150           return u_range(u);
151     }
152     return 0;
153 }
154 
155 /* What is the ideal maximum striking range of u versus u2 when attacking or
156    firing? */
157 int
type_ideal_hit_range_max(int u,int u2)158 type_ideal_hit_range_max(int u, int u2)
159 {
160     return max(type_ideal_attack_range_max(u, u2),
161                type_ideal_fire_range_max(u, u2));
162 }
163 
164 /* What kind of hits can u do on u2? */
165 int
type_possible_damage_methods(int u,int u2)166 type_possible_damage_methods(int u, int u2)
167 {
168     int dmgtypes = DAMAGE_TYPE_NONE;
169 
170     if (could_damage_by_attacks(u, u2))
171       dmgtypes |= DAMAGE_TYPE_ATTACK;
172     if (could_damage_by_fire(u, u2))
173       dmgtypes |= DAMAGE_TYPE_FIRE;
174     /* (Consider other ways for u to damage u2.) */
175     return dmgtypes;
176 }
177 
178 /* What is the occupancy status of a given unit view? */
179 OccStatus
occ_status(UnitView * uview)180 occ_status(UnitView *uview)
181 {
182     UnitView *uvocc = NULL;
183     int u2 = uview->type;
184 
185     if (!type_can_have_occs(u2))
186       return CANNOT_HAVE_OCCS;
187     if (g_see_all() || u_see_occupants(u2)) {
188         for_all_occupant_views(uview, uvocc) {
189             if (uvocc)
190               return DEFINITELY_HAS_OCCS;
191         }
192         if (!uvocc) {
193             if (g_see_all())
194               return DEFINITELY_HAS_NO_OCCS;
195             else
196               return MAYBE_HAS_OCCS;
197         }
198     }
199     return MAYBE_HAS_OCCS;
200 }
201 
202 /* Is the unit planning to hit a unit of the given type at the given
203    position? */
204 int
planning_to_hit_type_at(Unit * unit,int u,int x,int y)205 planning_to_hit_type_at(Unit *unit, int u, int x, int y)
206 {
207     Task *task = NULL;
208 
209     if (in_play(unit) && unit->plan) {
210         for_all_tasks(unit->plan, task) {
211             if (task->type == TASK_HIT_UNIT
212                 && task->args[0] == x && task->args[1] == y
213                 && (task->args[2] == NONUTYPE || task->args[2] == u))
214               return TRUE;
215         }
216     }
217     return FALSE;
218 }
219 
220 /* Is the unit planning to capture a unit of the given type at the given
221    position? */
222 int
planning_to_capture_type_at(Unit * unit,int u,int x,int y)223 planning_to_capture_type_at(Unit *unit, int u, int x, int y)
224 {
225     Task *task = NULL;
226 
227     if (in_play(unit) && unit->plan) {
228         for_all_tasks(unit->plan, task) {
229             if (task->type == TASK_CAPTURE
230                 && task->args[0] == x && task->args[1] == y
231                 && (task->args[2] == NONUTYPE || task->args[2] == u))
232               return TRUE;
233             if (task->type == TASK_HIT_UNIT
234                 && task->args[0] == x && task->args[1] == y
235                 && (task->args[2] == NONUTYPE || task->args[2] == u)
236                 && uu_capture(unit->type, u) > 0)
237               return TRUE;
238         }
239     }
240     return FALSE;
241 }
242 
243 /* Is the side planning to hit a unit of the given type at the given
244    position? */
245 int
side_planning_to_hit_type_at(Side * side,int u,int x,int y)246 side_planning_to_hit_type_at(Side *side, int u, int x, int y)
247 {
248     Unit *unit = NULL;
249 
250     for_all_side_units(side, unit) {
251         if (planning_to_hit_type_at(unit, u, x, y))
252           return TRUE;
253     }
254     return FALSE;
255 }
256 
257 /* How many side units are planning to hit a unit of the given type at
258    the given position? */
259 int
n_planning_to_hit_type_at(Side * side,int u,int x,int y)260 n_planning_to_hit_type_at(Side *side, int u, int x, int y)
261 {
262     Unit *unit = NULL;
263     int counter = 0;
264 
265     for_all_side_units(side, unit) {
266         if (planning_to_hit_type_at(unit, u, x, y))
267           ++counter;
268     }
269     return counter;
270 }
271 
272 /* Is the side planning to capture a unit of the given type at the given
273    position? */
274 int
side_planning_to_capture_type_at(Side * side,int u,int x,int y)275 side_planning_to_capture_type_at(Side *side, int u, int x, int y)
276 {
277     Unit *unit = NULL;
278 
279     for_all_side_units(side, unit) {
280         if (planning_to_capture_type_at(unit, u, x, y))
281           return TRUE;
282     }
283     return FALSE;
284 }
285 
286 /* How many side units are planning to capture a unit of the given type at
287    the given position? */
288 int
n_planning_to_capture_type_at(Side * side,int u,int x,int y)289 n_planning_to_capture_type_at(Side *side, int u, int x, int y)
290 {
291     Unit *unit = NULL;
292     int counter = 0;
293 
294     for_all_side_units(side, unit) {
295         if (planning_to_capture_type_at(unit, u, x, y))
296           ++counter;
297     }
298     return counter;
299 }
300 
301 /* Is a given unit satisfied that it has enough of a given material? */
302 
303 int
has_enough_of_material(Unit * unit,int m)304 has_enough_of_material(Unit *unit, int m)
305 {
306     int u = NONUTYPE;
307 
308     if (NONMTYPE == m)
309       return TRUE;
310     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
311     assert_warning_return(m < nummtypes,
312                           "Reference to illegal material type.",
313                           FALSE);
314     if (!(unit->supply))
315       return TRUE;
316     u = unit->type;
317     if (um_storage_x(u, m) == 0)
318       return TRUE;
319     if (unit->supply[m] >
320         ((um_storage_x(u, m) * unit_doctrine(unit)->resupply_percent) / 100))
321       return TRUE;
322     return FALSE;
323 }
324 
325 /* How much of a given material is needed to satisfy a given unit? */
326 
327 int
needs_n_of_material(Unit * unit,int m)328 needs_n_of_material(Unit *unit, int m)
329 {
330     int u = NONUTYPE;
331 
332     if (has_enough_of_material(unit, m))
333       return 0;
334     assert_warning_return(unit, "Unexpected null pointer encountered.", 0);
335     assert_warning_return(m < nummtypes,
336                           "Reference to illegal material type.",
337                           0);
338     u = unit->type;
339     return (((um_storage_x(u, m) * unit_doctrine(unit)->resupply_percent)
340              / 100) - unit->supply[m]);
341 }
342 
343 /* Does the given unit have the full amount of a given material? */
344 
345 int
has_full_amount_of_material(Unit * unit,int m)346 has_full_amount_of_material(Unit *unit, int m)
347 {
348     int u = NONUTYPE;
349 
350     if (NONMTYPE == m)
351       return TRUE;
352     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
353     assert_warning_return(m < nummtypes,
354                           "Reference to illegal material type.",
355                           FALSE);
356     if (!(unit->supply))
357       return TRUE;
358     u = unit->type;
359     return (unit->supply[m] == um_storage_x(u, m));
360 }
361 
362 /* How much of a given material is needed to fill a given unit? */
363 
364 int
wants_n_of_material(Unit * unit,int m)365 wants_n_of_material(Unit *unit, int m)
366 {
367     int u = NONUTYPE;
368 
369     if (has_full_amount_of_material(unit, m))
370       return 0;
371     assert_warning_return(unit, "Unexpected null pointer encountered.", 0);
372     assert_warning_return(m < nummtypes,
373                           "Reference to illegal material type.",
374                           0);
375     u = unit->type;
376     return (um_storage_x(u, m) - unit->supply[m]);
377 }
378 
379 /* Would a given unit type starve without a given material? */
380 
381 int
would_starve_without_material(int u,int m)382 would_starve_without_material(int u, int m)
383 {
384     if (m == NONMTYPE)
385       return FALSE;
386     assert_warning_return(m < nummtypes,
387                           "Reference to illegal material type.",
388                           FALSE);
389     return (um_hp_per_starve(u, m) > 0);
390 }
391 
392 /* Will a given unit starve unless it has some of a given material? */
393 
394 int
will_starve_wrt_material(Unit * unit,int m)395 will_starve_wrt_material(Unit *unit, int m)
396 {
397     int u = NONUTYPE;
398 
399     if (m == NONMTYPE)
400       return FALSE;
401     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
402     assert_warning_return(m < nummtypes,
403                           "Reference to illegal material type.",
404                           FALSE);
405     if (!(unit->supply))
406       return FALSE;
407     u = unit->type;
408     if (wants_n_of_material(unit, m) >= unit->supply[m])
409       return would_starve_without_material(u, m);
410     return FALSE;
411 }
412 
413 /* How much of a given material can a given unit readily provide? */
414 
415 int
can_donate_n_of_material(Unit * unit,int m)416 can_donate_n_of_material(Unit *unit, int m)
417 {
418     int u = NONUTYPE, mavail = 0;
419 
420     if (m == NONMTYPE)
421       return 0;
422     assert_warning_return(unit, "Unexpected null pointer encountered.", 0);
423     assert_warning_return(m < nummtypes,
424                           "Reference to illegal material type.",
425                           0);
426     if (!(unit->supply))
427       return 0;
428     u = unit->type;
429     if (!um_storage_x(u, m))
430       return 0;
431     mavail = unit->supply[m] -
432              ((um_storage_x(u, m) * unit_doctrine(unit)->resupply_percent) /
433               100);
434     if (mavail < 0)
435       return 0;
436     return mavail;
437 }
438 
439 /* How much of a given material can a given unit provide and still be able
440    to survive without acting for one turn? */
441 
442 int
can_sacrifice_n_of_material(Unit * unit,int m)443 can_sacrifice_n_of_material(Unit *unit, int m)
444 {
445     int u = NONUTYPE, mavail = 0;
446 
447     if (m == NONMTYPE)
448       return 0;
449     assert_warning_return(unit, "Unexpected null pointer encountered.", 0);
450     assert_warning_return(m < nummtypes,
451                           "Reference to illegal material type.",
452                           0);
453     if (!(unit->supply))
454       return 0;
455     u = unit->type;
456     if (!um_storage_x(u, m))
457       return 0;
458     if (!would_starve_without_material(u, m))
459       return unit->supply[m];
460     mavail = unit->supply[m] -
461              (um_base_consumption(u, m) - um_base_production(u, m));
462     if (mavail < 0)
463       return 0;
464     return mavail;
465 }
466 
467 /* Is a given unit satisfied that it has enough of all materials? */
468 
469 int
has_enough_of_all_materials(Unit * unit)470 has_enough_of_all_materials(Unit *unit)
471 {
472     int m = NONMTYPE;
473 
474     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
475     if (!(unit->supply))
476       return TRUE;
477     for_all_material_types(m) {
478         if (!has_enough_of_material(unit, m))
479           return FALSE;
480     }
481     return TRUE;
482 }
483 
484 /* Does a given unit have a full amount of all materials? */
485 
486 int
has_full_amount_of_all_materials(Unit * unit)487 has_full_amount_of_all_materials(Unit *unit)
488 {
489     int m = NONMTYPE;
490 
491     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
492     if (!(unit->supply))
493       return TRUE;
494     for_all_material_types(m) {
495         if (!has_full_amount_of_material(unit, m))
496           return FALSE;
497     }
498     return TRUE;
499 }
500 
501 /* Will a given unit starve unless it receives some material? */
502 
503 int
will_starve(Unit * unit)504 will_starve(Unit *unit)
505 {
506     int m = NONMTYPE;
507 
508     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
509     if (!(unit->supply))
510       return FALSE;
511     for_all_material_types(m) {
512         if (will_starve_wrt_material(unit, m))
513           return TRUE;
514     }
515     return FALSE;
516 }
517 
518 /* Which material is needed to prevent starvation? */
519 
520 int
critically_needed_material(Unit * unit)521 critically_needed_material(Unit *unit)
522 {
523     int m = NONMTYPE;
524 
525     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
526     if (!(unit->supply))
527       return NONMTYPE;
528     for_all_material_types(m) {
529         if (will_starve_wrt_material(unit, m))
530           return m;
531     }
532     return NONMTYPE;
533 }
534 
535 /* Which material is most needed by the given unit? */
536 
537 int
most_needed_material(Unit * unit,int * nmost)538 most_needed_material(Unit *unit, int *nmost)
539 {
540     int m = NONMTYPE, mmost = NONMTYPE;
541     int n = 0, nmost2 = 0;
542 
543     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
544     if (!(unit->supply))
545       return NONMTYPE;
546     for_all_material_types(m) {
547         if (will_starve_wrt_material(unit, m)) {
548             mmost = m;
549             nmost2 = um_storage_x(unit->type, m) - unit->supply[m];
550             break;
551         }
552         n = needs_n_of_material(unit, m);
553         if (n > nmost2) {
554             nmost2 = n;
555             mmost = m;
556         }
557     }
558     if (nmost)
559       *nmost = nmost2;
560     return mmost;
561 }
562 
563 /* Which material is most wanted by the given unit? */
564 
565 int
most_wanted_material(Unit * unit,int * nmost)566 most_wanted_material(Unit *unit, int *nmost)
567 {
568     int m = NONMTYPE, mmost = NONMTYPE;
569     int n = 0, nmost2 = 0;
570 
571     assert_warning_return(unit, "Unexpected null pointer encountered.", FALSE);
572     if (!(unit->supply))
573       return NONMTYPE;
574     for_all_material_types(m) {
575         if (will_starve_wrt_material(unit, m)) {
576             mmost = m;
577             nmost2 = um_storage_x(unit->type, m) - unit->supply[m];
578             break;
579         }
580         n = wants_n_of_material(unit, m);
581         if (n > nmost2) {
582             nmost2 = n;
583             mmost = m;
584         }
585     }
586     if (nmost)
587       *nmost = nmost2;
588     return mmost;
589 }
590 
591 /* See if any known enemies in a given cell can exert a blocking ZOC against
592    a given unit to enter another given cell. */
593 
594 int
can_be_blocked_by_any_known_enemy_at_if_at(int x,int y,int * counter,ParamBox * parambox)595 can_be_blocked_by_any_known_enemy_at_if_at(int x, int y, int *counter,
596                                            ParamBox *parambox)
597 {
598     ParamBoxUnitAt *bzocpbox = NULL;
599     int nx = -1, ny = -1;
600     int dist = -1, range = -1;
601     Unit *unit = NULL, *unit2 = NULL;
602     Side *side = NULL, *oside = NULL;
603     UnitView *uview = NULL;
604     int u = NONUTYPE, u2 = NONUTYPE;
605     int t = NONTTYPE;
606 
607     bzocpbox = (ParamBoxUnitAt *)parambox;
608     /* Sanity checks. */
609     assert_warning_return(bzocpbox,
610                           "Function received unexpected null pointer",
611                           FALSE);
612     assert_warning_return(PBOX_TYPE_UNIT_AT == bzocpbox->get_type(),
613                           "Function received illegal parameter",
614                           FALSE);
615     unit = bzocpbox->unit;
616     assert_warning_return(unit && in_play(unit),
617                           "Function received bad parameter",
618                           FALSE);
619     if (max_zoc_range < 0)
620       return FALSE;
621     side = unit->side;
622     if (!unit_view_at(side, x, y))
623       return FALSE;
624     u = unit->type;
625     nx = bzocpbox->x; ny = bzocpbox->y;
626     t = terrain_at(nx, ny);
627     dist = distance(x, y, nx, ny);
628     for_all_view_stack_with_occs(side, x, y, uview) {
629         oside = side_n(uview->siden);
630         if (!enemy_side(side, oside))
631           continue;
632         u2 = uview->type;
633         unit2 = view_unit(uview);
634         range = zoc_range(unit2, u);
635         if (range < dist)
636           continue;
637         if (is_active(unit2) && unit_blockable_by(unit, unit2)
638             && ((UNSEEN != terrain_view(side, nx, ny)) ? ut_zoc_into(u2, t)
639                                                        : TRUE)
640             && ((UNSEEN != terrain_view(side, x, y))
641                  ? (ut_zoc_from_terrain(u2, terrain_at(x, y)) > 0) : TRUE))
642             return TRUE;
643     }
644     return FALSE;
645 }
646 
647 /* See if any known enemies can exert a blocking ZOC against a given type
648    attempting to enter a given cell. */
649 int
can_be_blocked_by_any_known_enemy_if_at(int x,int y,int * counter,ParamBox * parambox)650 can_be_blocked_by_any_known_enemy_if_at(int x, int y, int *counter,
651                                         ParamBox *parambox)
652 {
653     ParamBoxUnitAt *bzocpbox = NULL;
654     Unit *unit = NULL;
655     int dir = NODIR;
656     int nx = -1, ny = -1;
657 
658     bzocpbox = (ParamBoxUnitAt *)parambox;
659     /* Sanity checks. */
660     assert_warning_return(bzocpbox,
661                           "Function received unexpected null pointer.",
662                           FALSE);
663     assert_warning_return(PBOX_TYPE_UNIT_AT == bzocpbox->get_type(),
664                           "Function received illegal parameter.",
665                           FALSE);
666     unit = bzocpbox->unit;
667     assert_warning_return(unit && in_play(unit),
668                           "Function received bad parameter.",
669                           FALSE);
670     if (max_zoc_range < 0)
671       return FALSE;
672     /* Search for blockers from the speculative position. */
673     /* (Should do this with search_around, but that function could use
674         param box support first.) */
675     bzocpbox->x = x; bzocpbox->y = y;
676     for_all_directions(dir) {
677         if (point_in_dir(x, y, dir, &nx, &ny)) {
678             if (search_under_arc(nx, ny, dir, max_zoc_range, -1,
679                                  can_be_blocked_by_any_known_enemy_at_if_at,
680                                  (ParamBox *)parambox))
681               return TRUE;
682         }
683     }
684     return FALSE;
685 }
686