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