[Freeciv-Dev] (PR#6293) new reusable & reentrant pf iterator
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: |
undisclosed-recipients: ; |
Subject: |
[Freeciv-Dev] (PR#6293) new reusable & reentrant pf iterator |
From: |
"Per I. Mathisen" <per@xxxxxxxxxxx> |
Date: |
Sat, 27 Sep 2003 09:43:28 -0700 |
Reply-to: |
rt@xxxxxxxxxxxxxx |
Attached is the improved interface for pf that is both reusable (wrt to
maps) and reentrant (wrt pf_get_position).
rrpf1.diff contains the core changes to pf.
bigger.diff contains the punit->pf basic framework. It is basically the
same as the previously posted 'smaller.diff', only some small fixes. To
reiterate (no pun intended), this add punit->pf and converts rampage and
explorer code to use this system.
( Greg: Please check the code I added to ensure that we create valid maps
for explorers. It might be overkill (== wasted CPU), but at least it works
and seems safe. )
makeaiunit.diff contains code to create and destroy virtual units using pf
(these functions may not be well named).
dip1.diff contains a patch to convert AI diplomat code to use the new
scheme.
I did some crude and mostly unreliable speed tests (subtle differences in
pf maps made it impossible to do the usual autogame measurements) to see
if the cacheing stuff really improves speed (as opposed to recreating the
pf maps), and I found almost no difference in speed. It might be that the
total amount of pf/warmap related code changed is so small (only rampage,
explorer and diplomats) that it is too early to tell.
- Per
Index: common/aicore/path_finding.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/path_finding.c,v
retrieving revision 1.14
diff -u -r1.14 path_finding.c
--- common/aicore/path_finding.c 25 Sep 2003 16:58:00 -0000 1.14
+++ common/aicore/path_finding.c 27 Sep 2003 15:44:01 -0000
@@ -100,6 +100,8 @@
struct pf_map {
int x, y; /* The current position */
mapindex_t index; /* Current offset into lattice */
+ int cache_index; /* Offset into cache array */
+ int current_index; /* Current iterator index point */
struct pf_parameter *params; /* Initial parameters */
struct pqueue *queue; /* Queue of nodes we have reached but not
* processed yet (NS_NEW), sorted by their
@@ -109,6 +111,7 @@
* (enum pf_node_status really) */
struct pqueue *danger_queue; /* Dangerous positions go there */
struct danger_node *d_lattice; /* Lattice with danger stuff */
+ unsigned short *cache; /* Cached map for reusable iterators */
};
static bool danger_iterate_map(struct pf_map *pf_map);
@@ -252,6 +255,11 @@
mapindex_t index;
struct pf_node *node = &pf_map->lattice[pf_map->index];
+ /* Ensure that we have our cache if we want to use it. */
+ if (pf_map->params->cached && !pf_map->cache) {
+ pf_map->cache = malloc(MAX_MAP_INDEX * sizeof(*pf_map->cache));
+ }
+
if (pf_map->params->is_pos_dangerous) {
/* It's a lot different if is_pos_dangerous is defined */
return danger_iterate_map(pf_map);
@@ -348,6 +356,9 @@
pf_map->index = index;
index_to_map_pos(&(pf_map->x), &(pf_map->y), index);
+ if (pf_map->params->cached) {
+ pf_map->cache[pf_map->current_index++] = (unsigned short) index;
+ }
return TRUE;
}
@@ -393,6 +404,10 @@
pf_map->y = pf_map->params->start_y;
pf_map->index = map_pos_to_index(pf_map->x, pf_map->y);
+ /* Initialize cache pointers */
+ pf_map->cache_index = 0;
+ pf_map->current_index = 0;
+
/* Initialise starting node */
init_node(pf_map, &pf_map->lattice[pf_map->index], pf_map->x,
pf_map->y);
@@ -422,7 +437,9 @@
free(pf_map->lattice);
pq_destroy(pf_map->queue);
free(pf_map->status);
-
+ if (pf_map->cache) {
+ free(pf_map->cache);
+ }
free(pf_map->params);
/* Danger-related structs */
@@ -963,6 +980,10 @@
pf_map->index = index;
index_to_map_pos(&(pf_map->x), &(pf_map->y), index);
+ /* FIXME: I am not sure if this is the correct place... - Per */
+ if (pf_map->params->cached) {
+ pf_map->cache[pf_map->current_index] = (unsigned short) index;
+ }
if (pf_map->status[pf_map->index] == NS_WAITING) {
/* We've already returned this node once, skip it */
@@ -1124,4 +1145,46 @@
struct pf_parameter *pf_get_parameter(struct pf_map *map)
{
return map->params;
+}
+
+/************************************************************************
+ If we have enabled reusable iterators, then we can use this function
+ to reset the iterator.
+************************************************************************/
+void pf_cache_reset(struct pf_map *pf_map)
+{
+ assert(pf_map->params->cached);
+
+ pf_map->cache_index = 0;
+}
+
+/************************************************************************
+ Cached version of the pf_next() iterator. Whenever we read something
+ out of pf_next, we record it. If we have recorded, it we use the
+ recording rather than ask pf_next() again.
+
+ This iterator works reentrantly with pf_get_position(), meaning
+ calling that function will not screw up our iteration, like it will
+ if you try to rely on pf_next().
+************************************************************************/
+bool pf_cached_next(struct pf_map *pf_map, struct pf_position *pos)
+{
+ assert(pf_map->params->cached);
+
+ /* Check if we can pick the position out from the cache instead
+ * of iterating further. */
+ if (pf_map->cache_index < pf_map->current_index) {
+ int x, y;
+ unsigned int i = pf_map->cache[pf_map->cache_index++];
+
+ index_to_map_pos(&x, &y, i);
+ fill_position(pf_map, x, y, pos);
+ return TRUE;
+ } else if (pf_next(pf_map)) {
+ pf_next_get_position(pf_map, pos);
+ pf_map->cache_index++;
+ return TRUE;
+ } else {
+ return FALSE;
+ }
}
Index: common/aicore/path_finding.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/path_finding.h,v
retrieving revision 1.5
diff -u -r1.5 path_finding.h
--- common/aicore/path_finding.h 25 Sep 2003 16:58:00 -0000 1.5
+++ common/aicore/path_finding.h 27 Sep 2003 15:44:01 -0000
@@ -342,6 +342,10 @@
/* User provided data. Can be used to attach arbitrary information
* to the map. */
void *data;
+
+ /* Set this to TRUE in order to enable reusable iterators.
+ * Consumes extra memory. */
+ bool cached;
};
/* The map itself. Opaque type. */
@@ -397,5 +401,11 @@
/* Return the current parameters for the given map. */
struct pf_parameter *pf_get_parameter(struct pf_map *map);
+
+/* For reusable iterators: Reset it and start from origin again. */
+void pf_cache_reset(struct pf_map *pf_map);
+
+/* The resuable and reentrant safe map iterator. */
+bool pf_cached_next(struct pf_map *pf_map, struct pf_position *pos);
#endif
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.90
diff -u -r1.90 aitools.c
--- ai/aitools.c 27 Sep 2003 11:22:40 -0000 1.90
+++ ai/aitools.c 27 Sep 2003 13:54:51 -0000
@@ -39,6 +39,8 @@
#include "cityturn.h"
#include "gotohand.h"
#include "maphand.h"
+#include "path_finding.h"
+#include "pf_tools.h"
#include "plrhand.h"
#include "settlers.h"
#include "unithand.h"
@@ -91,6 +93,49 @@
|| pplayer->diplstates[aplayer->player_no].has_reason_to_cancel
|| ai->diplomacy.acceptable_reputation > aplayer->reputation
|| adip->is_allied_with_enemy);
+}
+
+/**************************************************************************
+ Ensure that our map is sane. If not, recreate. Call this always before
+ you reuse a punit->pf pf_map for path-execution unless you *know* where
+ it has been before and that punit has not moved. Note that all
+ parameters in the map may be reset here.
+**************************************************************************/
+void update_unit_map(struct unit *punit, enum ai_unit_map maptype)
+{
+ struct pf_parameter *param = NULL;
+
+ if (punit->pf) {
+ param = pf_get_parameter(punit->pf);
+ if ((maptype == UNIT_MAP_NORMAL && param->get_TB == no_fights_or_unknown)
+ || (maptype == UNIT_MAP_EXPLORER
+ && param->get_TB != no_fights_or_unknown)) {
+ /* We have the wrong type of map. Recreate it. */
+ param = NULL;
+ pf_destroy_map(punit->pf);
+ punit->pf = NULL;
+ }
+ }
+
+ CHECK_UNIT(punit);
+ if (!param || param->start_x != punit->x || param->start_y != punit->y
+ || param->moves_left_initially != punit->moves_left) {
+ /* We have moved since we created this pf_map. So recreate it. */
+ struct pf_parameter new_param;
+
+ if (punit->pf) {
+ pf_destroy_map(punit->pf);
+ }
+ pft_fill_default_parameter(&new_param);
+ pft_fill_unit_parameter(&new_param, punit);
+ if (maptype == UNIT_MAP_EXPLORER) {
+ new_param.get_TB = no_fights_or_unknown;
+ /* When exploring, even AI should pretend to not cheat. */
+ new_param.omniscience = FALSE;
+ }
+ new_param.cached = TRUE;
+ punit->pf = pf_create_map(&new_param);
+ }
}
/*************************************************************************
Index: ai/aitools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.h,v
retrieving revision 1.39
diff -u -r1.39 aitools.h
--- ai/aitools.h 27 Sep 2003 11:22:40 -0000 1.39
+++ ai/aitools.h 27 Sep 2003 13:54:51 -0000
@@ -23,6 +23,10 @@
struct player;
struct pf_path;
+enum ai_unit_map {
+ UNIT_MAP_NORMAL, UNIT_MAP_EXPLORER
+};
+
#ifdef DEBUG
#define CHECK_UNIT(punit) \
(assert(punit), \
@@ -42,6 +46,7 @@
int value, int delay, int build_cost);
int stack_cost(struct unit *pdef);
+void update_unit_map(struct unit *punit, enum ai_unit_map maptype);
bool ai_unit_execute_path(struct unit *punit, struct pf_path *path);
bool ai_unit_gothere(struct unit *punit);
bool ai_unit_goto(struct unit *punit, int x, int y);
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.296
diff -u -r1.296 aiunit.c
--- ai/aiunit.c 27 Sep 2003 11:22:40 -0000 1.296
+++ ai/aiunit.c 27 Sep 2003 13:54:52 -0000
@@ -548,24 +548,15 @@
int best_x = -1, best_y = -1;
/* Path-finding stuff */
- struct pf_map *map;
- struct pf_parameter parameter;
+ struct pf_position pos;
#define DIST_FACTOR 0.6
- pft_fill_default_parameter(¶meter);
- pft_fill_unit_parameter(¶meter, punit);
- parameter.get_TB = no_fights_or_unknown;
- /* When exploring, even AI should pretend to not cheat. */
- parameter.omniscience = FALSE;
-
- map = pf_create_map(¶meter);
- while (pf_next(map)) {
+ update_unit_map(punit, UNIT_MAP_EXPLORER);
+ pf_cache_reset(punit->pf);
+ while (pf_cached_next(punit->pf, &pos)) {
float desirable;
- struct pf_position pos;
- pf_next_get_position(map, &pos);
-
/* Our callback should insure this. */
assert(map_is_known(pos.x, pos.y, pplayer));
@@ -594,7 +585,6 @@
break;
}
}
- pf_destroy_map(map);
/* Go to the best tile found. */
if (most_desirable > 0) {
@@ -1007,27 +997,21 @@
static struct pf_path *find_rampage_target(struct unit *punit,
int thresh_adj, int thresh_move)
{
- struct pf_map *tgt_map;
struct pf_path *path = NULL;
- struct pf_parameter parameter;
/* Coordinates of the best target (initialize to silence compiler) */
int x = punit->x, y = punit->y;
/* Want of the best target */
int max_want = 0;
struct player *pplayer = unit_owner(punit);
-
- pft_fill_default_parameter(¶meter);
- pft_fill_unit_attack_param(¶meter, punit);
-
- tgt_map = pf_create_map(¶meter);
- while (pf_next(tgt_map)) {
- struct pf_position pos;
+ struct pf_position pos;
+
+ update_unit_map(punit, UNIT_MAP_NORMAL);
+ pf_cache_reset(punit->pf);
+ while (pf_cached_next(punit->pf, &pos)) {
int want;
bool move_needed;
int thresh;
- pf_next_get_position(tgt_map, &pos);
-
if (pos.total_MC > punit->moves_left) {
/* This is too far */
break;
@@ -1059,12 +1043,10 @@
if (max_want > 0) {
/* We found something */
- path = pf_get_path(tgt_map, x, y);
+ path = pf_get_path(punit->pf, x, y);
assert(path);
}
- pf_destroy_map(tgt_map);
-
return path;
}
@@ -2797,6 +2779,19 @@
**************************************************************************/
void ai_manage_units(struct player *pplayer)
{
+ unit_list_iterate(pplayer->units, punit) {
+ if (is_ground_unit(punit) || is_sailing_unit(punit)) {
+ /* Allocate a pf_map to the unit here, so that we can avoid
+ * doing it twice over and also can look at the pf maps of
+ * other units without worrying about them not existing.
+ * This might consume some memory, but really saves CPU time. */
+ if (!unit_has_role(punit->type, L_EXPLORER)) {
+ update_unit_map(punit, UNIT_MAP_NORMAL);
+ } else {
+ update_unit_map(punit, UNIT_MAP_EXPLORER);
+ }
+ }
+ } unit_list_iterate_end;
ai_airlift(pplayer);
unit_list_iterate_safe(pplayer->units, punit) {
ai_manage_unit(pplayer, punit);
Index: common/unit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unit.c,v
retrieving revision 1.183
diff -u -r1.183 unit.c
--- common/unit.c 22 Sep 2003 16:54:09 -0000 1.183
+++ common/unit.c 27 Sep 2003 13:54:52 -0000
@@ -1477,6 +1477,7 @@
punit->bribe_cost = -1; /* flag value */
punit->transported_by = -1;
punit->pgr = NULL;
+ punit->pf = NULL;
punit->focus_status = FOCUS_AVAIL;
punit->ord_map = 0;
punit->ord_city = 0;
@@ -1495,7 +1496,9 @@
if (punit->pgr) {
free(punit->pgr->pos);
free(punit->pgr);
- punit->pgr = NULL;
+ }
+ if (punit->pf) {
+ free(punit->pf);
}
free(punit);
}
Index: common/unit.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/unit.h,v
retrieving revision 1.100
diff -u -r1.100 unit.h
--- common/unit.h 22 Sep 2003 16:54:09 -0000 1.100
+++ common/unit.h 27 Sep 2003 13:54:52 -0000
@@ -139,6 +139,7 @@
int transported_by;
int occupy; /* number of units that occupy transporter */
struct goto_route *pgr;
+ struct pf_map *pf; /* path-finding map */
};
/* Wrappers for accessing the goto destination of a unit. This goto_dest
--- ai/aitools.c 2003-09-27 18:24:33.000000000 +0200
+++ ../freeciv-audio/ai/aitools.c 2003-09-27 14:08:57.000000000 +0200
@@ -29,6 +29,7 @@
#include "player.h"
#include "shared.h"
#include "unit.h"
+#include "unittype.h"
#include "path_finding.h"
#include "pf_tools.h"
@@ -54,6 +55,36 @@
#include "aitools.h"
/**************************************************************************
+ Specialized create unit function that creates a unit with a pf_map
+ and veteran status.
+**************************************************************************/
+struct unit *make_ai_unit(struct player *pplayer, struct city *pcity,
+ Unit_Type_id unit_type)
+{
+ struct pf_parameter pf_param;
+ struct unit *punit = create_unit_virtual(pplayer, pcity, unit_type,
+ do_make_unit_veteran(pcity, unit_type));
+
+ pft_fill_default_parameter(&pf_param);
+ pft_fill_unit_parameter(&pf_param, punit);
+ pf_param.cached = TRUE;
+ punit->pf = pf_create_map(&pf_param);
+ assert(punit->pf);
+
+ return punit;
+}
+
+/**************************************************************************
+ Undo above.
+**************************************************************************/
+struct unit *destroy_ai_unit(struct unit *punit)
+{
+ pf_destroy_map(punit->pf);
+ punit->pf = NULL;
+ destroy_unit_virtual(punit);
+}
+
+/**************************************************************************
Amortize a want modified by the shields (build_cost) we risk losing.
We add the build time of the unit(s) we risk to amortize delay. The
build time is claculated as the build cost divided by the production
--- hmm 2003-09-27 18:28:18.000000000 +0200
+++ ai/aitools.h 2003-09-27 18:29:02.000000000 +0200
@@ -46,6 +46,9 @@
int value, int delay, int build_cost);
int stack_cost(struct unit *pdef);
+struct unit *make_ai_unit(struct player *pplayer, struct city *pcity,
+ Unit_Type_id unit_type);
+struct unit *destroy_ai_unit(struct unit *punit);
void update_unit_map(struct unit *punit, enum ai_unit_map maptype);
bool ai_unit_execute_path(struct unit *punit, struct pf_path *path);
bool ai_unit_gothere(struct unit *punit);
Index: ai/aidiplomat.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aidiplomat.c,v
retrieving revision 1.24
diff -u -r1.24 aidiplomat.c
--- ai/aidiplomat.c 8 Aug 2003 22:11:41 -0000 1.24
+++ ai/aidiplomat.c 27 Sep 2003 15:56:33 -0000
@@ -40,7 +40,6 @@
#include "citytools.h"
#include "cityturn.h"
#include "diplomats.h"
-#include "gotohand.h"
#include "maphand.h"
#include "settlers.h"
#include "unithand.h"
@@ -158,7 +157,7 @@
int want, loss, p_success, p_failure, time_to_dest;
int gain_incite = 0, gain_theft = 0, gain = 1;
int incite_cost;
- struct unit *punit = create_unit_virtual(pplayer, pcity, u, FALSE);
+ struct unit *punit = make_ai_unit(pplayer, pcity, u);
find_city_to_diplomat(pplayer, punit, &acity, &time_to_dest);
@@ -232,7 +231,7 @@
choice->choice = u;
BV_SET(ai->stats.diplomat_reservations, acity->id);
}
- destroy_unit_virtual(punit);
+ destroy_ai_unit(punit);
}
}
@@ -319,16 +318,20 @@
bool has_embassy;
int incite_cost = 0; /* incite cost */
bool dipldef; /* whether target is protected by diplomats */
+ struct pf_position pos;
assert(punit != NULL);
*ctarget = NULL;
*move_dist = -1;
- simple_unit_path_iterator(punit, pos) {
- struct city *acity = map_get_city(pos.x, pos.y);
+ pf_cache_reset(punit->pf);
+ while (pf_cached_next(punit->pf, &pos)) {
+ struct city *acity;
struct player *aplayer;
bool can_incite;
+ acity = map_get_city(pos.x, pos.y);
+
if (!acity) {
continue;
}
@@ -362,7 +365,7 @@
*move_dist = pos.total_MC;
break;
}
- } simple_unit_path_iterator_end;
+ }
}
/**************************************************************************
@@ -376,6 +379,7 @@
int best_urgency = 0;
struct city *ctarget = NULL;
struct city *pcity = map_get_city(punit->x, punit->y);
+ struct pf_position pos;
if (pcity
&& count_diplomats_on_tile(pcity->x, pcity->y) == 1
@@ -384,11 +388,13 @@
return pcity;
}
- simple_unit_path_iterator(punit, pos) {
- struct city *acity = map_get_city(pos.x, pos.y);
+ pf_cache_reset(punit->pf);
+ while (pf_cached_next(punit->pf, &pos)) {
+ struct city *acity;
struct player *aplayer;
int dipls, urgency;
+ acity = map_get_city(pos.x, pos.y);
if (!acity) {
continue;
}
@@ -421,7 +427,7 @@
/* squelch divide-by-zero */
best_dist = MAX(pos.total_MC, 1);
}
- } simple_unit_path_iterator_end;
+ }
return ctarget;
}
@@ -437,8 +443,11 @@
struct packet_diplomat_action packet;
int gold_avail = pplayer->economic.gold - pplayer->ai.est_upkeep;
struct ai_data *ai = ai_data_get(pplayer);
+ struct pf_position pos;
- simple_unit_overlap_path_iterator(punit, pos) {
+ update_unit_map(punit, UNIT_MAP_NORMAL);
+ pf_cache_reset(punit->pf);
+ while (pf_cached_next(punit->pf, &pos)) {
struct tile *ptile = map_get_tile(pos.x, pos.y);
bool threat = FALSE;
int newval, bestval = 0, cost;
@@ -496,10 +505,11 @@
/* Found someone! */
{
int x, y;
+ struct pf_path *path;
MAPSTEP(x, y, pos.x, pos.y, DIR_REVERSE(pos.dir_to_here));
-
- if (!ai_unit_goto(punit, x, y) || punit->moves_left <= 0) {
+ path = pf_get_path(punit->pf, pos.x, pos.y);
+ if (!ai_unit_execute_path(punit, path) || punit->moves_left <= 0) {
return FALSE;
}
}
@@ -521,7 +531,7 @@
" %d moves left", pos.x, pos.y, punit->moves_left);
return FALSE;
}
- } simple_unit_overlap_path_iterator_end;
+ }
return (punit->moves_left > 0);
}
@@ -549,6 +559,7 @@
/* Died or ran out of moves */
return;
}
+ update_unit_map(punit, UNIT_MAP_NORMAL);
/* If we are the only diplomat in a threatened city, then stay to defend */
pcity = map_get_city(punit->x, punit->y); /* we may have moved */
@@ -627,27 +638,23 @@
/* GOTO unless we want to stay */
if (!same_pos(punit->x, punit->y, ctarget->x, ctarget->y)) {
- if (!ai_unit_gothere(punit)) {
- return;
- }
- }
+ struct pf_path *path;
- /* Have we run out of moves? */
- if (punit->moves_left <= 0) {
- return;
- }
-
- /* Check if we can do something with our destination now. */
- if (punit->ai.ai_role == AIUNIT_ATTACK) {
- int dist = real_map_distance(punit->x, punit->y,
- goto_dest_x(punit), goto_dest_y(punit));
- UNIT_LOG(LOG_DIPLOMAT, punit, "attack, dist %d to %s (%s goto)",
- dist, ctarget ? ctarget->name : "(none)",
- punit->activity == ACTIVITY_GOTO ? "has" : "no");
- if (dist == 1) {
- /* Do our stuff */
- ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
- ai_diplomat_city(punit, ctarget);
+ path = pf_get_path(punit->pf, goto_dest_x(punit), goto_dest_y(punit));
+ if (ai_unit_execute_path(punit, path) && punit->moves_left > 0) {
+ /* Check if we can do something with our destination now. */
+ if (punit->ai.ai_role == AIUNIT_ATTACK) {
+ int dist = real_map_distance(punit->x, punit->y,
+ goto_dest_x(punit), goto_dest_y(punit));
+ UNIT_LOG(LOG_DIPLOMAT, punit, "attack, dist %d to %s (%s goto)",
+ dist, ctarget ? ctarget->name : "(none)",
+ punit->activity == ACTIVITY_GOTO ? "has" : "no");
+ if (dist == 1) {
+ /* Do our stuff */
+ ai_unit_new_role(punit, AIUNIT_NONE, -1, -1);
+ ai_diplomat_city(punit, ctarget);
+ }
+ }
}
}
}
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Freeciv-Dev] (PR#6293) new reusable & reentrant pf iterator,
Per I. Mathisen <=
|
|