1
/**********************************************************************
2
Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3
This program is free software; you can redistribute it and/or modify
4
it under the terms of the GNU General Public License as published by
5
the Free Software Foundation; either version 2, or (at your option)
8
This program is distributed in the hope that it will be useful,
9
but WITHOUT ANY WARRANTY; without even the implied warranty of
10
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
GNU General Public License for more details.
12
***********************************************************************/
36
#include "unittools.h"
43
struct move_cost_map warmap;
46
* These settings should be either true or false. They are used for
47
* finding routes for airplanes - the airplane doesn't want to fly
48
* through occupied territory, but most territory will be either
49
* fogged or unknown entirely. See airspace_looks_safe(). Note that
50
* this is currently only used by the move-counting function
51
* air_can_move_between(), not by the path-finding function (whatever
54
#define AIR_ASSUMES_UNKNOWN_SAFE TRUE
55
#define AIR_ASSUMES_FOGGED_SAFE TRUE
57
static bool airspace_looks_safe(struct tile *ptile, struct player *pplayer);
60
/* These are used for all GOTO's */
62
/* A byte must be able to hold this value i.e. is must be less than
65
#define MAXARRAYS 10000
66
#define ARRAYLENGTH 10
71
struct tile *tile[ARRAYLENGTH];
72
struct mappos_array *next_array;
75
struct array_pointer {
76
struct mappos_array *first_array;
77
struct mappos_array *last_array;
80
static struct mappos_array *mappos_arrays[MAXARRAYS];
81
static struct array_pointer cost_lookup[MAXCOST];
82
static int array_count;
83
static int lowest_cost;
84
static int highest_cost;
87
/*************************************************************************
88
Used to check if the warmap distance to a position is "finite".
89
*************************************************************************/
90
bool is_dist_finite(int dist)
93
return (dist < MAXCOST);
96
/**************************************************************************
98
**************************************************************************/
99
static void init_queue(void)
102
static bool is_initialized = FALSE;
104
if (!is_initialized) {
105
for (i = 0; i < MAXARRAYS; i++) {
106
mappos_arrays[i] = NULL;
108
is_initialized = TRUE;
111
for (i = 0; i < MAXCOST; i++) {
112
cost_lookup[i].first_array = NULL;
113
cost_lookup[i].last_array = NULL;
120
/**************************************************************************
122
**************************************************************************/
123
void free_mapqueue(void)
127
for (i = 0; i < MAXARRAYS; i++) {
128
if (mappos_arrays[i] != NULL) {
129
free(mappos_arrays[i]);
130
mappos_arrays[i] = NULL;
135
/**************************************************************************
137
**************************************************************************/
138
static struct mappos_array *get_empty_array(void)
140
struct mappos_array *parray;
142
if (!mappos_arrays[array_count]) {
143
mappos_arrays[array_count]
144
= fc_malloc(sizeof(*mappos_arrays[array_count]));
146
parray = mappos_arrays[array_count++];
147
parray->first_pos = 0;
148
parray->last_pos = -1;
149
parray->next_array = NULL;
153
/**************************************************************************
155
**************************************************************************/
156
static void add_to_mapqueue(int cost, struct tile *ptile)
158
struct mappos_array *our_array;
160
assert(cost < MAXCOST && cost >= 0);
162
our_array = cost_lookup[cost].last_array;
164
our_array = get_empty_array();
165
cost_lookup[cost].first_array = our_array;
166
cost_lookup[cost].last_array = our_array;
167
} else if (our_array->last_pos == ARRAYLENGTH-1) {
168
our_array->next_array = get_empty_array();
169
our_array = our_array->next_array;
170
cost_lookup[cost].last_array = our_array;
173
our_array->tile[++(our_array->last_pos)] = ptile;
174
if (cost > highest_cost)
176
freelog(LOG_DEBUG, "adding cost:%i at %i,%i", cost, ptile->x, ptile->y);
179
/**************************************************************************
181
**************************************************************************/
182
static struct tile *get_from_mapqueue(void)
184
struct mappos_array *our_array;
187
freelog(LOG_DEBUG, "trying get");
188
while (lowest_cost < MAXCOST) {
189
if (lowest_cost > highest_cost)
191
our_array = cost_lookup[lowest_cost].first_array;
196
if (our_array->last_pos < our_array->first_pos) {
197
if (our_array->next_array) {
198
cost_lookup[lowest_cost].first_array = our_array->next_array;
199
continue; /* note NOT "lowest_cost++;" */
201
cost_lookup[lowest_cost].first_array = NULL;
206
ptile = our_array->tile[our_array->first_pos];
207
our_array->first_pos++;
213
/**************************************************************************
214
Reset the movecosts of the warmap.
215
**************************************************************************/
216
static void init_warmap(struct tile *orig_tile, enum unit_move_type move_type)
218
if (warmap.size != MAP_INDEX_SIZE) {
219
warmap.cost = fc_realloc(warmap.cost,
220
MAP_INDEX_SIZE * sizeof(*warmap.cost));
221
warmap.seacost = fc_realloc(warmap.seacost,
222
MAP_INDEX_SIZE * sizeof(*warmap.seacost));
223
warmap.vector = fc_realloc(warmap.vector,
224
MAP_INDEX_SIZE * sizeof(*warmap.vector));
225
warmap.size = MAP_INDEX_SIZE;
233
assert(sizeof(*warmap.cost) == sizeof(char));
234
memset(warmap.cost, MAXCOST, MAP_INDEX_SIZE * sizeof(char));
235
warmap.cost[tile_index(orig_tile)] = 0;
238
assert(sizeof(*warmap.seacost) == sizeof(char));
239
memset(warmap.seacost, MAXCOST, MAP_INDEX_SIZE * sizeof(char));
240
warmap.seacost[tile_index(orig_tile)] = 0;
243
freelog(LOG_ERROR, "Bad move_type in init_warmap().");
248
/**************************************************************************
249
This creates a movecostmap (warmap), which is a two-dimentional array
250
corresponding to the real map. The value of a position is the number of
251
moves it would take to get there. If the function found no route the cost
252
is MAXCOST. (the value it is initialized with)
253
For sea units we let the map overlap onto the land one field, to allow
254
transporters and shore bombardment (ships can target the shore).
255
This map does NOT take enemy units onto account, nor ZOC.
256
Let generate_warmap interface to this function. Sometimes (in the AI when
257
using transports) this function will be called directly.
259
It is an search via Dijkstra's Algorithm
260
(see fx http://odin.ee.uwa.edu.au/~morris/Year2/PLDS210/dijkstra.html)
261
ie it piles tiles on a queue, and then use those tiles to find more tiles,
262
until all tiles we can reach are visited. To avoid making the warmap
263
potentially too big (heavy to calculate), the warmap is initialized with
264
a maxcost value, limiting the maximum length.
266
Note that this function is responsible for 20% of the CPU usage of freeciv...
268
This function is used by the AI in various cases when it is searching for
269
something to do with a unit. Maybe we could make a version that processed
270
the tiles in order after how many moves it took the unit to get to them,
271
and then gave them to the unit. This would achive 2 things:
272
-The unit could stop the function the instant it found what it was looking
273
for, or when it had already found something reasonably close and the distance
274
to the tiles later being searched was reasoable big. In both cases avoiding
275
to build the full warmap.
276
-The warmap would avoid examining a tile multiple times due to a series of
277
path with succesively smaller cost, since we would always have the smallest
278
possible cost the first time.
279
This would be done by inserting the tiles in a list after their move_cost
281
**************************************************************************/
282
static void really_generate_warmap(struct city *pcity, struct unit *punit,
283
enum unit_move_type move_type)
286
struct tile *orig_tile;
287
bool igter, terrain_speed;
288
int maxcost = THRESHOLD * 6 + 2; /* should be big enough without being TOO big */
290
struct player *pplayer;
291
struct unit_class *pclass = NULL;
292
int unit_speed = SINGLE_MOVE;
295
orig_tile = pcity->tile;
296
pplayer = city_owner(pcity);
298
orig_tile = punit->tile;
299
pplayer = unit_owner(punit);
302
init_warmap(orig_tile, move_type);
303
add_to_mapqueue(0, orig_tile);
307
pclass = unit_class(punit);
309
terrain_speed = uclass_has_flag(unit_class(punit), UCF_TERRAIN_SPEED);
310
if (!terrain_speed /* Igter meaningless without terrain_speed */
311
&& unit_has_type_flag(punit, F_IGTER)) {
314
unit_speed = unit_move_rate(punit);
316
/* Guess - can result in bad city warmaps with custom rulesets */
317
if (move_type == LAND_MOVING || move_type == SEA_MOVING) {
318
terrain_speed = TRUE;
320
terrain_speed = FALSE;
324
/* FIXME: Should this apply only to F_CITIES units? -- jjm */
326
&& unit_has_type_flag(punit, F_SETTLERS)
327
&& unit_move_rate(punit)==3)
329
/* (?) was unit_type(punit) == U_SETTLERS -- dwp */
331
while ((ptile = get_from_mapqueue())) {
332
/* Just look up the cost value once. This is a minor optimization but
333
* it makes a big difference since this code is called so much. */
334
unsigned char cost = ((move_type == SEA_MOVING)
335
? WARMAP_SEACOST(ptile) : WARMAP_COST(ptile));
337
adjc_dir_iterate(ptile, tile1, dir) {
338
struct terrain *pterrain1 = tile_terrain(tile1);
340
if ((move_type == LAND_MOVING && WARMAP_COST(tile1) <= cost)
341
|| (move_type == SEA_MOVING && WARMAP_SEACOST(tile1) <= cost)) {
342
continue; /* No need for calculations */
345
if (!terrain_speed) {
347
if (!can_unit_exist_at_tile(punit, tile1)) {
349
if (can_attack_non_native(unit_type(punit))
350
&& is_native_tile(unit_type(punit), ptile)) {
351
/* Able to shore bombardment or similar activity,
352
* but not from city in non-native terrain. */
354
/* Attack cost is SINGLE_MOVE */
355
move_cost = SINGLE_MOVE;
356
} else if (unit_class_transporter_capacity(tile1, pplayer, pclass) > 0) {
357
/* Enters transport */
358
move_cost = SINGLE_MOVE;
360
/* Can't enter tile */
364
/* Punit can_exist_at_tile() */
365
move_cost = SINGLE_MOVE;
368
/* No punit. Crude guess */
369
if ((move_type == SEA_MOVING && !is_ocean(pterrain1))
370
|| (move_type == LAND_MOVING && is_ocean(pterrain1))) {
371
/* Can't enter tile */
374
move_cost = SINGLE_MOVE;
378
/* Speed determined by terrain */
380
if (!can_unit_exist_at_tile(punit, tile1)) {
381
if (can_attack_non_native(unit_type(punit))
382
&& is_native_tile(unit_type(punit), ptile)) {
383
/* Shore bombardment. Impossible if already in city
384
* in non-native terrain. */
385
move_cost = SINGLE_MOVE;
387
} else if (unit_class_transporter_capacity(tile1, pplayer, pclass) > 0) {
389
move_cost = SINGLE_MOVE;
391
/* Can't enter tile */
394
} else if (is_native_tile(unit_type(punit), tile1)) {
395
/* can_exist_at_tile() */
396
int base_cost = map_move_cost(ptile, tile1);
398
base_cost = base_cost > unit_speed ? unit_speed : base_cost;
399
move_cost = igter ? MIN(MOVE_COST_ROAD, base_cost) : base_cost;
401
/* can_exist_at_tile(), but !is_native_tile() -> entering port */
402
move_cost = SINGLE_MOVE;
404
} else if ((move_type == LAND_MOVING && is_ocean(pterrain1))
405
|| (move_type == SEA_MOVING && !is_ocean(pterrain1))) {
406
continue; /* City warmap ignores possible transports */
408
move_cost = map_move_cost(ptile, tile1);
414
if (move_type != SEA_MOVING) {
415
if (WARMAP_COST(tile1) > move_cost && move_cost < maxcost) {
416
warmap.cost[tile_index(tile1)] = move_cost;
417
add_to_mapqueue(move_cost, tile1);
419
} else if (move_type == SEA_MOVING) {
420
if (WARMAP_SEACOST(tile1) > move_cost && move_cost < maxcost) {
421
/* by adding the move_cost to the warmap regardless if we
422
* can move between we allow for shore bombardment/transport
423
* to inland positions/etc. */
424
warmap.seacost[tile_index(tile1)] = move_cost;
425
if ((punit && can_unit_exist_at_tile(punit, tile1))
426
|| (!punit && (is_ocean(pterrain1) || tile_city(tile1)))) {
427
/* Unit can actually move to tile */
428
add_to_mapqueue(move_cost, tile1);
432
} adjc_dir_iterate_end;
435
freelog(LOG_DEBUG, "Generated warmap for (%d,%d).",
439
/**************************************************************************
440
This is a wrapper for really_generate_warmap that checks if the warmap we
441
want is allready in existence. Also calls correctly depending on whether
442
pcity and/or punit is nun-NULL.
443
FIXME: Why is the movetype not used initialized on the warmap? Leaving it
445
**************************************************************************/
446
void generate_warmap(struct city *pcity, struct unit *punit)
448
freelog(LOG_DEBUG, "Generating warmap, pcity = %s, punit = %s",
449
(pcity ? city_name(pcity) : "NULL"),
450
(punit ? unit_rule_name(punit) : "NULL"));
454
* If the previous warmap was for the same unit and it's still
455
* correct (warmap.(sea)cost[x][y] == 0), reuse it.
457
if (warmap.warunit == punit &&
458
(is_sailing_unit(punit) ? (WARMAP_SEACOST(punit->tile) == 0)
459
: (WARMAP_COST(punit->tile) == 0))) {
466
warmap.warcity = pcity;
467
warmap.warunit = punit;
469
warmap.invalid = FALSE;
472
if (is_sailing_unit(punit)) {
473
really_generate_warmap(pcity, punit, SEA_MOVING);
475
really_generate_warmap(pcity, punit, LAND_MOVING);
477
warmap.orig_tile = punit->tile;
479
really_generate_warmap(pcity, punit, LAND_MOVING);
480
really_generate_warmap(pcity, punit, SEA_MOVING);
481
warmap.orig_tile = pcity->tile;
485
/**************************************************************************
486
Return the direction that takes us most directly to dest_x,dest_y.
487
The direction is a value for use in DIR_DX[] and DIR_DY[] arrays.
489
FIXME: This is a bit crude; this only gives one correct path, but sometimes
490
there can be more than one straight path (fx going NW then W is the
491
same as going W then NW)
492
**************************************************************************/
493
static int straightest_direction(struct tile *src_tile,
494
struct tile *dst_tile)
499
/* Should we go up or down, east or west: diff_x/y is the "step" in x/y. */
500
map_distance_vector(&diff_x, &diff_y, src_tile, dst_tile);
503
best_dir = (diff_y > 0) ? DIR8_SOUTH : DIR8_NORTH;
504
} else if (diff_y == 0) {
505
best_dir = (diff_x > 0) ? DIR8_EAST : DIR8_WEST;
506
} else if (diff_x > 0) {
507
best_dir = (diff_y > 0) ? DIR8_SOUTHEAST : DIR8_NORTHEAST;
508
} else if (diff_x < 0) {
509
best_dir = (diff_y > 0) ? DIR8_SOUTHWEST : DIR8_NORTHWEST;
518
/**************************************************************************
519
Basic checks as to whether a GOTO is possible. The target (x,y) should
520
be on the same continent as punit is, up to embarkation/disembarkation.
521
**************************************************************************/
522
bool goto_is_sane(struct unit *punit, struct tile *ptile, bool omni)
524
struct player *pplayer = unit_owner(punit);
525
struct city *pcity = tile_city(ptile);
526
Continent_id my_cont = tile_continent(punit->tile);
527
Continent_id target_cont = tile_continent(ptile);
529
if (same_pos(punit->tile, ptile)) {
533
if (!(omni || map_is_known_and_seen(ptile, pplayer, V_MAIN))) {
534
/* The destination is in unknown -- assume sane */
538
switch (uclass_move_type(unit_class(punit))) {
541
if (is_ocean_tile(ptile)) {
542
/* Going to a sea tile, the target should be next to our continent
544
if (unit_class_transporter_capacity(ptile, pplayer,
545
unit_class(punit)) > 0) {
546
adjc_iterate(ptile, tmp_tile) {
547
if (tile_continent(tmp_tile) == my_cont) {
548
/* The target is adjacent to our continent! */
554
/* Going to a land tile: better be our continent */
555
if (my_cont == target_cont) {
558
/* Well, it's not our continent, but maybe we are on a boat
559
* adjacent to the target continent? */
560
adjc_iterate(punit->tile, tmp_tile) {
561
if (tile_continent(tmp_tile) == target_cont) {
571
if (!is_ocean_tile(punit->tile)) {
572
/* Oops, we are not in the open waters. Pick an ocean that we have
573
* access to. We can assume we are in a city, and any oceans adjacent
574
* are connected, so it does not matter which one we pick. */
575
adjc_iterate(punit->tile, tmp_tile) {
576
if (is_ocean_tile(tmp_tile)) {
577
my_cont = tile_continent(tmp_tile);
582
if (is_ocean_tile(ptile)) {
583
if (ai_channel(pplayer, target_cont, my_cont)) {
584
return TRUE; /* Ocean -> Ocean travel ok. */
586
} else if ((pcity && pplayers_allied(city_owner(pcity), pplayer))
587
|| !unit_has_type_flag(punit, F_NO_LAND_ATTACK)) {
588
/* Not ocean, but allied city or can bombard, checking if there is
589
* good ocean adjacent */
590
adjc_iterate(ptile, tmp_tile) {
591
if (is_ocean_tile(tmp_tile)
592
&& ai_channel(pplayer, my_cont, tile_continent(tmp_tile))) {
597
return FALSE; /* Not ok. */
604
/**************************************************************************
605
Returns true if the airspace at given map position _looks_ safe to
606
the given player. The airspace is unsafe if the player believes
607
there is an enemy unit on it. This is tricky, since we have to
608
consider what the player knows/doesn't know about the tile.
609
**************************************************************************/
610
static bool airspace_looks_safe(struct tile *ptile, struct player *pplayer)
613
* We do handicap checks for the player with ai_handicap(). This
614
* function returns true if the player is handicapped. For human
615
* players they'll always return true. This is the desired behavior.
618
/* If the tile's unknown, we (may) assume it's safe. */
619
if (ai_handicap(pplayer, H_MAP) && !map_is_known(ptile, pplayer)) {
620
return AIR_ASSUMES_UNKNOWN_SAFE;
623
/* This is bad: there could be a city there that the player doesn't
624
know about. How can we check that? */
625
if (is_non_allied_city_tile(ptile, pplayer)) {
629
/* If the tile's fogged we again (may) assume it's safe. */
630
if (ai_handicap(pplayer, H_FOG) &&
631
!map_is_known_and_seen(ptile, pplayer, V_MAIN)) {
632
return AIR_ASSUMES_FOGGED_SAFE;
635
/* The tile is unfogged so we can check for enemy units on the
637
return !is_non_allied_unit_tile(ptile, pplayer);
640
/**************************************************************************
641
An air unit starts (src_x,src_y) with moves moves and want to go to
642
(dest_x,dest_y). It returns the number of moves left if this is
643
possible without running out of moves. It returns -1 if it is
646
Note that the 'moves' passed to this function should be the number of
647
steps the air unit has left. The caller should *already* have
648
divided by SINGLE_MOVE.
650
The function has 3 stages:
651
Try to rule out the possibility in O(1) time else
652
Try to quickly verify in O(moves) time else
653
Do an A* search using the warmap to completely search for the path.
654
**************************************************************************/
655
int air_can_move_between(int moves, struct tile *src_tile,
656
struct tile *dest_tile, struct player *pplayer)
659
int dist, total_distance = real_map_distance(src_tile, dest_tile);
662
"air_can_move_between(moves=%d, src=(%i,%i), "
663
"dest=(%i,%i), player=%s)", moves, TILE_XY(src_tile),
664
TILE_XY(dest_tile), player_name(pplayer));
666
/* First we do some very simple O(1) checks. */
667
if (total_distance > moves) {
670
if (total_distance == 0) {
676
* Then we do a fast O(n) straight-line check. It'll work so long
677
* as the straight-line doesn't cross any unreal tiles, unknown
678
* tiles, or enemy-controlled tiles. So, it should work most of the
684
* We don't check the endpoint of the goto, since it is possible
685
* that the endpoint is a tile which has an enemy which should be
686
* attacked. But we do check that all points in between are safe.
688
for (dist = total_distance; dist > 1; dist--) {
689
/* Warning: straightest_direction may not actually follow the
691
int dir = straightest_direction(ptile, dest_tile);
692
struct tile *new_tile;
694
if (!(new_tile = mapstep(ptile, dir))
695
|| !airspace_looks_safe(new_tile, pplayer)) {
701
/* Looks like the O(n) quicksearch worked. */
702
assert(real_map_distance(ptile, dest_tile) == 1);
703
assert(moves - total_distance >= 0);
704
return moves - total_distance;
708
* Finally, we do a full A* search if this isn't one of the specical
709
* cases from above. This is copied from find_the_shortest_path but
710
* we use real_map_distance as a minimum distance estimator for the
711
* A* search. This distance estimator is used for the cost value in
712
* the queue, but is not stored in the warmap itself.
714
* Note, A* is possible here but not in a normal FreeCiv path
715
* finding because planes always take 1 movement unit to move -
716
* which is not true of land units.
719
"air_can_move_between: quick search didn't work. Lets try full.");
721
warmap.warunit = NULL;
722
warmap.warcity = NULL;
724
init_warmap(src_tile, BOTH_MOVING);
726
/* The 0 is inaccurate under A*, but it doesn't matter. */
727
add_to_mapqueue(0, src_tile);
729
while ((ptile = get_from_mapqueue())) {
730
adjc_dir_iterate(ptile, tile1, dir) {
731
if (WARMAP_COST(tile1) != MAXCOST) {
736
* This comes before the airspace_looks_safe check because it's
737
* okay to goto into an enemy.
739
if (same_pos(tile1, dest_tile)) {
741
freelog(LOG_DEBUG, "air_can_move_between: movecost: %i",
742
WARMAP_COST(ptile) + 1);
743
/* The -1 is because we haven't taken the final
745
assert(moves - WARMAP_COST(ptile) - 1 >= 0);
746
return moves - WARMAP_COST(ptile) - 1;
749
/* We refuse to goto through unsafe airspace. */
750
if (airspace_looks_safe(tile1, pplayer)) {
751
int cost = WARMAP_COST(ptile) + 1;
753
warmap.cost[tile_index(tile1)] = cost;
755
/* Now for A* we find the minimum total cost. */
756
cost += real_map_distance(tile1, dest_tile);
758
add_to_mapqueue(cost, tile1);
761
} adjc_dir_iterate_end;
764
freelog(LOG_DEBUG, "air_can_move_between: no route found");