[freeciv-ai] Re: [Freeciv-Dev] Re: (PR#11995) Stupid AI Creates Tall Sta
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: |
[freeciv-ai] Re: [Freeciv-Dev] Re: (PR#11995) Stupid AI Creates Tall Stacks |
From: |
"Benedict Adamson" <badamson@xxxxxxxxxxx> |
Date: |
Mon, 14 Mar 2005 12:57:09 -0800 |
Reply-to: |
bugs@xxxxxxxxxxx |
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=11995 >
I wrote:
...
>> Unfortunately, the AI movement function, ai_unit_goto, uses a warmap
>> (via do_unit_goto) rather than PF
...
>> so I suggest rewriting
>> ai_unit_goto and have that change reviewed and committed before
>> implementing a fix to prevent tall stacks.
...
Attached is the second version of my patch.
* ai_unit_goto uses PF instead of Warmap
* the ferry code uses an amphibious PF
* the PF code uses an extra-cost callback to discourage creation of tall
stacks.
diff -ru -Xvendor.freeciv.current/diff_ignore vendor.freeciv.current/ai/aiair.c
freeciv.PR11995/ai/aiair.c
--- vendor.freeciv.current/ai/aiair.c 2005-01-24 20:43:05.000000000 +0000
+++ freeciv.PR11995/ai/aiair.c 2005-03-14 20:45:46.000000000 +0000
@@ -20,6 +20,8 @@
#include "combat.h"
#include "log.h"
#include "map.h"
+#include "path_finding.h"
+#include "pf_tools.h"
#include "player.h"
#include "unit.h"
@@ -266,6 +268,22 @@
return found;
}
+/**************************************************************************
+ Go to specified destination but do not disturb existing role or activity
+ and do not clear the role's destination. Return FALSE iff we died.
+**************************************************************************/
+static bool ai_air_goto(struct unit *punit, struct tile *ptile)
+{
+ struct pf_parameter parameter;
+
+ pft_fill_unit_parameter(¶meter, punit);
+ /* Air units are immune to most risks, especially dangerous terrain,
+ * so no call to pft_avoid_risks() and no is_pos_dangerous function. */
+ parameter.is_pos_dangerous = NULL;
+ parameter.get_zoc = NULL;
+ return ai_unit_goto_constrained(punit, ptile, ¶meter);
+}
+
/************************************************************************
* Trying to manage bombers and stuff.
* If we are in the open {
@@ -302,13 +320,13 @@
punit->tile, punit->goto_tile,
pplayer) >= 0) {
/* It's an ok GOTO, just go there */
- ai_unit_goto(punit, punit->goto_tile);
+ ai_air_goto(punit, punit->goto_tile);
} else if (find_nearest_airbase(punit->tile, punit,
&refuel_tile)) {
/* Go refuelling */
punit->goto_tile = refuel_tile;
freelog(LOG_DEBUG, "Sent %s to refuel", unit_type(punit)->name);
- ai_unit_goto(punit, punit->goto_tile);
+ ai_air_goto(punit, punit->goto_tile);
} else {
if (punit->fuel == 1) {
freelog(LOG_DEBUG, "Oops, %s is fallin outta sky",
@@ -325,7 +343,7 @@
* TODO: separate attacking into a function, check for the best
* tile to attack from */
assert(punit->goto_tile != NULL);
- if (!ai_unit_goto(punit, punit->goto_tile)) {
+ if (!ai_air_goto(punit, punit->goto_tile)) {
return; /* died */
}
@@ -343,7 +361,7 @@
(map_get_city(dst_tile) ?
map_get_city(dst_tile)->name : ""));
punit->goto_tile = dst_tile;
- ai_unit_goto(punit, punit->goto_tile);
+ ai_air_goto(punit, punit->goto_tile);
} else {
freelog(LOG_DEBUG, "%s cannot find anything to kill and is staying put",
unit_type(punit)->name);
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/ai/aiexplorer.c freeciv.PR11995/ai/aiexplorer.c
--- vendor.freeciv.current/ai/aiexplorer.c 2005-01-29 21:57:33.000000000
+0000
+++ freeciv.PR11995/ai/aiexplorer.c 2005-03-14 20:45:46.000000000 +0000
@@ -342,7 +342,7 @@
if (best_tile != NULL) {
/* TODO: read the path off the map we made. Then we can make a path
* which goes beside the unknown, with a good EC callback... */
- if (!ai_unit_goto(punit, best_tile)) {
+ if (!ai_unit_goto_nocombat(punit, best_tile)) {
/* Died? Strange... */
return FALSE;
}
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/ai/aiferry.c freeciv.PR11995/ai/aiferry.c
--- vendor.freeciv.current/ai/aiferry.c 2005-01-24 20:43:05.000000000 +0000
+++ freeciv.PR11995/ai/aiferry.c 2005-03-14 20:45:46.000000000 +0000
@@ -270,7 +270,7 @@
Runs a few checks to determine if "boat" is a free boat that can carry
"cap" units of the same type as "punit" (the last one isn't implemented).
****************************************************************************/
-static bool is_boat_free(struct unit *boat, struct unit *punit, int cap)
+bool is_boat_free(struct unit *boat, struct unit *punit, int cap)
{
/* - Only ground-unit transporters are consider.
* - Units with orders are skipped (the AI doesn't control units with
@@ -386,6 +386,70 @@
/* ============================= go by boat ============================== */
+/**************************************************************************
+ Move a ferry to a specified destination. The destination may be a land tile.
+ in which case the ferry should stop on an adjacent tile.
+ Return FALSE iff we died.
+**************************************************************************/
+static bool aiferry_goto(struct unit *punit, struct tile *ptile)
+{
+ struct pf_parameter parameter;
+ struct pft_risk_cost risk_cost;
+
+ pft_fill_unit_overlap_param(¶meter, punit);
+ pft_avoid_risks(¶meter, &risk_cost, punit, NORMAL_STACKING_FEARFULNESS);
+
+ /* Must use TM_WORST_TIME, so triremes move safely */
+ parameter.turn_mode = TM_WORST_TIME;
+ /* Move as far along the path to the destination as we can;
+ * that is, ignore the presence of enemy units when computing the
+ * path */
+ parameter.get_zoc = NULL;
+ /* Ferries are not warships: */
+ parameter.get_TB = no_fights;
+
+ punit->goto_tile = ptile;
+ return ai_unit_goto_constrained(punit, ptile, ¶meter);
+}
+
+/**************************************************************************
+ Move a passenger on a ferry to a specified destination.
+ Return FALSE iff we died.
+**************************************************************************/
+static bool aiferry_goto_amphibious(struct unit *ferry,
+ struct unit *passenger, struct tile *ptile)
+{
+ struct pft_amphibious parameter;
+ struct pft_risk_cost land_risk_cost;
+ struct pft_risk_cost sea_risk_cost;
+
+ pft_fill_unit_parameter(¶meter.land, passenger);
+ /* Allow attacks over dangerous terrain and polar settlements */
+ parameter.land.is_pos_dangerous = NULL;
+ /* Use the ferry to go around danger areas: */
+ parameter.land.get_TB = no_intermediate_fights;
+ pft_avoid_risks(¶meter.land, &land_risk_cost, passenger,
+ NORMAL_STACKING_FEARFULNESS);
+
+ pft_fill_unit_parameter(¶meter.sea, ferry);
+ /* Ferries are not warships: */
+ parameter.sea.get_TB = no_fights;
+ pft_avoid_risks(¶meter.sea, &sea_risk_cost, ferry,
+ NORMAL_STACKING_FEARFULNESS);
+
+ pft_fill_amphibious_parameter(¶meter);
+
+ /* Move as far along the path to the destination as we can;
+ * that is, ignore the presence of enemy units when computing the
+ * path */
+ parameter.combined.get_zoc = NULL;
+ parameter.land.get_zoc = NULL;
+ parameter.sea.get_zoc = NULL;
+
+ ferry->goto_tile = ptile;
+ return ai_amphibious_goto_constrained(ferry, passenger, ptile, ¶meter);
+}
+
/****************************************************************************
This function is to be called if punit needs to use a boat to get to the
destination.
@@ -395,10 +459,6 @@
TODO: A big one is rendezvous points between units and boats. When this is
implemented, we won't have to be at the coast to ask for a boat to come
to us.
-
- You MUST have warmap created before calling this function in order for
- find_beachhead to work here. This requirement should be removed. For
- example, we can require that (dest_x,dest_y) is on a coast.
****************************************************************************/
bool aiferry_gobyboat(struct player *pplayer, struct unit *punit,
struct tile *dest_tile)
@@ -478,7 +538,6 @@
/* Check if we are the passenger-in-charge */
if (is_boat_free(ferryboat, punit, 0)) {
- struct tile *beach_tile; /* Destination for the boat */
struct unit *bodyguard = find_unit_by_id(punit->ai.bodyguard);
UNIT_LOG(LOGLEVEL_GOBYBOAT, punit,
@@ -486,30 +545,12 @@
ferryboat->id, ferryboat->moves_left, TILE_XY(dest_tile));
aiferry_psngr_meet_boat(punit, ferryboat);
- /* If the location is not accessible directly from sea
- * or is defended and we are not marines, we will need a
- * landing beach */
- if (!is_ocean_near_tile(dest_tile)
- ||((is_non_allied_city_tile(dest_tile, pplayer)
- || is_non_allied_unit_tile(dest_tile, pplayer))
- && !unit_flag(punit, F_MARINES))) {
- if (!find_beachhead(punit, dest_tile, &beach_tile)) {
- /* Nowhere to go */
- return FALSE;
- }
- UNIT_LOG(LOGLEVEL_GOBYBOAT, punit,
- "Found beachhead (%d,%d)", TILE_XY(beach_tile));
- } else {
- beach_tile = dest_tile;
- }
-
- ferryboat->goto_tile = beach_tile;
punit->goto_tile = dest_tile;
/* Grab bodyguard */
if (bodyguard
&& !same_pos(punit->tile, bodyguard->tile)) {
if (!goto_is_sane(bodyguard, punit->tile, TRUE)
- || !ai_unit_goto(punit, punit->tile)) {
+ || !ai_unit_goto_nocombat(bodyguard, punit->tile)) {
/* Bodyguard can't get there or died en route */
punit->ai.bodyguard = BODYGUARD_WANTED;
bodyguard = NULL;
@@ -529,14 +570,12 @@
assert(same_pos(punit->tile, bodyguard->tile));
handle_unit_load(pplayer, bodyguard->id, ferryboat->id);
}
- if (!ai_unit_goto(ferryboat, beach_tile)) {
+ if(!aiferry_goto_amphibious(ferryboat, punit, dest_tile)) {
/* died */
return FALSE;
}
- if (!is_tiles_adjacent(ferryboat->tile, beach_tile)
- && !same_pos(ferryboat->tile, beach_tile)) {
- /* We are in still transit */
- return FALSE;
+ if (same_pos(punit->tile, dest_tile)) {
+ handle_unit_activity_request(punit, ACTIVITY_IDLE);
}
} else {
/* Waiting for the boss to load and move us */
@@ -545,10 +584,6 @@
ferryboat->id, ferryboat->ai.passenger);
return FALSE;
}
-
- UNIT_LOG(LOGLEVEL_GOBYBOAT, punit, "Our boat has arrived "
- "[%d](moves left: %d)", ferryboat->id, ferryboat->moves_left);
- handle_unit_activity_request(punit, ACTIVITY_IDLE);
}
return TRUE;
@@ -841,7 +876,7 @@
if (aiferry_findcargo(punit)) {
UNIT_LOG(LOGLEVEL_FERRY, punit, "picking up cargo (moves left: %d)",
punit->moves_left);
- ai_unit_goto(punit, punit->goto_tile);
+ aiferry_goto(punit, punit->goto_tile);
return;
}
@@ -852,7 +887,7 @@
return;
} else {
UNIT_LOG(LOGLEVEL_FERRY, punit, "going to city that needs us");
- (void) ai_unit_goto(punit, punit->goto_tile);
+ (void) aiferry_goto(punit, punit->goto_tile);
return;
}
}
@@ -865,7 +900,7 @@
if (pcity) {
punit->goto_tile = pcity->tile;
UNIT_LOG(LOGLEVEL_FERRY, punit, "No work, going home");
- (void) ai_unit_goto(punit, pcity->tile);
+ (void) aiferry_goto(punit, pcity->tile);
}
}
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/ai/aiferry.h freeciv.PR11995/ai/aiferry.h
--- vendor.freeciv.current/ai/aiferry.h 2005-01-24 20:43:05.000000000 +0000
+++ freeciv.PR11995/ai/aiferry.h 2005-03-14 20:45:46.000000000 +0000
@@ -41,6 +41,8 @@
bool aiferry_gobyboat(struct player *pplayer, struct unit *punit,
struct tile *dst_tile);
+bool is_boat_free(struct unit *boat, struct unit *punit, int cap);
+
/*
* Main boat managing function. Gets units on board to where they want to
* go and then looks for new passengers or (if it fails) for a city which
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/ai/aihunt.c freeciv.PR11995/ai/aihunt.c
--- vendor.freeciv.current/ai/aihunt.c 2005-01-24 20:43:05.000000000 +0000
+++ freeciv.PR11995/ai/aihunt.c 2005-03-14 20:45:46.000000000 +0000
@@ -471,8 +471,15 @@
}
/* Go towards it. */
- if (!ai_unit_goto(punit, target->tile)) {
- return TRUE;
+ if (unit_type(punit)->move_type == LAND_MOVING) {
+ /* If necessary, use a ferry */
+ if (!ai_gothere(pplayer, punit, target->tile)) {
+ return TRUE;
+ }
+ } else {
+ if (!ai_unit_goto(punit, target->tile)) {
+ return TRUE;
+ }
}
/* Check if we can nuke it now */
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/ai/aitools.c freeciv.PR11995/ai/aitools.c
--- vendor.freeciv.current/ai/aitools.c 2005-03-11 22:35:41.000000000 +0000
+++ freeciv.PR11995/ai/aitools.c 2005-03-14 20:45:46.000000000 +0000
@@ -214,9 +214,6 @@
TODO: A big one is rendezvous points. When this is implemented, we won't
have to be at the coast to ask for a boat to come to us.
-
- You MUST have warmap created before calling this function in order for
- find_beachhead to work here. This requirement should be removed.
****************************************************************************/
bool ai_gothere(struct player *pplayer, struct unit *punit,
struct tile *dest_tile)
@@ -269,30 +266,223 @@
}
/**************************************************************************
- Go to specified destination but do not disturb existing role or activity
- and do not clear the role's destination. Return FALSE iff we died.
+ Returns the destination for a unit moving towards a given final destination.
+ That is, it gives a suitable waypoint, if necessary.
+ For example, aircraft need these waypoints to refuel.
+**************************************************************************/
+static struct tile *immediate_destination(struct unit *punit,
+ struct tile *dest_tile)
+{
+ if (!same_pos(punit->tile, dest_tile) && is_air_unit(punit)) {
+ struct tile *waypoint_tile = punit->goto_tile;
+ if (find_air_first_destination(punit, &waypoint_tile)) {
+ return waypoint_tile;
+ } else {
+ struct player *pplayer = unit_owner(punit);
+ freelog(LOG_VERBOSE, "Did not find an airroute for "
+ "%s's %s at (%d, %d) -> (%d, %d)",
+ pplayer->name, unit_type(punit)->name,
+ TILE_XY(punit->tile), TILE_XY(dest_tile));
+ /* Prevent take off */
+ return punit->tile;
+ }
+ }
+ /* else does not need waypoints */
+ return dest_tile;
+}
- FIXME: add some logging functionality to replace GOTO_LOG()
+/**************************************************************************
+ Move a unit along a path without disturbing its activity, role
+ or assigned destination
+ Return FALSE iff we died.
**************************************************************************/
-bool ai_unit_goto(struct unit *punit, struct tile *ptile)
+static bool follow_path(struct unit *punit, struct pf_path *path,
+ struct tile *ptile)
{
- enum goto_result result;
- struct tile *old_tile;
+ struct tile *old_tile = punit->goto_tile;
enum unit_activity activity = punit->activity;
-
- old_tile = punit->goto_tile; /* May be NULL. */
-
- CHECK_UNIT(punit);
- /* TODO: log error on same_pos with punit->x|y */
+ bool alive;
punit->goto_tile = ptile;
handle_unit_activity_request(punit, ACTIVITY_GOTO);
- result = do_unit_goto(punit, GOTO_MOVE_ANY, FALSE);
- if (result != GR_DIED) {
+ alive = ai_unit_execute_path(punit, path);
+ if (alive) {
+ handle_unit_activity_request(punit, ACTIVITY_IDLE);
+ send_unit_info(NULL, punit);
handle_unit_activity_request(punit, activity);
punit->goto_tile = old_tile; /* May be NULL. */
+ send_unit_info(NULL, punit);
+ }
+ return alive;
+}
+
+/**************************************************************************
+ Log the cost of travelling a path.
+**************************************************************************/
+static void log_path(struct unit *punit,
+ struct pf_path *path, struct pf_parameter *parameter)
+{
+ struct pf_position *last = pf_last_position(path);
+ const int cc = PF_TURN_FACTOR * last->total_MC
+ + parameter->move_rate * last->total_EC;
+ const int tc = cc / (PF_TURN_FACTOR *parameter->move_rate);
+ UNIT_LOG(LOG_DEBUG, punit, "path L=%d T=%d(%d) MC=%d EC=%d CC=%d",
+ path->length - 1, last->turn, tc,
+ last->total_MC, last->total_EC, cc);
+}
+
+/**************************************************************************
+ Go to specified destination, subject to given PF constraints,
+ but do not disturb existing role or activity
+ and do not clear the role's destination. Return FALSE iff we died.
+
+ parameter: the PF constraints on the computed path. The unit will move
+ as far along the computed path is it can; the movement code will impose
+ all the real constraints (ZOC, etc).
+**************************************************************************/
+bool ai_unit_goto_constrained(struct unit *punit, struct tile *ptile,
+ struct pf_parameter *parameter)
+{
+ bool alive = TRUE;
+ struct pf_map *map = NULL;
+ struct pf_path *path = NULL;
+
+ ptile = immediate_destination(punit, ptile);
+
+ if (same_pos(punit->tile, ptile)) {
+ /* Not an error; sometimes immediate_destination instructs the unit
+ * to stay here. For example, to refuel.*/
+ send_unit_info(NULL, punit);
+ return TRUE;
+ } else if (!goto_is_sane(punit, ptile, FALSE)) {
+ punit->activity = ACTIVITY_IDLE;
+ send_unit_info(NULL, punit);
+ return TRUE;
+ } else if(punit->moves_left == 0) {
+ send_unit_info(NULL, punit);
return TRUE;
}
- return FALSE;
+
+ map = pf_create_map(parameter);
+ path = pf_get_path(map, ptile);
+
+ if (path) {
+ log_path(punit, path, parameter);
+ alive = follow_path(punit, path, ptile);
+ } else {
+ UNIT_LOG(LOG_DEBUG, punit, "no path to destination");
+ }
+
+ pf_destroy_path(path);
+ pf_destroy_map(map);
+
+ return alive;
+}
+
+/**************************************************************************
+ Go to specified destination but do not disturb existing role or activity
+ and do not clear the role's destination. Return FALSE iff we died.
+**************************************************************************/
+bool ai_unit_goto(struct unit *punit, struct tile *ptile)
+{
+ struct pf_parameter parameter;
+ struct pft_risk_cost risk_cost;
+
+ pft_fill_unit_parameter(¶meter, punit);
+ pft_avoid_risks(¶meter, &risk_cost, punit, NORMAL_STACKING_FEARFULNESS);
+ /* Be optimisitic; allows attacks across dangerous terrain,
+ * and polar settlements. */
+ parameter.is_pos_dangerous = NULL;
+ /* Move as far along the path to the destination as we can;
+ * that is, ignore the presence of enemy units when computing the
+ * path */
+ parameter.get_zoc = NULL;
+ return ai_unit_goto_constrained(punit, ptile, ¶meter);
+}
+
+/**************************************************************************
+ Go to specified destination, but do not disturb existing role or activity,
+ and do not clear the role's destination, and avoid enemy units.
+ Suitable for non-combat units.
+ Return FALSE iff we died.
+**************************************************************************/
+bool ai_unit_goto_nocombat(struct unit *punit, struct tile *ptile)
+{
+ struct pf_parameter parameter;
+ struct pft_risk_cost risk_cost;
+
+ pft_fill_unit_parameter(¶meter, punit);
+ pft_avoid_risks(¶meter, &risk_cost, punit, NORMAL_STACKING_FEARFULNESS);
+ parameter.get_TB = no_fights;
+ /* Be optimisitic; allows retreat across dangerous terrain,
+ * and polar settlements. */
+ parameter.is_pos_dangerous = NULL;
+ /* Move as far along the path to the destination as we can;
+ * that is, ignore the presence of enemy units when computing the
+ * path */
+ parameter.get_zoc = NULL;
+ return ai_unit_goto_constrained(punit, ptile, ¶meter);
+}
+
+/**************************************************************************
+ Move a passenger on a ferry to a specified destination.
+ The passenger is assumed to be on the given ferry.
+ The destination may be inland, in which case the passenger will ride
+ the ferry to a beach head, disembark, then continue on land.
+ Return FALSE iff we died.
+**************************************************************************/
+bool ai_amphibious_goto_constrained(struct unit *ferry,
+ struct unit *passenger,
+ struct tile *ptile,
+ struct pft_amphibious *parameter)
+{
+ bool alive = TRUE;
+ struct player *pplayer = unit_owner(passenger);
+ struct pf_map *map = NULL;
+ struct pf_path *path = NULL;
+
+ assert(pplayer->ai.control);
+ assert(!unit_has_orders(passenger));
+
+ ptile = immediate_destination(passenger, ptile);
+
+ if (same_pos(passenger->tile, ptile)) {
+ /* Not an error; sometimes immediate_destination instructs the unit
+ * to stay here. For example, to refuel.*/
+ send_unit_info(NULL, passenger);
+ return TRUE;
+ } else if (passenger->moves_left == 0 && ferry->moves_left == 0) {
+ send_unit_info(NULL, passenger);
+ return TRUE;
+ }
+
+ map = pf_create_map(¶meter->combined);
+ path = pf_get_path(map, ptile);
+
+ if (path) {
+ log_path(passenger, path, ¶meter->combined);
+ /* Sea leg */
+ alive = follow_path(ferry, path, ptile);
+ if (alive && passenger->tile != ptile) {
+ pft_advance_path(path, passenger->tile);
+ if (!is_ocean(path->positions[1].tile->terrain)) {
+ UNIT_LOG(LOG_DEBUG, passenger, "Our boat has arrived "
+ "[%d](moves left: %d)", ferry->id, ferry->moves_left);
+ UNIT_LOG(LOG_DEBUG, passenger, "Disembarking to (%d,%d)",
+ TILE_XY(path->positions[1].tile));
+ /* Land leg */
+ alive = follow_path(passenger, path, ptile);
+ }
+ /* else at sea */
+ }
+ } else {
+ /* Not always an error; enemy units might block all paths. */
+ UNIT_LOG(LOG_DEBUG, passenger, "no path to destination");
+ }
+
+ pf_destroy_path(path);
+ pf_destroy_map(map);
+
+ return alive;
}
/**************************************************************************
@@ -478,7 +668,6 @@
bool alive;
CHECK_UNIT(punit);
- assert(unit_owner(punit)->ai.control);
assert(is_tiles_adjacent(punit->tile, ptile));
handle_unit_activity_request(punit, ACTIVITY_IDLE);
@@ -509,8 +698,6 @@
struct player *pplayer = unit_owner(punit);
CHECK_UNIT(punit);
- assert(unit_owner(punit)->ai.control);
- assert(is_tiles_adjacent(punit->tile, ptile));
/* if enemy, stop and let ai attack function take this case */
if (is_enemy_unit_tile(ptile, pplayer)
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/ai/aitools.h freeciv.PR11995/ai/aitools.h
--- vendor.freeciv.current/ai/aitools.h 2005-02-13 15:07:21.000000000 +0000
+++ freeciv.PR11995/ai/aitools.h 2005-03-14 20:45:46.000000000 +0000
@@ -21,6 +21,8 @@
struct ai_choice;
struct pf_path;
+struct pf_parameter;
+struct pft_amphibious;
#ifdef DEBUG
#define CHECK_UNIT(punit) \
@@ -44,7 +46,14 @@
bool ai_unit_execute_path(struct unit *punit, struct pf_path *path);
bool ai_gothere(struct player *pplayer, struct unit *punit,
struct tile *dst_tile);
+bool ai_unit_goto_constrained(struct unit *punit, struct tile *ptile,
+ struct pf_parameter *parameter);
+bool ai_amphibious_goto_constrained(struct unit *ferry,
+ struct unit *passenger,
+ struct tile *ptile,
+ struct pft_amphibious *parameter);
bool ai_unit_goto(struct unit *punit, struct tile *ptile);
+bool ai_unit_goto_nocombat(struct unit *punit, struct tile *ptile);
void ai_unit_new_role(struct unit *punit, enum ai_unit_task task,
struct tile *ptile);
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/ai/aiunit.c freeciv.PR11995/ai/aiunit.c
--- vendor.freeciv.current/ai/aiunit.c 2005-02-13 15:07:21.000000000 +0000
+++ freeciv.PR11995/ai/aiunit.c 2005-03-14 20:45:46.000000000 +0000
@@ -658,6 +658,7 @@
return 0;
}
+
/*************************************************************************
Look for worthy targets within a one-turn horizon.
*************************************************************************/
@@ -674,6 +675,10 @@
struct player *pplayer = unit_owner(punit);
pft_fill_unit_attack_param(¶meter, punit);
+ /* When trying to find rampage targets we ignore risks such as
+ * enemy units because we are looking for trouble!
+ * Hence no call to pft_avoid_risks()
+ */
tgt_map = pf_create_map(¶meter);
while (pf_next(tgt_map)) {
@@ -804,7 +809,7 @@
if (!same_pos(punit->tile, ptile)) {
if (goto_is_sane(punit, ptile, TRUE)) {
- if (!ai_unit_goto(punit, ptile)) {
+ if (!ai_gothere(pplayer, punit, ptile)) {
/* We died */
return;
}
@@ -813,21 +818,20 @@
ai_unit_new_role(punit, AIUNIT_NONE, NULL);
}
}
- /* I had these guys set to just fortify, which is so dumb. -- Syela
- * Instead we can attack adjacent units and maybe even pick up some free
- * cities! */
+ /* We might have stopped because of an enemy nearby.
+ * Perhaps we can kill it.*/
(void) ai_military_rampage(punit, BODYGUARD_RAMPAGE_THRESHOLD,
RAMPAGE_FREE_CITY_OR_BETTER);
}
/*************************************************************************
Tries to find a land tile adjacent to water and to our target
- (dest_x, dest_y). Prefers tiles which are more defensible and/or
+ (dest_tile). Prefers tiles which are more defensible and/or
where we will have moves left.
FIXME: It checks if the ocean tile is in our Zone of Control?!
**************************************************************************/
-bool find_beachhead(struct unit *punit, struct tile *dest_tile,
- struct tile **beachhead_tile)
+static bool find_beachhead(struct unit *punit, struct tile *dest_tile,
+ struct tile **beachhead_tile)
{
int ok, best = 0;
Terrain_type_id t;
@@ -872,32 +876,6 @@
return (best > 0);
}
-/**************************************************************************
-find_beachhead() works only when city is not further that 1 tile from
-the sea. But Sea Raiders might want to attack cities inland.
-So this finds the nearest land tile on the same continent as the city.
-**************************************************************************/
-static void find_city_beach(struct city *pc, struct unit *punit,
- struct tile **dest_tile)
-{
- struct tile *best_tile = punit->tile;
- int dist = 100;
- int search_dist = real_map_distance(pc->tile, punit->tile) - 1;
-
- CHECK_UNIT(punit);
-
- square_iterate(punit->tile, search_dist, tile1) {
- if (map_get_continent(tile1) == map_get_continent(pc->tile)
- && real_map_distance(punit->tile, tile1) < dist) {
-
- dist = real_map_distance(punit->tile, tile1);
- best_tile = tile1;
- }
- } square_iterate_end;
-
- *dest_tile = best_tile;
-}
-
/*************************************************************************
Does the unit with the id given have the flag L_DEFEND_GOOD?
**************************************************************************/
@@ -1759,6 +1737,7 @@
int id = punit->id;
int ct = 10;
struct city *pcity = NULL;
+ struct tile *start_tile = punit->tile;
CHECK_UNIT(punit);
@@ -1798,6 +1777,14 @@
dest_tile->x, dest_tile->y);
if (!ai_gothere(pplayer, punit, dest_tile)) {
/* Died or got stuck */
+ if (find_unit_by_id(id)
+ && punit->moves_left && punit->tile != start_tile) {
+ /* Got stuck. Possibly because of adjacency to an
+ * enemy unit. Perhaps we are in luck and are now next to a
+ * tempting target? Let's find out... */
+ (void) ai_military_rampage(punit,
+ RAMPAGE_ANYTHING, RAMPAGE_ANYTHING);
+ }
return;
}
if (punit->moves_left <= 0) {
@@ -1848,22 +1835,46 @@
UNIT_LOG(LOG_DEBUG, punit, "nothing to do - no more exploring either");
}
} else {
+ /* Barbarian */
/* You can still have some moves left here, but barbarians should
not sit helplessly, but advance towards nearest known enemy city */
struct city *pc;
- struct tile *ftile;
if ((pc = dist_nearest_city(pplayer, punit->tile, FALSE, TRUE))) {
if (!is_ocean(map_get_terrain(punit->tile))) {
UNIT_LOG(LOG_DEBUG, punit, "Barbarian marching to conquer %s",
pc->name);
(void) ai_gothere(pplayer, punit, pc->tile);
} else {
- /* sometimes find_beachhead is not enough */
- if (!find_beachhead(punit, pc->tile, &ftile)) {
- find_city_beach(pc, punit, &ftile);
- }
- UNIT_LOG(LOG_DEBUG, punit, "Barbarian sailing to %s", pc->name);
- (void) ai_gothere(pplayer, punit, ftile);
+ struct unit *ferry = NULL;
+ unit_list_iterate(punit->tile->units, aunit) {
+ if (is_boat_free(aunit, punit, 2)) {
+ ferry = aunit;
+ break;
+ }
+ } unit_list_iterate_end;
+ if (ferry) {
+ struct pft_amphibious parameter;
+ UNIT_LOG(LOG_DEBUG, punit, "Barbarian sailing to conquer %s",
+ pc->name);
+
+ pft_fill_unit_parameter(¶meter.land, punit);
+ /* Allow attacks over dangerous terrain */
+ parameter.land.is_pos_dangerous = NULL;
+ /* Use the ferry to go around danger areas: */
+ parameter.land.get_TB = no_intermediate_fights;
+
+ pft_fill_unit_parameter(¶meter.sea, ferry);
+ /* Ferries are not warships: */
+ parameter.sea.get_TB = no_fights;
+
+ pft_fill_amphibious_parameter(¶meter);
+ /* Barbarians bravely/stupidly ignore risks,
+ * so no call to pft_avoid_risks */
+ (void)ai_amphibious_goto_constrained(punit, punit,
+ pc->tile, ¶meter);
+ } else {
+ UNIT_LOG(LOG_ERROR, punit, "unable to find barbarian ferry");
+ }
}
}
}
@@ -1905,7 +1916,7 @@
if (punit->moves_left == 0) {
return;
}
- (void) ai_unit_goto(punit, pcity->tile);
+ (void) ai_unit_goto_nocombat(punit, pcity->tile);
} else {
/*
* We really don't want to just drop all caravans in immediately.
@@ -1944,7 +1955,7 @@
if (punit->moves_left == 0) {
return;
}
- (void) ai_unit_goto(punit, pcity->tile);
+ (void) ai_unit_goto_nocombat(punit, pcity->tile);
} else {
handle_unit_establish_trade(pplayer, punit->id);
}
@@ -1987,7 +1998,7 @@
safe = find_nearest_safe_city(punit);
if (safe) {
UNIT_LOG(LOGLEVEL_RECOVERY, punit, "going to %s to recover", safe->name);
- if (!ai_unit_goto(punit, safe->tile)) {
+ if (ai_unit_goto_nocombat(punit, safe->tile)) {
freelog(LOGLEVEL_RECOVERY, "died trying to hide and recover");
return;
}
@@ -2344,7 +2355,7 @@
}
last_tile = leader->tile;
- (void) ai_unit_goto(leader, safest_tile);
+ (void) ai_unit_goto_nocombat(leader, safest_tile);
if (same_pos(leader->tile, last_tile)) {
/* Deep inside the goto handling code, in
server/unithand.c::handle_unite_move_request(), the server
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/ai/aiunit.h freeciv.PR11995/ai/aiunit.h
--- vendor.freeciv.current/ai/aiunit.h 2005-02-13 15:07:21.000000000 +0000
+++ freeciv.PR11995/ai/aiunit.h 2005-03-14 20:45:46.000000000 +0000
@@ -62,8 +62,6 @@
Unit_Type_id enemy_type);
int find_something_to_kill(struct player *pplayer, struct unit *punit,
struct tile **ptile);
-bool find_beachhead(struct unit *punit, struct tile *dst_tile,
- struct tile **ptile);
int build_cost_balanced(Unit_Type_id type);
int unittype_att_rating(Unit_Type_id type, int veteran,
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/common/aicore/path_finding.c
freeciv.PR11995/common/aicore/path_finding.c
--- vendor.freeciv.current/common/aicore/path_finding.c 2005-01-24
20:45:28.000000000 +0000
+++ freeciv.PR11995/common/aicore/path_finding.c 2005-03-14
20:45:45.000000000 +0000
@@ -222,11 +222,15 @@
/* 2 means can move unrestricted from/into it,
* 1 means can move unrestricted into it, but not necessarily from it */
node->zoc_number = (my_zoc ? 2 : (occupied ? 1 : 0));
+ } else {
+ node->zoc_number = 0;
}
/* Evaluate the extra cost of the destination */
if (params->get_EC) {
node->extra_tile = params->get_EC(ptile, node->node_known_type, params);
+ } else {
+ node->extra_tile = 0;
}
}
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/common/aicore/pf_tools.c
freeciv.PR11995/common/aicore/pf_tools.c
--- vendor.freeciv.current/common/aicore/pf_tools.c 2005-03-11
22:35:41.000000000 +0000
+++ freeciv.PR11995/common/aicore/pf_tools.c 2005-03-14 20:45:45.000000000
+0000
@@ -18,7 +18,11 @@
#include <assert.h>
#include <string.h>
+#include "combat.h"
+#include "map.h"
#include "mem.h"
+#include "packets.h"
+#include "player.h"
#include "pf_tools.h"
@@ -26,6 +30,87 @@
static void pft_fill_unit_default_parameter(struct pf_parameter *parameter,
struct unit *punit);
+
+/*********************************************************************
+ The value of the units belonging to a given player on a given tile.
+*********************************************************************/
+static int stack_value(const struct tile *ptile,
+ const struct player *pplayer)
+{
+ int cost = 0;
+ if (is_stack_vulnerable(ptile)) {
+ unit_list_iterate(ptile->units, punit) {
+ if (unit_owner(punit) == pplayer) {
+ cost += unit_build_shield_cost(punit->type);
+ }
+ } unit_list_iterate_end;
+ }
+ return cost;
+}
+
+/*********************************************************************
+ How dangerous would it be stop on a particular tile,
+ because of enemy attacks,
+ expressed as the probability of being killed.
+*********************************************************************/
+static double p_killed_at(const struct tile *ptile,
+ struct pft_risk_cost *risk_cost,
+ struct pf_parameter *param)
+{
+ double db;
+ /* Compute the basic probability */
+ /* WAG */
+ double p = is_ocean(ptile->terrain)? 0.05: 0.15;
+
+ /* If we are on defensive terrain, we are more likely to survive */
+ db = get_tile_type(ptile->terrain)->defense_bonus;
+ if (map_has_special(ptile, S_RIVER)) {
+ db += (db * terrain_control.river_defense_bonus) / 100;
+ }
+ p *= 10.0 / db;
+
+ return p;
+}
+
+/*********************************************************************
+ Stack risk cost. How undesirable is passing through a tile
+ because of risks?
+ Weight by the cost of destruction, for risks that can kill the unit.
+*********************************************************************/
+static int stack_risk(const struct tile *ptile,
+ struct pft_risk_cost *risk_cost,
+ struct pf_parameter *param)
+{
+ double risk = 0;
+ /* Compute the risk of destruction, assuming we will stop at this tile */
+ const double value = risk_cost->base_value
+ + stack_value(ptile, param->owner);
+ const double p_killed = p_killed_at(ptile, risk_cost, param);
+ double danger = value * p_killed;
+ if (terrain_has_flag(ptile->terrain, TER_UNSAFE)) {
+ danger += risk_cost->unsafe_terrain_cost;
+ }
+ if (is_ocean(ptile->terrain) && !is_safe_ocean(ptile)) {
+ danger += risk_cost->ocean_cost;
+ }
+
+ /* Adjust for the fact that we might not stop at this tile,
+ * and for our fearfulness */
+ risk += danger * risk_cost->fearfulness;
+
+ /* Adjust for the risk that we migt become stuck (for an indefinite period)
+ * if we enter or try to enter the tile. */
+ if (risk_cost->enemy_zoc_cost != 0
+ && (is_non_allied_city_tile(ptile, param->owner)
+ || !is_my_zoc(param->owner, ptile)
+ || is_non_allied_unit_tile(ptile, param->owner))) {
+ /* We could become stuck. */
+ risk += risk_cost->enemy_zoc_cost;
+ }
+
+ return risk;
+}
+
/* ===================== Move Cost Callbacks ========================= */
/*************************************************************
@@ -36,10 +121,8 @@
static int seamove(const struct tile *ptile, enum direction8 dir,
const struct tile *ptile1, struct pf_parameter *param)
{
- /* MOVE_COST_FOR_VALID_SEA_STEP means ships can move between */
- if (ptile->move_cost[dir] == MOVE_COST_FOR_VALID_SEA_STEP
- || is_non_allied_unit_tile(ptile1, param->owner)
- || is_non_allied_city_tile(ptile1, param->owner)) {
+ if (is_ocean(ptile1->terrain) || ptile1->city
+ || is_non_allied_unit_tile(ptile1, param->owner)) {
return SINGLE_MOVE;
} else {
return PF_IMPOSSIBLE_MC;
@@ -323,6 +406,49 @@
#endif
+/*************************************************************
+ A cost function for amphibious movement.
+*************************************************************/
+static int amphibious_move(const struct tile *ptile, enum direction8 dir,
+ const struct tile *ptile1,
+ struct pf_parameter *param)
+{
+ const bool ocean = is_ocean(ptile->terrain);
+ const bool ocean1 = is_ocean(ptile1->terrain);
+ struct pft_amphibious *amphibious = (struct pft_amphibious *)param->data;
+ int cost, scale;
+
+ if (ocean && ocean1) {
+ /* Sea move */
+ cost = amphibious->sea.get_MC(ptile, dir, ptile1, &hibious->sea);
+ scale = amphibious->sea_scale;
+ } else if (ocean && is_allied_city_tile(ptile1, param->owner)) {
+ /* Entering port */
+ cost = amphibious->sea.get_MC(ptile, dir, ptile1, &hibious->sea);
+ scale = amphibious->sea_scale;
+ } else if (ocean) {
+ /* Disembark; use land movement function to handle F_MARINES */
+ cost = amphibious->land.get_MC(ptile, dir, ptile1, &hibious->land);
+ scale = amphibious->land_scale;
+ } else if (is_allied_city_tile(ptile, param->owner) && ocean1) {
+ /* Leaving port */
+ cost = amphibious->sea.get_MC(ptile, dir, ptile1, &hibious->sea);
+ scale = amphibious->sea_scale;
+ } else if (ocean1) {
+ /* Now we have disembarked, our ferry can not help us */
+ cost = PF_IMPOSSIBLE_MC;
+ scale = amphibious->land_scale;
+ } else {
+ /* land move */
+ cost = amphibious->land.get_MC(ptile, dir, ptile1, &hibious->land);
+ scale = amphibious->land_scale;
+ }
+ if (cost != PF_IMPOSSIBLE_MC) {
+ cost *= scale;
+ }
+ return cost;
+}
+
/* ===================== Extra Cost Callbacks ======================== */
/*********************************************************************
@@ -342,6 +468,50 @@
}
#endif
+/*********************************************************************
+ Extra cost call back to avoid creating tall stacks.
+ By setting this as an extra-cost call-back, paths will avoid tall stacks.
+ Avoiding tall stacks *all* along a path is useful because a unit following a
+ path might have to stop early because of ZoC or random movement.
+*********************************************************************/
+static int prefer_short_stacks(const struct tile *ptile,
+ enum known_type known,
+ struct pf_parameter *param)
+{
+ return stack_risk(ptile, (struct pft_risk_cost *)param->data, param);
+}
+
+/*********************************************************************
+ Extra cost call back for amphibious movement
+*********************************************************************/
+static int amphibious_extra_cost(const struct tile *ptile,
+ enum known_type known,
+ struct pf_parameter *param)
+{
+ struct pft_amphibious *amphibious = (struct pft_amphibious *)param->data;
+ const bool ocean = is_ocean(ptile->terrain);
+ int cost, scale;
+ if (known == TILE_UNKNOWN) {
+ /* We can travel almost anywhere */
+ cost = SINGLE_MOVE;
+ scale = MAX(amphibious->sea_scale, amphibious->land_scale);
+ } else if (ocean && amphibious->sea.get_EC) {
+ cost = amphibious->sea.get_EC(ptile, known, &hibious->sea);
+ scale = amphibious->sea_scale;
+ } else if (!ocean && amphibious->land.get_EC) {
+ cost = amphibious->land.get_EC(ptile, known, &hibious->land);
+ scale = amphibious->land_scale;
+ } else {
+ cost = 0;
+ scale = 1;
+ }
+
+ if (cost != PF_IMPOSSIBLE_MC) {
+ cost *= scale;
+ }
+ return cost;
+}
+
/* ===================== Tile Behaviour Callbacks ==================== */
@@ -390,12 +560,44 @@
return TB_NORMAL;
}
+/**********************************************************************
+ PF callback to prohibit attacking anyone, except at the destination.
+***********************************************************************/
+enum tile_behavior no_intermediate_fights(const struct tile *ptile,
+ enum known_type known,
+ struct pf_parameter *param)
+{
+ if (is_non_allied_unit_tile(ptile, param->owner)
+ || is_non_allied_city_tile(ptile, param->owner)) {
+ return TB_DONT_LEAVE;
+ }
+ return TB_NORMAL;
+}
+
+/*********************************************************************
+ A callback for amphibious movement
+*********************************************************************/
+static enum tile_behavior amphibious_behaviour(const struct tile *ptile,
+ enum known_type known,
+ struct pf_parameter *param)
+{
+ struct pft_amphibious *amphibious = (struct pft_amphibious *)param->data;
+ const bool ocean = is_ocean(ptile->terrain);
+ if (ocean && amphibious->sea.get_TB) {
+ return amphibious->sea.get_TB(ptile, known, &hibious->sea);
+ } else if (!ocean && amphibious->land.get_TB) {
+ return amphibious->land.get_TB(ptile, known, &hibious->land);
+ }
+ return TB_NORMAL;
+}
/* ===================== Postion Dangerous Callbacks ================ */
/**********************************************************************
An example of position-dangerous callback. For triremes.
FIXME: it cheats.
+ Allow one move onto land (for use for ferries and land
+ bombardment)
***********************************************************************/
static bool trireme_is_pos_dangerous(const struct tile *ptile,
enum known_type known,
@@ -404,8 +606,10 @@
/* We test TER_UNSAFE even though under the current ruleset there is no
* way for a trireme to be on a TER_UNSAFE tile. */
/* Unsafe or unsafe-ocean tiles without cities are dangerous. */
- return ((terrain_has_flag(ptile->terrain, TER_UNSAFE)
- || (is_ocean(ptile->terrain) && !is_safe_ocean(ptile)))
+ /* Pretend all land tiles are safe. */
+ return (is_ocean(ptile->terrain)
+ && (terrain_has_flag(ptile->terrain, TER_UNSAFE)
+ || (is_ocean(ptile->terrain) && !is_safe_ocean(ptile)))
&& ptile->city == NULL);
}
/****************************************************************************
@@ -432,7 +636,23 @@
}
/**********************************************************************
- Position-dangerous callback for all units other than triremes.
+ Position-dangerous callback for sea units other than triremes.
+ Allow one move onto land (for use for ferries and land
+ bombardment)
+***********************************************************************/
+static bool is_overlap_pos_dangerous(const struct tile *ptile,
+ enum known_type known,
+ struct pf_parameter *param)
+{
+ /* Unsafe tiles without cities are dangerous. */
+ /* Pretend all land tiles are safe. */
+ return (is_ocean(ptile->terrain)
+ && terrain_has_flag(ptile->terrain, TER_UNSAFE)
+ && ptile->city == NULL);
+}
+
+/**********************************************************************
+ Position-dangerous callback for typical units.
***********************************************************************/
static bool is_pos_dangerous(const struct tile *ptile, enum known_type known,
struct pf_parameter *param)
@@ -442,6 +662,23 @@
&& ptile->city == NULL);
}
+/**********************************************************************
+ Position-dangerous callback for amphibious movement.
+***********************************************************************/
+static bool amphibious_is_pos_dangerous(const struct tile *ptile,
+ enum known_type known,
+ struct pf_parameter *param)
+{
+ struct pft_amphibious *amphibious = (struct pft_amphibious *)param->data;
+ const bool ocean = is_ocean(ptile->terrain);
+ if (ocean && amphibious->sea.is_pos_dangerous) {
+ return amphibious->sea.is_pos_dangerous(ptile, known, param);
+ } else if (!ocean && amphibious->land.is_pos_dangerous) {
+ return amphibious->land.is_pos_dangerous(ptile, known, param);
+ }
+ return FALSE;
+}
+
/* ===================== Tools for filling parameters =============== */
/**********************************************************************
@@ -511,15 +748,31 @@
void pft_fill_unit_overlap_param(struct pf_parameter *parameter,
struct unit *punit)
{
+ const bool trireme_danger = unit_flag(punit, F_TRIREME)
+ && base_trireme_loss_pct(unit_owner(punit), punit) > 0;
+ const bool danger = base_unsafe_terrain_loss_pct(unit_owner(punit), punit)
+ > 0;
+
pft_fill_unit_default_parameter(parameter, punit);
switch (unit_type(punit)->move_type) {
case LAND_MOVING:
parameter->get_MC = land_overlap_move;
parameter->get_TB = dont_cross_ocean;
+
+ assert(!trireme_danger);
+ if (danger) {
+ parameter->is_pos_dangerous = is_pos_dangerous;
+ }
break;
case SEA_MOVING:
parameter->get_MC = sea_overlap_move;
+
+ if (trireme_danger) {
+ parameter->is_pos_dangerous = trireme_is_pos_dangerous;
+ } else if (danger) {
+ parameter->is_pos_dangerous = is_overlap_pos_dangerous;
+ }
break;
case AIR_MOVING:
case HELI_MOVING:
@@ -527,13 +780,6 @@
}
parameter->get_zoc = NULL;
-
- if (unit_flag(punit, F_TRIREME)
- && base_trireme_loss_pct(unit_owner(punit), punit) > 0) {
- parameter->is_pos_dangerous = trireme_is_pos_dangerous;
- } else if (base_unsafe_terrain_loss_pct(unit_owner(punit), punit) > 0) {
- parameter->is_pos_dangerous = is_pos_dangerous;
- }
}
/**********************************************************************
@@ -568,6 +814,35 @@
}
/**********************************************************************
+ Fill parameters for combined sea-land movement.
+ This is suitable for the case of a land unit riding a ferry.
+ The starting position of the ferry is taken to be the starting position for
+ the PF. The passenger is assumed to initailly be on the given ferry.
+ One complexity of amphibious movement is that the movement rate on land
+ might be different from that at sea. We therefore scale up the movement rates
+ (and the corresponding movement consts) to the product of the two rates.
+***********************************************************************/
+void pft_fill_amphibious_parameter(struct pft_amphibious *parameter)
+{
+ int move_rate;
+ parameter->combined = parameter->sea;
+ move_rate = parameter->land.move_rate * parameter->sea.move_rate;
+ parameter->land_scale = move_rate / parameter->land.move_rate;
+ parameter->sea_scale = move_rate / parameter->sea.move_rate;
+ parameter->combined.moves_left_initially *= parameter->sea_scale;
+ parameter->combined.move_rate = move_rate;
+ /* To ensure triremes behave correctly: */
+ parameter->combined.turn_mode = TM_WORST_TIME;
+ parameter->combined.get_MC = amphibious_move;
+ parameter->combined.get_TB = amphibious_behaviour;
+ parameter->combined.get_EC = amphibious_extra_cost;
+ parameter->combined.is_pos_dangerous = amphibious_is_pos_dangerous;
+ BV_CLR_ALL(parameter->combined.unit_flags);
+
+ parameter->combined.data = parameter;
+}
+
+/**********************************************************************
Fill general use parameters to defaults
***********************************************************************/
static void pft_fill_unit_default_parameter(struct pf_parameter *parameter,
@@ -624,3 +899,59 @@
}
return dest_path;
}
+
+/**********************************************************************
+ Remove the part of a path leading up to a given tile.
+ The given tile must be on the path.
+***********************************************************************/
+void pft_advance_path(struct pf_path *path,
+ struct tile *ptile)
+{
+ int i;
+ struct pf_position *new_positions;
+ for(i = 0; i < path->length; i++) {
+ if (path->positions[i].tile == ptile) {
+ break;
+ }
+ }
+ assert(i < path->length);
+ path->length -= i;
+ new_positions =
+ fc_malloc(sizeof(*path->positions) * path->length);
+ memcpy(new_positions, path->positions + i,
+ path->length * sizeof(*path->positions));
+ free(path->positions);
+ path->positions = new_positions;
+}
+
+/**********************************************************************
+ Set callbacks to favour paths that do not create tall stacks
+ or cross dangerous tiles.
+***********************************************************************/
+void pft_avoid_risks(struct pf_parameter *parameter,
+ struct pft_risk_cost *risk_cost,
+ struct unit *punit,
+ const double fearfulness)
+{
+ const struct player *pplayer = unit_owner(punit);
+ /* If we stay a short time on each tile, the danger of each individual tile
+ * is reduced. If we do not do this,
+ * we will not favour longer but faster routs. */
+ const double linger_fraction = (double)SINGLE_MOVE / parameter->move_rate;
+
+ parameter->data = risk_cost;
+ parameter->get_EC = prefer_short_stacks;
+ parameter->turn_mode = TM_WORST_TIME;
+ risk_cost->base_value = unit_build_shield_cost(punit->type);
+ risk_cost->fearfulness = fearfulness * linger_fraction;
+ if (unit_flag(punit, F_TRIREME)) {
+ risk_cost->ocean_cost = risk_cost->base_value
+ * (double)base_trireme_loss_pct(pplayer, punit)
+ / 100.0;
+ } else {
+ risk_cost->ocean_cost = 0;
+ }
+ risk_cost->unsafe_terrain_cost = risk_cost->base_value
+ * (double)base_unsafe_terrain_loss_pct(pplayer, punit) / 100.0;
+ risk_cost->enemy_zoc_cost = PF_TURN_FACTOR * 20;
+}
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/common/aicore/pf_tools.h
freeciv.PR11995/common/aicore/pf_tools.h
--- vendor.freeciv.current/common/aicore/pf_tools.h 2005-01-24
20:43:03.000000000 +0000
+++ freeciv.PR11995/common/aicore/pf_tools.h 2005-03-14 20:45:45.000000000
+0000
@@ -15,19 +15,71 @@
#include "path_finding.h"
+
+
+/*
+ * WAGs: how hard to avoid tall stacks of units.
+ * Pass as fearfulness values to pft_avoid_risks.
+ */
+#define NORMAL_STACKING_FEARFULNESS ((double)PF_TURN_FACTOR / 36.0)
+
+/*
+ * Use to create 'amphibious' paths. An amphibious path starts on a sea tile,
+ * perhaps goes over some other sea tiles, then perhaps goes over some land
+ * tiles. This is suitable for a land unit riding on a ferry.
+ * If you want, you can use different call-backs for the land and sea legs,
+ * by changing the 'land' and 'sea' fields.
+ * Initialise the 'land' and 'sea' fields, then initialise the other
+ * fields using using pft_fill_amphibious_parameter().
+ * Give the 'combined' field to pf_create_map to create a map
+ * for finding the path.
+ */
+struct pft_amphibious
+{
+ struct pf_parameter combined;
+ struct pf_parameter land;
+ struct pf_parameter sea;
+ int land_scale;
+ int sea_scale;
+};
+
+/*
+ * Initialise using pft_avoid_risks()
+ */
+struct pft_risk_cost
+{
+ double base_value;
+ double fearfulness;
+ double ocean_cost;
+ double unsafe_terrain_cost;
+ double enemy_zoc_cost;
+};
+
+
+
struct pf_path *pft_concat(struct pf_path *dest_path,
const struct pf_path *src_path);
+void pft_advance_path(struct pf_path *path,
+ struct tile *ptile);
void pft_fill_unit_parameter(struct pf_parameter *parameter,
struct unit *punit);
void pft_fill_unit_overlap_param(struct pf_parameter *parameter,
struct unit *punit);
void pft_fill_unit_attack_param(struct pf_parameter *parameter,
struct unit *punit);
+void pft_avoid_risks(struct pf_parameter *parameter,
+ struct pft_risk_cost *risk_cost,
+ struct unit *punit,
+ const double fearfulness);
+void pft_fill_amphibious_parameter(struct pft_amphibious *parameter);
enum tile_behavior no_fights_or_unknown(const struct tile *ptile,
enum known_type known,
struct pf_parameter *param);
enum tile_behavior no_fights(const struct tile *ptile, enum known_type known,
struct pf_parameter *param);
+enum tile_behavior no_intermediate_fights(const struct tile *ptile,
+ enum known_type known,
+ struct pf_parameter *param);
#define pf_iterator(map, position) { \
struct pf_position position; \
diff -ru -Xvendor.freeciv.current/diff_ignore
vendor.freeciv.current/server/unittools.c freeciv.PR11995/server/unittools.c
--- vendor.freeciv.current/server/unittools.c 2005-03-09 23:03:27.000000000
+0000
+++ freeciv.PR11995/server/unittools.c 2005-03-14 20:45:21.000000000 +0000
@@ -1923,8 +1923,8 @@
/**************************************************************************
Send the unit into to those connections in dest which can see the units
- at it's position, or the specified (x,y) (if different).
- Eg, use x and y as where the unit came from, so that the info can be
+ at it's position, or the specified ptile (if different).
+ Eg, use ptile as where the unit came from, so that the info can be
sent if the other players can see either the target or destination tile.
dest = NULL means all connections (game.game_connections)
**************************************************************************/
- [freeciv-ai] Re: [Freeciv-Dev] Re: (PR#11995) Stupid AI Creates Tall Stacks,
Benedict Adamson <=
|
|