Freeciv21
Develop your civilization from humble roots to a global empire
startpos.cpp
Go to the documentation of this file.
1 /*
2 \^~~~~\ ) ( /~~~~^/ * _ Copyright (c) 1996-2020 Freeciv21 and
3  ) *** \ {**} / *** ( * _ {o} _ Freeciv contributors. This file is
4  ) *** \_ ^^ _/ *** ( * {o}{o}{o} part of Freeciv21. Freeciv21 is free
5  ) **** vv **** ( * ~\ | /~software: you can redistribute it and/or
6  )_**** ****_( * OoO modify it under the terms of the GNU
7  )*** m m ***( * /|\ General Public License as published
8  by the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version. You should have received a copy of
10  the GNU General Public License along with Freeciv21.
11  If not, see https://www.gnu.org/licenses/.
12  */
13 
14 #include <QBitArray>
15 #include <cmath> // sqrt, HUGE_VAL
16 
17 // utility
18 #include "fcintl.h"
19 #include "log.h"
20 
21 // common
22 #include "game.h"
23 #include "map.h"
24 #include "movement.h"
25 
26 // server
27 #include "maphand.h"
28 
29 /* server/generator */
30 #include "mapgen_utils.h"
31 #include "startpos.h"
32 #include "temperature_map.h"
33 
36  int size;
37  int goodies;
38  int starters;
39  int total;
40 };
41 static struct islands_data_type *islands;
42 static int *islands_index;
43 
47 static int get_tile_value(struct tile *ptile)
48 {
49  int value;
50  int irrig_bonus = 0;
51  int mine_bonus = 0;
52  struct tile *roaded;
53  struct extra_type *nextra;
54 
55  /* Give one point for each food / shield / trade produced. */
56  value = 0;
58  {
59  value += city_tile_output(nullptr, ptile, false,
60  static_cast<Output_type_id>(o));
61  }
63 
64  roaded = tile_virtual_new(ptile);
65 
66  if (num_role_units(L_SETTLERS) > 0) {
67  struct unit_type *start_worker = get_role_unit(L_SETTLERS, 0);
68 
69  extra_type_by_cause_iterate(EC_ROAD, pextra)
70  {
71  struct road_type *proad = extra_road_get(pextra);
72 
73  if (road_can_be_built(proad, roaded)
74  && are_reqs_active(nullptr, nullptr, nullptr, nullptr, roaded,
75  nullptr, start_worker, nullptr, nullptr,
76  nullptr, &pextra->reqs, RPT_CERTAIN)) {
77  tile_add_extra(roaded, pextra);
78  }
79  }
81  }
82 
83  nextra = next_extra_for_tile(roaded, EC_IRRIGATION, nullptr, nullptr);
84 
85  if (nextra != nullptr) {
86  struct tile *vtile;
87 
88  vtile = tile_virtual_new(roaded);
89  tile_apply_activity(vtile, ACTIVITY_IRRIGATE, nextra);
90  irrig_bonus = -value;
92  {
93  irrig_bonus += city_tile_output(nullptr, vtile, false,
94  static_cast<Output_type_id>(o));
95  }
97  tile_virtual_destroy(vtile);
98  }
99 
100  nextra = next_extra_for_tile(roaded, EC_MINE, nullptr, nullptr);
101 
102  // Same set of roads used with mine as with irrigation.
103  if (nextra != nullptr) {
104  struct tile *vtile;
105 
106  vtile = tile_virtual_new(roaded);
107  tile_apply_activity(vtile, ACTIVITY_MINE, nextra);
108  mine_bonus = -value;
110  {
111  mine_bonus += city_tile_output(nullptr, vtile, false,
112  static_cast<Output_type_id>(o));
113  }
115  tile_virtual_destroy(vtile);
116  }
117 
118  tile_virtual_destroy(roaded);
119 
120  value += MAX(0, MAX(mine_bonus, irrig_bonus)) / 2;
121 
122  return value;
123 }
124 
128  int *value;
129 };
130 
136 static bool check_native_area(const struct unit_type *utype,
137  const struct tile *ptile, int min_area)
138 {
139  int tiles = 1; // There's the central tile already.
140  struct tile_list *tlist = tile_list_new();
141  struct tile *central = tile_virtual_new(ptile); // Non-const virtual tile
142  QBitArray handled(MAP_INDEX_SIZE);
143 
144  tile_list_append(tlist, central);
145 
146  while (tile_list_size(tlist) > 0 && tiles < min_area) {
147  tile_list_iterate(tlist, ptile2)
148  {
149  adjc_iterate(&(wld.map), ptile2, ptile3)
150  {
151  int idx = tile_index(ptile3);
152 
153  if (!handled.at(idx) && is_native_tile(utype, ptile3)) {
154  tiles++;
155  tile_list_append(tlist, ptile3);
156  handled.setBit(idx);
157  if (tiles >= min_area) {
158  // Break out when we already know that area is sufficient.
159  break;
160  }
161  }
162  }
164 
165  tile_list_remove(tlist, ptile2);
166 
167  if (tiles >= min_area) {
168  // Break out when we already know that area is sufficient.
169  break;
170  }
171  }
173  }
174 
175  tile_list_destroy(tlist);
176  tile_virtual_destroy(central);
177 
178  return tiles >= min_area;
179 }
180 
192 static bool is_valid_start_pos(const struct tile *ptile, const void *dataptr)
193 {
194  const struct start_filter_data *pdata =
195  static_cast<const start_filter_data *>(dataptr);
196  struct islands_data_type *island;
197  int cont_size, cont = tile_continent(ptile);
198 
199  // Only start on certain terrain types.
200  if (pdata->value[tile_index(ptile)] < pdata->min_value) {
201  return false;
202  }
203 
204  fc_assert_ret_val(cont > 0, false);
205  if (islands[islands_index[cont]].starters == 0) {
206  return false;
207  }
208 
209  // Don't start on a hut.
210  // FIXME: for HUT_NOTHING might be valid
211  if (hut_on_tile(ptile)) {
212  return false;
213  }
214 
215  // Has to be native tile for initial unit
216  if (!is_native_tile(pdata->initial_unit, ptile)) {
217  return false;
218  }
219 
220  // Check native area size.
221  if (!check_native_area(pdata->initial_unit, ptile,
222  terrain_control.min_start_native_area)) {
223  return false;
224  }
225 
226  if (game.server.start_city
227  && terrain_has_flag(tile_terrain(ptile), TER_NO_CITIES)) {
228  return false;
229  }
230 
231  /* A longstanding bug allowed starting positions to exist on poles,
232  * sometimes. This hack prevents it by setting a fixed distance from
233  * the pole (dependent on map temperature) that a start pos must be.
234  * Cold and frozen tiles are not allowed for start pos placement. */
235  if (tmap_is(ptile, TT_NHOT)) {
236  return false;
237  }
238 
239  // Don't start too close to someone else.
240  cont_size = get_continent_size(cont);
241  island = islands + islands_index[cont];
242  for (auto *psp : qAsConst(*wld.map.startpos_table)) {
243  if (psp->exclude) {
244  continue;
245  }
246  struct tile *tile1 = startpos_tile(psp);
247 
248  if ((tile_continent(ptile) == tile_continent(tile1)
249  && (real_map_distance(ptile, tile1) * 1000 / pdata->min_value
250  <= (sqrt(cont_size / island->total))))
251  || (real_map_distance(ptile, tile1) * 1000 / pdata->min_value < 5)) {
252  return false;
253  }
254  }
255  return true;
256 }
257 
261 static int compare_islands(const void *A_, const void *B_)
262 {
263  const struct islands_data_type *A = static_cast<const islands_data_type *>(
264  A_),
265  *B = static_cast<const islands_data_type *>(
266  B_);
267 
268  return B->goodies - A->goodies;
269 }
270 
274 static void initialize_isle_data()
275 {
276  int nr;
278  islands_index = new int[wld.map.num_continents + 1];
279 
280  // islands[0] is unused.
281  for (nr = 1; nr <= wld.map.num_continents; nr++) {
282  islands[nr].id = nr;
283  islands[nr].size = get_continent_size(nr);
284  islands[nr].goodies = 0;
285  islands[nr].starters = 0;
286  islands[nr].total = 0;
287  }
288 }
289 
293 static bool filter_starters(const struct tile *ptile, const void *data)
294 {
295  return terrain_has_flag(tile_terrain(ptile), TER_STARTER);
296 }
297 
311  struct unit_type *initial_unit)
312 {
313  struct tile *ptile;
314  int k, sum;
315  struct start_filter_data data;
316  int *tile_value_aux = nullptr;
317  int *tile_value = nullptr;
318  int min_goodies_per_player = 1500;
319  int total_goodies = 0;
320  // this is factor is used to maximize land used in extreme little maps
321  float efactor =
322  static_cast<float>(player_count()) / float(map_size_checked()) / 4;
323  bool failure = false;
324  bool is_tmap = temperature_is_initialized();
325 
326  if (wld.map.num_continents < 1) {
327  /* Currently we can only place starters on land terrain, so fail
328  * immediately if there isn't any on the map. */
329  qDebug("Map has no land, so cannot assign start positions!");
330  return false;
331  }
332 
333  if (!is_tmap) {
334  /* The temperature map has already been destroyed by the time start
335  * positions have been placed. We check for this and then create a
336  * false temperature map. This is used in the tmap_is() call above.
337  * We don't create a "real" map here because that requires the height
338  * map and other information which has already been destroyed. */
339  create_tmap(false);
340  }
341 
342  // If the default is given, just use MAPSTARTPOS_VARIABLE.
343  if (MAPSTARTPOS_DEFAULT == mode) {
344  qDebug("Using startpos=VARIABLE");
345  mode = MAPSTARTPOS_VARIABLE;
346  }
347 
348  tile_value_aux = new int[MAP_INDEX_SIZE]();
349  tile_value = new int[MAP_INDEX_SIZE]();
350 
351  // get the tile value
352  whole_map_iterate(&(wld.map), value_tile)
353  {
354  tile_value_aux[tile_index(value_tile)] = get_tile_value(value_tile);
355  }
357 
358  // select the best tiles
359  whole_map_iterate(&(wld.map), value_tile)
360  {
361  int this_tile_value = tile_value_aux[tile_index(value_tile)];
362  int lcount = 0, bcount = 0;
363 
364  // check all tiles within the default city radius
365  city_tile_iterate(CITY_MAP_DEFAULT_RADIUS_SQ, value_tile, ptile1)
366  {
367  if (this_tile_value > tile_value_aux[tile_index(ptile1)]) {
368  lcount++;
369  } else if (this_tile_value < tile_value_aux[tile_index(ptile1)]) {
370  bcount++;
371  }
372  }
374 
375  if (lcount <= bcount) {
376  this_tile_value = 0;
377  }
378  tile_value[tile_index(value_tile)] = 100 * this_tile_value;
379  }
381  // get an average value
382  smooth_int_map(tile_value, true);
383 
385 
386  // Only consider tiles marked as 'starter terrains' by ruleset
387  whole_map_iterate(&(wld.map), starter_tile)
388  {
389  if (!filter_starters(starter_tile, nullptr)) {
390  tile_value[tile_index(starter_tile)] = 0;
391  } else {
392  // Oceanic terrain cannot be starter terrain currently
393  fc_assert_action(tile_continent(starter_tile) > 0, continue);
394  islands[tile_continent(starter_tile)].goodies +=
395  tile_value[tile_index(starter_tile)];
396  total_goodies += tile_value[tile_index(starter_tile)];
397  }
398  }
400 
401  // evaluate the best places on the map
402  adjust_int_map_filtered(tile_value, 1000, nullptr, filter_starters);
403 
404  /* Sort the islands so the best ones come first. Note that islands[0] is
405  * unused so we just skip it. */
406  qsort(islands + 1, wld.map.num_continents, sizeof(*islands),
408 
409  /* If we can't place starters according to the first choice, change the
410  * choice. */
411  if (MAPSTARTPOS_SINGLE == mode
412  && wld.map.num_continents < player_count() + 3) {
413  qDebug("Not enough continents; falling back to startpos=2or3");
414  mode = MAPSTARTPOS_2or3;
415  }
416 
417  if (MAPSTARTPOS_2or3 == mode
418  && wld.map.num_continents < player_count() / 2 + 4) {
419  qDebug("Not enough continents; falling back to startpos=VARIABLE");
420  mode = MAPSTARTPOS_VARIABLE;
421  }
422 
423  if (MAPSTARTPOS_ALL == mode
424  && (islands[1].goodies < player_count() * min_goodies_per_player
425  || islands[1].goodies
426  < total_goodies * (0.5 + 0.8 * efactor) / (1 + efactor))) {
427  qDebug("No good enough island; falling back to startpos=VARIABLE");
428  mode = MAPSTARTPOS_VARIABLE;
429  }
430 
431  // the variable way is the last possibility
432  if (MAPSTARTPOS_VARIABLE == mode) {
433  min_goodies_per_player = total_goodies * (0.65 + 0.8 * efactor)
434  / (1 + efactor) / player_count();
435  }
436 
437  {
438  int nr, to_place = player_count(), first = 1;
439 
440  // inizialize islands_index
441  for (nr = 1; nr <= wld.map.num_continents; nr++) {
442  islands_index[islands[nr].id] = nr;
443  }
444 
445  /* When placing a fixed number of players per island, for fairness, try
446  * to avoid sets of populated islands where there is more than 10%
447  * variation in "goodness" within the entire set. (Fallback if that
448  * fails: place players on the worst available islands.) */
449  if (MAPSTARTPOS_SINGLE == mode || MAPSTARTPOS_2or3 == mode) {
450  float var_goodies, best = HUGE_VAL;
451  int num_islands =
452  (MAPSTARTPOS_SINGLE == mode ? player_count() : player_count() / 2);
453 
454  for (nr = 1; nr <= 1 + wld.map.num_continents - num_islands; nr++) {
455  if (islands[nr + num_islands - 1].goodies < min_goodies_per_player) {
456  break;
457  }
458  var_goodies =
459  (islands[nr].goodies - islands[nr + num_islands - 1].goodies)
460  / (islands[nr + num_islands - 1].goodies);
461 
462  if (var_goodies < best * 0.9) {
463  best = var_goodies;
464  first = nr;
465  }
466  }
467  }
468 
469  // set starters per isle
470  if (MAPSTARTPOS_ALL == mode) {
471  islands[1].starters = to_place;
472  islands[1].total = to_place;
473  to_place = 0;
474  }
475  for (nr = 1; nr <= wld.map.num_continents; nr++) {
476  if (MAPSTARTPOS_SINGLE == mode && 0 < to_place && nr >= first) {
477  islands[nr].starters = 1;
478  islands[nr].total = 1;
479  to_place--;
480  }
481  if (MAPSTARTPOS_2or3 == mode && 0 < to_place && nr >= first) {
482  islands[nr].starters = 2 + (nr == 1 ? (player_count() % 2) : 0);
483  to_place -= islands[nr].total = islands[nr].starters;
484  }
485 
486  if (MAPSTARTPOS_VARIABLE == mode && 0 < to_place) {
487  islands[nr].starters =
488  MAX(1, islands[nr].goodies / MAX(1, min_goodies_per_player));
489  to_place -= islands[nr].total = islands[nr].starters;
490  }
491  }
492  }
493 
494  data.value = tile_value;
495  data.min_value = 900;
496  data.initial_unit = initial_unit;
497  sum = 0;
498  for (k = 1; k <= wld.map.num_continents; k++) {
499  sum += islands[islands_index[k]].starters;
500  if (islands[islands_index[k]].starters != 0) {
501  qDebug("starters on isle %i", k);
502  }
503  }
504  fc_assert_ret_val(player_count() <= sum, false);
505 
506  // now search for the best place and set start_positions
507  while (map_startpos_count() < player_count()) {
508  if ((ptile =
510  islands[islands_index[static_cast<int> tile_continent(ptile)]]
511  .starters--;
512  log_debug("Adding (%d, %d) as starting position %d, %d goodies on "
513  "islands.",
514  TILE_XY(ptile), map_startpos_count(),
515  islands[islands_index[(int) tile_continent(ptile)]].goodies);
516  (void) map_startpos_new(ptile);
517  } else {
518  data.min_value *= 0.95;
519  if (data.min_value <= 10) {
520  qInfo(_("The server appears to have gotten into an infinite "
521  "loop in the allocation of starting positions.\nMaybe "
522  "the number of players is too high for this map."));
523  failure = true;
524  break;
525  }
526  }
527  }
528 
529  delete[] islands;
530  delete[] islands_index;
531  islands = nullptr;
532  islands_index = nullptr;
533 
534  if (!is_tmap) {
535  destroy_tmap();
536  }
537 
538  delete[] tile_value_aux;
539  delete[] tile_value;
540  tile_value_aux = nullptr;
541  tile_value = nullptr;
542 
543  return !failure;
544 }
int city_tile_output(const struct city *pcity, const struct tile *ptile, bool is_celebrating, Output_type_id otype)
Calculate the output for the tile.
Definition: city.cpp:1230
#define city_tile_iterate(_radius_sq, _city_tile, _tile)
Definition: city.h:202
#define output_type_iterate(output)
Definition: city.h:764
#define CITY_MAP_DEFAULT_RADIUS_SQ
Definition: city.h:55
#define city_tile_iterate_end
Definition: city.h:209
#define output_type_iterate_end
Definition: city.h:771
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
struct extra_type * next_extra_for_tile(const struct tile *ptile, enum extra_cause cause, const struct player *pplayer, const struct unit *punit)
Returns next extra by cause that unit or player can build to tile.
Definition: extras.cpp:687
#define extra_road_get(_e_)
Definition: extras.h:171
#define extra_type_by_cause_iterate_end
Definition: extras.h:307
#define extra_type_by_cause_iterate(_cause, _extra)
Definition: extras.h:299
@ RPT_CERTAIN
Definition: fc_types.h:568
signed short Continent_id
Definition: fc_types.h:289
enum output_type_id Output_type_id
Definition: fc_types.h:295
#define _(String)
Definition: fcintl.h:50
struct civ_game game
Definition: game.cpp:47
struct world wld
Definition: game.cpp:48
#define fc_assert_ret_val(condition, val)
Definition: log.h:114
#define fc_assert_action(condition, action)
Definition: log.h:104
#define log_debug(message,...)
Definition: log.h:65
int map_startpos_count()
Is there start positions set for map.
Definition: map.cpp:1518
struct startpos * map_startpos_new(struct tile *ptile)
Create a new start position at the given tile and return it.
Definition: map.cpp:1531
struct tile * startpos_tile(const struct startpos *psp)
Returns the tile where this start position is located.
Definition: map.cpp:1419
int real_map_distance(const struct tile *tile0, const struct tile *tile1)
Return real distance between two tiles.
Definition: map.cpp:599
struct terrain_misc terrain_control
Definition: map.cpp:40
struct tile * rand_map_pos_filtered(const struct civ_map *nmap, void *data, bool(*filter)(const struct tile *ptile, const void *data))
Give a random tile anywhere on the map for which the 'filter' function returns TRUE.
Definition: map.cpp:1039
#define adjc_iterate_end
Definition: map.h:358
#define MAP_INDEX_SIZE
Definition: map.h:91
#define adjc_iterate(nmap, center_tile, itr_tile)
Definition: map.h:351
#define whole_map_iterate(_map, _tile)
Definition: map.h:473
#define map_size_checked()
Definition: map.h:200
#define whole_map_iterate_end
Definition: map.h:480
map_startpos
Definition: map_types.h:51
@ MAPSTARTPOS_VARIABLE
Definition: map_types.h:56
@ MAPSTARTPOS_2or3
Definition: map_types.h:54
@ MAPSTARTPOS_ALL
Definition: map_types.h:55
@ MAPSTARTPOS_DEFAULT
Definition: map_types.h:52
@ MAPSTARTPOS_SINGLE
Definition: map_types.h:53
int get_continent_size(Continent_id id)
Return size in tiles of the given continent (not ocean)
void smooth_int_map(int *int_map, bool zeroes_at_edges)
Apply a Gaussian diffusion filter on the map.
void adjust_int_map_filtered(int *int_map, int int_map_max, void *data, bool(*filter)(const struct tile *ptile, const void *data))
Change the values of the integer map, so that they contain ranking of each tile scaled to [0 .
bool is_native_tile(const struct unit_type *punittype, const struct tile *ptile)
This tile is native to unit.
Definition: movement.cpp:279
int player_count()
Return the number of players.
Definition: player.cpp:739
bool are_reqs_active(const struct player *target_player, const struct player *other_player, const struct city *target_city, const struct impr_type *target_building, const struct tile *target_tile, const struct unit *target_unit, const struct unit_type *target_unittype, const struct output_type *target_output, const struct specialist *target_specialist, const struct action *target_action, const struct requirement_vector *reqs, const enum req_problem_type prob_type, const enum vision_layer vision_layer, const enum national_intelligence nintel)
Checks the requirement(s) to see if they are active on the given target.
bool road_can_be_built(const struct road_type *proad, const struct tile *ptile)
Tells if road can build to tile if all other requirements are met.
Definition: road.cpp:173
#define MAX(x, y)
Definition: shared.h:48
static int compare_islands(const void *A_, const void *B_)
Helper function for qsort.
Definition: startpos.cpp:261
bool create_start_positions(enum map_startpos mode, struct unit_type *initial_unit)
where do the different nations start on the map? well this function tries to spread them out on the d...
Definition: startpos.cpp:310
static bool filter_starters(const struct tile *ptile, const void *data)
A function that filters for TER_STARTER tiles.
Definition: startpos.cpp:293
static int * islands_index
Definition: startpos.cpp:42
static struct islands_data_type * islands
Definition: startpos.cpp:41
static int get_tile_value(struct tile *ptile)
Return an approximation of the goodness of a tile to a civilization.
Definition: startpos.cpp:47
static void initialize_isle_data()
Initialize islands data.
Definition: startpos.cpp:274
static bool check_native_area(const struct unit_type *utype, const struct tile *ptile, int min_area)
Check if number of reachable native tiles is sufficient.
Definition: startpos.cpp:136
static bool is_valid_start_pos(const struct tile *ptile, const void *dataptr)
Return TRUE if (x,y) is a good starting position.
Definition: startpos.cpp:192
struct civ_game::@28::@32 server
int num_continents
Definition: map_types.h:74
QHash< struct tile *, struct startpos * > * startpos_table
Definition: map_types.h:77
Continent_id id
Definition: startpos.cpp:35
Definition: road.h:54
struct unit_type * initial_unit
Definition: startpos.cpp:127
Definition: tile.h:42
struct civ_map map
Definition: world_object.h:21
bool temperature_is_initialized()
Returns one line (given by the y coordinate) of the temperature map.
bool tmap_is(const struct tile *ptile, temperature_type tt)
Return true if the tile has tt temperature type.
void destroy_tmap()
Free the tmap.
void create_tmap(bool real)
Initialize the temperature_map if arg is FALSE, create a dummy tmap == map_colatitude to be used if h...
#define TT_NHOT
#define terrain_has_flag(terr, flag)
Definition: terrain.h:260
void tile_add_extra(struct tile *ptile, const struct extra_type *pextra)
Adds extra to tile.
Definition: tile.cpp:974
void tile_virtual_destroy(struct tile *vtile)
Frees all memory used by the virtual tile, including freeing virtual units in the tile's unit list an...
Definition: tile.cpp:1051
bool tile_apply_activity(struct tile *ptile, Activity_type_id act, struct extra_type *tgt)
Apply an activity (Activity_type_id, e.g., ACTIVITY_TRANSFORM) to a tile.
Definition: tile.cpp:701
struct tile * tile_virtual_new(const struct tile *ptile)
Returns a virtual tile.
Definition: tile.cpp:997
#define tile_index(_pt_)
Definition: tile.h:70
#define tile_list_iterate(tile_list, ptile)
Definition: tile.h:65
#define tile_terrain(_tile)
Definition: tile.h:93
#define TILE_XY(ptile)
Definition: tile.h:36
#define tile_list_iterate_end
Definition: tile.h:67
#define tile_continent(_tile)
Definition: tile.h:74
int num_role_units(int role)
How many unit types have specified role/flag.
Definition: unittype.cpp:1866
struct unit_type * get_role_unit(int role, int role_index)
Return index-th unit with specified role/flag.
Definition: unittype.cpp:1900