[freeciv-ai] (PR#8777) Find ferry
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
<URL: http://rt.freeciv.org/Ticket/Display.html?id=8777 >
Here is the implementation of find_ferry function torn out from the new
settler code. It is tested using the old settler code and it works, see
settler4.gz. Now even settlers inland can find a ferry far away in the
seas to take them to a new continent.
New find_ferry should eventually replace all uses of find_boat.
Changes to server/settler.c are for testing only and can be removed.
G.
? ttt.gz
Index: ai/aitools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.c,v
retrieving revision 1.103
diff -u -r1.103 aitools.c
--- ai/aitools.c 4 May 2004 17:40:25 -0000 1.103
+++ ai/aitools.c 19 May 2004 17:41:46 -0000
@@ -97,8 +97,8 @@
This is a function to execute paths returned by the path-finding engine.
It is analogous to goto_route_execute, only much simpler.
- We use ai_unit_attack which means "move if target unoccupied, attack
- otherwise" and also brings our bodyguard along.
+ Brings our bodyguard along.
+ Returns FALSE only if died.
*************************************************************************/
bool ai_unit_execute_path(struct unit *punit, struct pf_path *path)
{
@@ -107,7 +107,6 @@
/* We start with i = 1 for i = 0 is our present position */
for (i = 1; i < path->length; i++) {
int x = path->positions[i].x, y = path->positions[i].y;
- bool result;
int id = punit->id;
/* We use ai_unit_move() for everything but the last step
@@ -115,12 +114,11 @@
* shows up. Any enemy on the target tile is expected to
* be our target and any attack there intentional. */
if (i == path->length - 1) {
- result = ai_unit_attack(punit, x, y);
+ (void) ai_unit_attack(punit, x, y);
} else {
- ai_unit_move(punit, x, y);
- result = (find_unit_by_id(id) != NULL);
+ (void) ai_unit_move(punit, x, y);
}
- if (!result) {
+ if (!find_unit_by_id(id)) {
/* Died... */
return FALSE;
}
@@ -206,7 +204,126 @@
/* What if we have a bodyguard, but don't need one? */
}
-#define LOGLEVEL_GOTHERE LOG_DEBUG
+/****************************************************************************
+ Combined cost function for a land unit looking for a ferry. The path
+ finding first goes over the continent and then into the ocean where we
+ actually look for ferry. Thus moves land-to-sea are allowed and moves
+ sea-to-land are not. A consequence is that we don't get into the cities
+ on other continent, which might station boats. This defficiency seems to
+ be impossible to fix with the current PF structure, so it has to be
+ accounted for in the actual ferry search function.
+
+ For movements sea-to-sea the cost is collected via the extra cost
+ call-back. Doesn't care for enemy/neutral tiles, these should be excluded
+ using a TB call-back.
+****************************************************************************/
+static int combined_land_sea_move(int x, int y, enum direction8 dir,
+ int x1, int y1,
+ struct pf_parameter *param)
+{
+ struct tile *src_tile = map_get_tile(x, y);
+ struct tile *tgt_tile = map_get_tile(x1, y1);
+ int move_cost;
+
+ if (is_ocean(tgt_tile->terrain)) {
+ /* Any-to-Sea */
+ move_cost = 0;
+ } else if (src_tile->terrain == T_OCEAN) {
+ /* Sea-to-Land */
+ move_cost = PF_IMPOSSIBLE_MC;
+ } else {
+ /* Land-to-Land */
+ move_cost = src_tile->move_cost[dir];
+ }
+
+ return move_cost;
+}
+
+/****************************************************************************
+ EC callback to account for the cost of sea moves by a ferry hurrying to
+ pick our unit up.
+****************************************************************************/
+static int sea_move(int x, int y, enum known_type known,
+ struct pf_parameter *param)
+{
+ if (is_ocean(map_get_tile(x, y)->terrain)) {
+ /* Approximately TURN_FACTOR / average ferry move rate */
+ return SINGLE_MOVE * PF_TURN_FACTOR / 16;
+ } else {
+ return 0;
+ }
+}
+
+/****************************************************************************
+ Proper and real PF function for finding a boat.
+
+ WRANING: Due to the nature of this function and PF (see the comment of
+ combined_land_sea_move), the path won't lead onto the boat itself.
+
+ FIXME: Actuall check the capacity.
+
+ FIXME: Lift the path off the PF map, otherwise ai_unit_got in ai_gothere
+ fails to move us (thinks that if the boat is off the coast, goto is
+ insane -- need to give it a beach instead).
+****************************************************************************/
+int find_ferry(struct unit *punit, int cap, struct pf_path **path)
+{
+ int best_turns = FC_INFINITY;
+ int best_id = 0;
+ struct pf_parameter param;
+ struct pf_map *search_map;
+
+ pft_fill_unit_parameter(¶m, punit);
+ param.turn_mode = TM_WORST_TIME;
+ param.get_TB = no_fights_or_unknown;
+ param.get_EC = sea_move;
+ param.get_MC = combined_land_sea_move;
+
+ search_map = pf_create_map(¶m);
+
+ pf_iterator(search_map, pos) {
+ int radius = (is_ocean(map_get_tile(pos.x, pos.y)->terrain) ? 1 : 0);
+
+ if (pos.turn + pos.total_EC/PF_TURN_FACTOR > best_turns) {
+ /* Won't find anything better */
+ /* FIXME: This condiion is somewhat dodgy */
+ break;
+ }
+
+ square_iterate(pos.x, pos.y, radius, x, y) {
+ struct tile *ptile = map_get_tile(x, y);
+
+ unit_list_iterate(ptile->units, aunit) {
+ if (is_ground_units_transport(aunit)
+ && (aunit->ai.passenger == FERRY_AVAILABLE
+ || aunit->ai.passenger == punit->id)) {
+ /* Turns for the unit to get to rendezvous pnt */
+ int u_turns = pos.turn;
+ /* Turns for the boat to get to the rendezvous pnt */
+ int f_turns = ((pos.total_EC / PF_TURN_FACTOR * 16
+ - aunit->moves_left)
+ / unit_type(aunit)->move_rate);
+ int turns = MAX(u_turns, f_turns);
+
+ if (turns < best_turns) {
+ UNIT_LOG(LOG_DEBUG, punit,
+ "Found a potential boat %s[%d](%d,%d)",
+ unit_type(aunit)->name, aunit->id, aunit->x, aunit->y);
+ if (path) {
+ *path = pf_next_get_path(search_map);
+ }
+ best_turns = turns;
+ best_id = aunit->id;
+ }
+ }
+ } unit_list_iterate_end;
+ } square_iterate_end;
+ } pf_iterator_end;
+ pf_destroy_map(search_map);
+ return(best_id);
+}
+
+#define LOGLEVEL_GOTHERE LOG_NORMAL
/****************************************************************************
This is ferry-enabled goto. Should not normally be used for non-ferried
units (i.e. planes or ships), use ai_unit_goto instead.
@@ -220,6 +337,7 @@
int dest_x, int dest_y)
{
struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard);
+ struct pf_path *path_to_ferry;
CHECK_UNIT(punit);
@@ -242,8 +360,7 @@
dest_x, dest_y);
if (boatid <= 0) {
- int bx, by;
- boatid = find_boat(pplayer, &bx, &by, 2);
+ boatid = find_ferry(punit, 2, &path_to_ferry);
}
ferryboat = find_unit_by_id(boatid);
@@ -259,21 +376,28 @@
}
ai_set_ferry(punit, ferryboat);
- if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)
- && (!is_at_coast(punit->x, punit->y)
- || is_tiles_adjacent(punit->x, punit->y,
- ferryboat->x, ferryboat->y))) {
+
+ if (!is_tiles_adjacent(punit->x, punit->y, ferryboat->x, ferryboat->y)
+ && !same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)
+ && !is_at_coast(punit->x, punit->y)) {
/* Go to the boat only if it cannot reach us or it's parked
* next to us. Otherwise just wait (boats are normally faster) */
- /* FIXME: agree on a rendez-vous point */
- /* FIXME: this can lose bodyguard */
+ /* TODO: agree on a rendez-vous point */
UNIT_LOG(LOGLEVEL_GOTHERE, punit, "going to boat[%d](%d,%d).", boatid,
ferryboat->x, ferryboat->y);
- if (!ai_unit_goto(punit, ferryboat->x, ferryboat->y)) {
+ /* The path can be amphibious so we will stop at the coast.
+ * It might not lead _onto_ the boat. */
+ if (!ai_unit_execute_path(punit, path_to_ferry)) {
/* Died. */
+ pf_destroy_path(path_to_ferry);
return FALSE;
}
}
+ pf_destroy_path(path_to_ferry);
+
+ if (is_tiles_adjacent(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
+ (void) ai_unit_move(punit, ferryboat->x, ferryboat->y);
+ }
if (!same_pos(punit->x, punit->y, ferryboat->x, ferryboat->y)) {
/* Didn't get to the boat */
Index: ai/aitools.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aitools.h,v
retrieving revision 1.41
diff -u -r1.41 aitools.h
--- ai/aitools.h 9 Jan 2004 16:59:49 -0000 1.41
+++ ai/aitools.h 19 May 2004 17:41:47 -0000
@@ -78,5 +78,7 @@
bool ai_wants_no_science(struct player *pplayer);
bool is_player_dangerous(struct player *pplayer, struct player *aplayer);
+int find_ferry(struct unit *punit, int cap, struct pf_path **path);
+
#endif /* FC__AITOOLS_H */
Index: ai/aiunit.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/ai/aiunit.c,v
retrieving revision 1.314
diff -u -r1.314 aiunit.c
--- ai/aiunit.c 4 May 2004 17:40:25 -0000 1.314
+++ ai/aiunit.c 19 May 2004 17:41:58 -0000
@@ -2320,8 +2320,6 @@
static void ai_manage_ferryboat(struct player *pplayer, struct unit *punit)
{
struct city *pcity;
- int oldbossid = -1; /* Loop prevention. If boss doesn't want to move,
- * neither do we. */
CHECK_UNIT(punit);
@@ -2343,17 +2341,20 @@
/* Do we have the passenger-in-charge on board? */
struct tile *ptile = map_get_tile(punit->x, punit->y);
- if (punit->ai.passenger > 0
- && !unit_list_find(&ptile->units, punit->ai.passenger)) {
- UNIT_LOG(LOGLEVEL_FERRY, punit,
- "lost passenger-in-charge[%d], resetting",
- punit->ai.passenger);
- ai_set_passenger(punit, NULL);
- } else if (oldbossid > 0) {
- /* Need to look for a new boss */
- UNIT_LOG(LOGLEVEL_FERRY, punit, "taking control back from [%d]",
- oldbossid);
- ai_set_passenger(punit, NULL);
+ if (punit->ai.passenger > 0) {
+ struct unit *psngr = find_unit_by_id(punit->ai.passenger);
+
+ /* If the passenger-in-charge is adjacent, we should wait for it to
+ * board. We will pass control to it later.
+ * FIXME: A possible side-effect: a boat will linger near a passenger
+ * which already landed. */
+ if (!psngr
+ || real_map_distance(punit->x, punit->y, psngr->x, psngr->y) > 1) {
+ UNIT_LOG(LOGLEVEL_FERRY, punit,
+ "lost passenger-in-charge[%d], resetting",
+ punit->ai.passenger);
+ punit->ai.passenger = 0;
+ }
}
if (punit->ai.passenger <= 0) {
@@ -2375,11 +2376,6 @@
}
} unit_list_iterate_end;
- if (candidate && candidate->id == oldbossid) {
- /* The boss decided to stay put on the ferry. We aren't moving. */
- return;
- }
-
if (candidate) {
UNIT_LOG(LOGLEVEL_FERRY, punit,
"appointed %s[%d] our passenger-in-charge",
@@ -2392,11 +2388,11 @@
}
if (punit->ai.passenger > 0) {
+ int bossid = punit->ai.passenger; /* Loop prevention */
struct unit *boss = find_unit_by_id(punit->ai.passenger);
int id = punit->id; /* To check if survived */
assert(boss != NULL);
- oldbossid = punit->ai.passenger;
if (unit_flag(boss, F_SETTLERS) || unit_flag(boss, F_CITIES)) {
/* Temporary hack: settlers all go in the end, forcing them
@@ -2411,6 +2407,11 @@
if (!find_unit_by_id(id) || punit->moves_left <= 0) {
return;
}
+ if (find_unit_by_id(bossid)
+ && same_pos(punit->x, punit->y, boss->x, boss->y)) {
+ /* The boss decided to stay put on the ferry. We aren't moving. */
+ return;
+ }
} else {
/* Cannot select a passenger-in-charge */
break;
Index: server/settlers.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/settlers.c,v
retrieving revision 1.180
diff -u -r1.180 settlers.c
--- server/settlers.c 26 Feb 2004 03:24:16 -0000 1.180
+++ server/settlers.c 19 May 2004 17:42:14 -0000
@@ -928,11 +928,11 @@
int food_upkeep = unit_food_upkeep(punit);
int food_cost = unit_foodbox_cost(punit);
- int boatid, bx = 0, by = 0; /* as returned by find_boat */
+ int boatid, bx = punit->x, by = punit->y; /* as returned by find_boat */
enemy_mask my_enemies = enemies[pplayer->player_no]; /* optimalization */
if (pplayer->ai.control)
- boatid = find_boat(pplayer, &bx, &by, 1); /* might need 2 for bodyguard */
+ boatid = find_ferry(punit, 2, NULL); /* might need 2 for bodyguard */
else
boatid = 0;
*ferryboat = unit_list_find(&(map_get_tile(punit->x, punit->y)->units),
boatid);
@@ -969,9 +969,7 @@
} else if (!goto_is_sane(punit, x, y, TRUE) ||
WARMAP_COST(x, y) > THRESHOLD * mv_rate) {
/* for Rome->Carthage */
- if (!is_ocean_near_tile(x, y)) {
- mv_cost = 9999;
- } else if (boatid != 0) {
+ if (boatid != 0) {
if (punit->id == 0 && mycity->id == boatid) {
w_virtual = TRUE;
}
@@ -1028,7 +1026,7 @@
}
#endif
- if (map_get_continent(x, y) != ucont && !nav_known && near >= 8) {
+ if (map_get_continent(x, y) != ucont && !nav_known && near >= 10) {
#ifdef REALLY_DEBUG_THIS
freelog(LOG_DEBUG,
"%s (%d, %d) rejected city at (%d, %d) to %d, newv = %d, moves
= %d" \
settle4.gz
Description: GNU Zip compressed data
- [freeciv-ai] (PR#8777) Find ferry,
Gregory Berkolaiko <=
|
|