Freeciv21
Develop your civilization from humble roots to a global empire
autoexplorer.cpp
Go to the documentation of this file.
1 /*
2  Copyright (c) 1996-2020 Freeciv21 and Freeciv contributors. This file is
3  __ __ part of Freeciv21. Freeciv21 is free software: you can
4 / \\..// \ redistribute it and/or modify it under the terms of the GNU
5  ( oo ) General Public License as published by the Free Software
6  \__/ Foundation, either version 3 of the License, or (at your
7  option) any later version. You should have received
8  a copy of the GNU General Public License along with Freeciv21. If not,
9  see https://www.gnu.org/licenses/.
10  */
11 
12 #include <cmath> // log
13 
14 // utility
15 #include "log.h"
16 
17 // common
18 #include "ai.h"
19 #include "map.h"
20 #include "movement.h"
21 #include "player.h"
22 #include "unit.h"
23 
24 /* common/aicore */
25 #include "path_finding.h"
26 #include "pf_tools.h"
27 
28 // server
29 #include "maphand.h"
30 #include "srv_log.h"
31 
32 /* server/advisors */
33 #include "advgoto.h"
34 
35 // ai
36 #include "handicaps.h"
37 
38 #include "autoexplorer.h"
39 
45 static int likely_native(struct tile *ptile, struct player *pplayer,
46  struct unit_class *pclass)
47 {
48  int native = 0;
49  int foreign = 0;
50 
51  // We do not check H_MAP here, it should be done by map_is_known()
52  if (map_is_known(ptile, pplayer)) {
53  // we've seen the tile already.
54  return (is_native_tile_to_class(pclass, ptile) ? 100 : 0);
55  }
56 
57  /* The central tile is likely to be the same as the
58  * nearby tiles. */
59  adjc_dir_iterate(&(wld.map), ptile, ptile1, dir)
60  {
61  if (map_is_known(ptile1, pplayer)) {
62  if (is_native_tile_to_class(pclass, ptile)) {
63  native++;
64  } else {
65  foreign++;
66  }
67  }
68  }
70 
71  return 50 + (50 / wld.map.num_valid_dirs * (native - foreign));
72 }
73 
79 static bool player_may_explore(const struct tile *ptile,
80  const struct player *pplayer,
81  const struct unit_type *punittype)
82 {
83  // Don't allow military units to cross borders.
84  if (!utype_has_flag(punittype, UTYF_CIVILIAN)
85  && !player_can_invade_tile(pplayer, ptile)) {
86  return false;
87  }
88 
89  // Can't visit tiles with non-allied units.
90  if (is_non_allied_unit_tile(ptile, pplayer)) {
91  return false;
92  }
93 
94  // Non-allied cities are taboo even if no units are inside.
95  if (tile_city(ptile)
96  && !pplayers_allied(city_owner(tile_city(ptile)), pplayer)) {
97  return false;
98  }
99 
100  return true;
101 }
102 
106 static enum tile_behavior explorer_tb(const struct tile *ptile,
107  enum known_type k,
108  const struct pf_parameter *param)
109 {
110  if (!player_may_explore(ptile, param->owner, param->utype)) {
111  return TB_IGNORE;
112  }
113  return TB_NORMAL;
114 }
115 
119 static bool explorer_goto(struct unit *punit, struct tile *ptile)
120 {
121  struct pf_parameter parameter;
122  struct adv_risk_cost risk_cost;
123  bool alive = true;
124  struct pf_map *pfm;
125  PFPath path;
126  struct player *pplayer = unit_owner(punit);
127 
128  pft_fill_unit_parameter(&parameter, punit);
129  parameter.omniscience = !has_handicap(pplayer, H_MAP);
130  parameter.get_TB = explorer_tb;
131  adv_avoid_risks(&parameter, &risk_cost, punit,
133 
134  // Show the destination in the client
135  punit->goto_tile = ptile;
136 
137  UNIT_LOG(LOG_DEBUG, punit, "explorer_goto to %d,%d", TILE_XY(ptile));
138 
139  pfm = pf_map_new(&parameter);
140  path = pf_map_path(pfm, ptile);
141 
142  if (!path.empty()) {
143  alive = adv_follow_path(punit, path, ptile);
144  }
145 
146  pf_map_destroy(pfm);
147 
148  return alive;
149 }
150 
161 #define SAME_TER_SCORE 21
162 #define DIFF_TER_SCORE 81
163 #define KNOWN_SAME_TER_SCORE 0
164 #define KNOWN_DIFF_TER_SCORE 51
165 
166 /* The maximum number of tiles that the unit might uncover in a move.
167  * #define MAX_NEW_TILES (1 + 4 *
168  * (unit_type_get(punit)->vision_range)) The previous line would be ideal,
169  * but we'd like these to be constants for efficiency, so pretend
170  * vision_range == 1 */
171 #define MAX_NEW_TILES 5
172 
173 /* The number of tiles that the unit can see. =(1 + 2r)^2
174  * #define VISION_TILES (1 + 2 *
175  * unit_type_get(punit)->vision_range)*\ (1 + 2 *
176  * unit_type_get(punit)->vision_range) As above, set vision_range == 1 */
177 #define VISION_TILES 9
178 
179 /* The desirability of the best tile possible without cities or huts.
180  * TER_SCORE is given per 1% of certainty about the terrain, so
181  * muliply by 100 to compensate. */
182 #define BEST_NORMAL_TILE \
183  (100 * MAX_NEW_TILES * DIFF_TER_SCORE \
184  + 100 * (VISION_TILES - MAX_NEW_TILES) * KNOWN_DIFF_TER_SCORE)
185 
186 /* We value exploring around our cities just slightly more than exploring
187  * tiles fully surrounded by different terrain. */
188 #define OWN_CITY_SCORE (BEST_NORMAL_TILE + 1)
189 
190 /* And we value exploring huts even more than our own cities.
191  * FIXME: different desirability of entering different huts
192  * in different circumstates must be specifiable by a ruleset. */
193 #define HUT_SCORE (OWN_CITY_SCORE + 1)
194 
195 #define BEST_POSSIBLE_SCORE (HUT_SCORE + BEST_NORMAL_TILE)
196 
197 static int explorer_desirable(struct tile *ptile, struct player *pplayer,
198  struct unit *punit)
199 {
200  int radius_sq = unit_type_get(punit)->vision_radius_sq;
201  int desirable = 0;
202  int unknown = 0;
203 
204  /* First do some checks that would make a tile completely non-desirable.
205  * If we're a barbarian and the tile has a hut, don't go there. */
206  // FIXME: HUT_NOTHING ok
207  if (is_barbarian(pplayer) && hut_on_tile(ptile)) {
208  return 0;
209  }
210 
211  // Do no try to cross borders and break a treaty, etc.
212  if (!player_may_explore(ptile, punit->owner, unit_type_get(punit))) {
213  return 0;
214  }
215 
216  circle_iterate(&(wld.map), ptile, radius_sq, ptile1)
217  {
218  int native = likely_native(ptile1, pplayer, unit_class_get(punit));
219 
220  if (!map_is_known(ptile1, pplayer)) {
221  unknown++;
222 
223  /* FIXME: we should add OWN_CITY_SCORE to desirable if the tile
224  * can be harvested by a city of ours. Just calculating this each
225  * time becomes rather expensive. Jason Short suggests:
226  * It should be easy to generate this information once, for
227  * the entire world. It can be used by everyone and only
228  * sometimes needs to be recalculated (actually all changes
229  * only require local recalculation, but that could be unstable). */
230 
231  desirable +=
232  (native * SAME_TER_SCORE + (100 - native) * DIFF_TER_SCORE);
233  } else {
234  if (is_tiles_adjacent(ptile, ptile1)) {
235  /* we don't value staying offshore from land,
236  * only adjacent. Otherwise destroyers do the wrong thing. */
237  desirable += (native * KNOWN_SAME_TER_SCORE
238  + (100 - native) * KNOWN_DIFF_TER_SCORE);
239  }
240  }
241  }
243 
244  if (unknown <= 0) {
245  // We make sure we'll uncover at least one unexplored tile.
246  desirable = 0;
247  }
248 
249  if ((!is_ai(pplayer) || !has_handicap(pplayer, H_HUTS))
250  && map_is_known(ptile, pplayer)
251  && unit_can_displace_hut(punit, ptile)) {
252  /* we want to explore huts whenever we can,
253  * even if doing so will not uncover any tiles. */
254  // FIXME: should HUT_FRIGHTEN explorer strive to destroy huts?
255  desirable += HUT_SCORE;
256  }
257 
258  return desirable;
259 }
260 
270 enum unit_move_result manage_auto_explorer(struct unit *punit)
271 {
272  struct player *pplayer = unit_owner(punit);
273  // Loop prevention
274  const struct tile *init_tile = unit_tile(punit);
275 
276  /* The log of the want of the most desirable tile,
277  * given nearby water, cities, etc. */
278  double log_most_desirable = -FC_INFINITY;
279 
280  /* The maximum distance we are willing to search. It decreases depending
281  * on the want of already discovered tagets. It is defined as the distance
282  * at which a tile with BEST_POSSIBLE_SCORE would have to be found in
283  * order to be better than the current most_desirable tile. */
284  int max_dist = FC_INFINITY;
285 
286  /* Coordinates of most desirable tile. Initialized to make
287  * compiler happy. Also MC to the best tile. */
288  struct tile *best_tile = nullptr;
289  int best_MC = FC_INFINITY;
290 
291  // Path-finding stuff
292  struct pf_map *pfm;
293  struct pf_parameter parameter;
294 
295 #define DIST_FACTOR 0.6
296 
297  double logDF = log(DIST_FACTOR);
298  double logBPS = log(BEST_POSSIBLE_SCORE);
299 
300  UNIT_LOG(LOG_DEBUG, punit, "auto-exploring.");
301 
302  if (!is_human(pplayer) && unit_has_type_flag(punit, UTYF_GAMELOSS)) {
303  UNIT_LOG(LOG_DEBUG, punit, "exploration too dangerous!");
304  return MR_BAD_ACTIVITY; // too dangerous
305  }
306 
308 
309  pft_fill_unit_parameter(&parameter, punit);
310  parameter.get_TB = no_fights_or_unknown;
311  // When exploring, even AI should pretend to not cheat.
312  parameter.omniscience = false;
313 
314  pfm = pf_map_new(&parameter);
315  pf_map_move_costs_iterate(pfm, ptile, move_cost, false)
316  {
317  int desirable;
318  double log_desirable;
319 
320  // Our callback should insure this.
321  fc_assert_action(map_is_known(ptile, pplayer), continue);
322 
323  desirable = explorer_desirable(ptile, pplayer, punit);
324 
325  if (desirable <= 0) {
326  // Totally non-desirable tile. No need to continue.
327  continue;
328  }
329 
330  // take the natural log
331  log_desirable = log(desirable);
332 
333  /* Ok, the way we calculate goodness is taking the base tile
334  * desirability amortized by the time it takes to get there:
335  *
336  * goodness = desirability * DIST_FACTOR^total_MC
337  *
338  * TODO: JDS notes that we should really make our exponential
339  * term dimensionless by dividing by move_rate.
340  *
341  * We want to truncate our search, so we calculate a maximum distance
342  * that we would move to find the tile with the most possible
343  * desirability (BEST_POSSIBLE_SCORE) that gives us the same goodness as
344  * the current tile position we're looking at. Therefore we have:
345  *
346  * desirability * DIST_FACTOR^total_MC =
347  * BEST_POSSIBLE_SCORE * DIST_FACTOR^(max distance) (1)
348  *
349  * and then solve for max_dist. We only want to change max_dist when
350  * we find a tile that has better goodness than we've found so far; hence
351  * the conditional below. It looks cryptic, but all it is is testing
352  * which of two goodnesses is bigger after taking the natural log of both
353  * sides.
354  */
355  if (log_desirable + move_cost * logDF
356  > log_most_desirable + best_MC * logDF) {
357  log_most_desirable = log_desirable;
358  best_tile = ptile;
359  best_MC = move_cost;
360 
361  /* take the natural log and solve equation (1) above. We round
362  * max_dist down (is this correct?). */
363  max_dist = best_MC + (log_most_desirable - logBPS) / logDF;
364  }
365 
366  // let's not go further than this
367  if (move_cost > max_dist) {
368  break;
369  }
370  }
372  pf_map_destroy(pfm);
373 
375 
376  // Go to the best tile found.
377  if (best_tile != nullptr) {
378  /* TODO: read the path off the map we made. Then we can make a path
379  * which goes beside the unknown, with a good EC callback... */
380  enum override_bool allow = NO_OVERRIDE;
381 
382  if (is_ai(pplayer)) {
383  CALL_PLR_AI_FUNC(want_to_explore, pplayer, punit, best_tile, &allow);
384  }
385  if (allow == OVERRIDE_FALSE) {
386  UNIT_LOG(LOG_DEBUG, punit, "not allowed to explore");
387  return MR_NOT_ALLOWED;
388  }
389  if (!explorer_goto(punit, best_tile)) {
390  // Died? Strange...
391  return MR_DEATH;
392  }
393  UNIT_LOG(LOG_DEBUG, punit, "exploration GOTO succeeded");
394  if (punit->moves_left > 0) {
395  // We can still move on...
396  if (!same_pos(init_tile, unit_tile(punit))) {
397  /* At least we moved (and maybe even got to where we wanted).
398  * Let's do more exploring.
399  * (Checking only whether our position changed is unsafe: can allow
400  * yoyoing on a RR) */
401  UNIT_LOG(LOG_DEBUG, punit, "recursively exploring...");
402  return manage_auto_explorer(punit);
403  } else {
404  UNIT_LOG(LOG_DEBUG, punit, "done exploring (all finished)...");
405  return MR_PAUSE;
406  }
407  }
408  UNIT_LOG(LOG_DEBUG, punit, "done exploring (but more go go)...");
409  return MR_OK;
410  } else {
411  // Didn't find anything.
412  UNIT_LOG(LOG_DEBUG, punit, "failed to explore more");
413  return MR_BAD_MAP_POSITION;
414  }
415 #undef DIST_FACTOR
416 }
417 
418 #undef SAME_TER_SCORE
419 #undef DIFF_TER_SCORE
420 #undef KNOWN_SAME_TER_SCORE
421 #undef KNOWN_DIFF_TER_SCORE
422 #undef OWN_CITY_SCORE
423 #undef HUT_SCORE
bool adv_follow_path(struct unit *punit, const PFPath &path, struct tile *ptile)
Move a unit along a path without disturbing its activity, role or assigned destination Return FALSE i...
Definition: advgoto.cpp:43
void adv_avoid_risks(struct pf_parameter *parameter, struct adv_risk_cost *risk_cost, struct unit *punit, const double fearfulness)
Set PF callbacks to favour paths that do not create tall stacks or cross dangerous tiles.
Definition: advgoto.cpp:490
#define NORMAL_STACKING_FEARFULNESS
Definition: advgoto.h:20
#define CALL_PLR_AI_FUNC(_func, _player,...)
Definition: ai.h:383
#define SAME_TER_SCORE
Return a value indicating how desirable it is to explore the given tile.
static int explorer_desirable(struct tile *ptile, struct player *pplayer, struct unit *punit)
static enum tile_behavior explorer_tb(const struct tile *ptile, enum known_type k, const struct pf_parameter *param)
TB function used by explorer_goto().
enum unit_move_result manage_auto_explorer(struct unit *punit)
Handle eXplore mode of a unit (explorers are always in eXplore mode for AI) - explores unknown territ...
#define HUT_SCORE
#define DIFF_TER_SCORE
static bool explorer_goto(struct unit *punit, struct tile *ptile)
Constrained goto using player_may_explore().
#define KNOWN_DIFF_TER_SCORE
#define KNOWN_SAME_TER_SCORE
static int likely_native(struct tile *ptile, struct player *pplayer, struct unit_class *pclass)
Determine if a tile is likely to be native, given information that the player actually has.
#define DIST_FACTOR
#define BEST_POSSIBLE_SCORE
static bool player_may_explore(const struct tile *ptile, const struct player *pplayer, const struct unit_type *punittype)
Returns TRUE if a unit owned by the given player can safely "explore" the given tile.
struct player * city_owner(const struct city *pcity)
Return the owner of the city.
Definition: city.cpp:1083
bool empty() const
bool unit_can_displace_hut(const struct unit *punit, const struct tile *ptile)
Returns TRUE iff the unit can enter or frighten any hut on the tile.
Definition: extras.cpp:665
bool hut_on_tile(const struct tile *ptile)
Returns TRUE iff an extra on the tile is a hut (removed by entering).
Definition: extras.cpp:626
override_bool
Definition: fc_types.h:78
@ NO_OVERRIDE
Definition: fc_types.h:78
@ OVERRIDE_FALSE
Definition: fc_types.h:78
struct world wld
Definition: game.cpp:48
bool has_handicap(const struct player *pplayer, enum handicap_type htype)
AI players may have handicaps - allowing them to cheat or preventing them from using certain algorith...
Definition: handicaps.cpp:62
@ H_MAP
Definition: handicaps.h:27
@ H_HUTS
Definition: handicaps.h:24
constexpr auto LOG_DEBUG
Definition: log.h:27
#define fc_assert_action(condition, action)
Definition: log.h:104
bool is_tiles_adjacent(const struct tile *tile0, const struct tile *tile1)
Are two tiles adjacent to each other.
Definition: map.cpp:878
bool same_pos(const struct tile *tile1, const struct tile *tile2)
Are (x1,y1) and (x2,y2) really the same when adjusted? This function might be necessary ALOT of place...
Definition: map.cpp:887
#define adjc_dir_iterate(nmap, center_tile, itr_tile, dir_itr)
Definition: map.h:364
#define circle_iterate(nmap, center_tile, sq_radius, tile_itr)
Definition: map.h:322
#define adjc_dir_iterate_end
Definition: map.h:368
#define circle_iterate_end
Definition: map.h:325
bool map_is_known(const struct tile *ptile, const struct player *pplayer)
Return whether the player knows the tile.
Definition: maphand.cpp:884
static bool is_native_tile_to_class(const struct unit_class *punitclass, const struct tile *ptile)
Definition: movement.h:73
unit_move_result
Definition: movement.h:25
@ MR_OK
Definition: movement.h:26
@ MR_BAD_MAP_POSITION
Definition: movement.h:34
@ MR_BAD_ACTIVITY
Definition: movement.h:32
@ MR_NOT_ALLOWED
Definition: movement.h:42
@ MR_DEATH
Definition: movement.h:27
@ MR_PAUSE
Definition: movement.h:28
struct pf_map * pf_map_new(const struct pf_parameter *parameter)
Factory function to create a new map according to the parameter.
PFPath pf_map_path(struct pf_map *pfm, struct tile *ptile)
CHECK DOCS AFTER FULL CONVERSTION OF pf_path to class PFPath Tries to find the best path in the given...
void pf_map_destroy(struct pf_map *pfm)
After usage the map must be destroyed.
#define pf_map_move_costs_iterate_end
Definition: path_finding.h:542
#define pf_map_move_costs_iterate(ARG_pfm, NAME_tile, NAME_cost, COND_from_start)
Definition: path_finding.h:532
tile_behavior
Definition: path_finding.h:276
@ TB_NORMAL
Definition: path_finding.h:277
@ TB_IGNORE
Definition: path_finding.h:278
void pft_fill_unit_parameter(struct pf_parameter *parameter, const struct unit *punit)
Fill classic parameters for an unit.
Definition: pf_tools.cpp:822
enum tile_behavior no_fights_or_unknown(const struct tile *ptile, enum known_type known, const struct pf_parameter *param)
PF callback to prohibit going into the unknown.
Definition: pf_tools.cpp:469
bool player_can_invade_tile(const struct player *pplayer, const struct tile *ptile)
Return TRUE iff the player can invade a particular tile (linked with borders and diplomatic states).
Definition: player.cpp:236
bool pplayers_allied(const struct player *pplayer, const struct player *pplayer2)
Returns true iff players are allied.
Definition: player.cpp:1334
static bool is_barbarian(const struct player *pplayer)
Definition: player.h:474
#define is_ai(plr)
Definition: player.h:227
#define is_human(plr)
Definition: player.h:226
#define FC_INFINITY
Definition: shared.h:32
@ AIT_EXPLORER
Definition: srv_log.h:63
#define UNIT_LOG(_, punit, msg,...)
Definition: srv_log.h:95
@ TIMER_STOP
Definition: srv_log.h:77
@ TIMER_START
Definition: srv_log.h:77
#define TIMING_LOG(timer, activity)
Definition: srv_log.h:121
int num_valid_dirs
Definition: map_types.h:70
enum tile_behavior(* get_TB)(const struct tile *ptile, enum known_type known, const struct pf_parameter *param)
Definition: path_finding.h:380
const struct player * owner
Definition: path_finding.h:356
const struct unit_type * utype
Definition: path_finding.h:355
Definition: player.h:231
Definition: tile.h:42
int vision_radius_sq
Definition: unittype.h:487
Definition: unit.h:134
int moves_left
Definition: unit.h:147
struct tile * goto_tile
Definition: unit.h:152
struct player * owner
Definition: unit.h:139
struct civ_map map
Definition: world_object.h:21
struct city * tile_city(const struct tile *ptile)
Return the city on this tile (or nullptr), checking for city center.
Definition: tile.cpp:72
known_type
Definition: tile.h:28
#define TILE_XY(ptile)
Definition: tile.h:36
struct unit * is_non_allied_unit_tile(const struct tile *ptile, const struct player *pplayer)
Is there an non-allied unit on this tile?
Definition: unit.cpp:1252
#define unit_tile(_pu)
Definition: unit.h:371
#define unit_owner(_pu)
Definition: unit.h:370
const struct unit_type * unit_type_get(const struct unit *punit)
Return the unit type for this unit.
Definition: unittype.cpp:114
struct unit_class * unit_class_get(const struct unit *punit)
Returns unit class pointer for a unit.
Definition: unittype.cpp:2151
bool unit_has_type_flag(const struct unit *punit, enum unit_type_flag_id flag)
Return whether the unit has the given flag.
Definition: unittype.cpp:176
static bool utype_has_flag(const struct unit_type *punittype, int flag)
Definition: unittype.h:584