Freeciv-3.1
Loading...
Searching...
No Matches
startpos.c
Go to the documentation of this file.
1/***********************************************************************
2 Freeciv - Copyright (C) 1996 - 2004 The Freeciv Project Team
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12***********************************************************************/
13
14#ifdef HAVE_CONFIG_H
15#include <fc_config.h>
16#endif
17
18#include <math.h> /* sqrt, HUGE_VAL */
19
20/* utility */
21#include "log.h"
22#include "fcintl.h"
23
24/* common */
25#include "game.h"
26#include "map.h"
27#include "movement.h"
28
29/* server */
30#include "maphand.h"
31
32/* server/generator */
33#include "mapgen_topology.h"
34#include "mapgen_utils.h"
35#include "startpos.h"
36#include "temperature_map.h"
37
46static int *islands_index;
47
48/************************************************************************/
51static int get_tile_value(struct tile *ptile)
52{
53 int value;
54 int irrig_bonus = 0;
55 int mine_bonus = 0;
56 struct tile *roaded;
57 struct extra_type *nextra;
58
59 /* Give one point for each food / shield / trade produced. */
60 value = 0;
62 value += city_tile_output(NULL, ptile, FALSE, o);
64
65 roaded = tile_virtual_new(ptile);
66
67 if (num_role_units(L_SETTLERS) > 0) {
68 const struct req_context start_worker_ctxt = {
69 .tile = roaded,
70 .unittype = get_role_unit(L_SETTLERS, 0),
71 };
72
73 extra_type_by_cause_iterate(EC_ROAD, pextra) {
74 struct road_type *proad = extra_road_get(pextra);
75
76 if (road_can_be_built(proad, roaded)
77 && are_reqs_active(&start_worker_ctxt, NULL,
78 &pextra->reqs, RPT_CERTAIN)) {
79 tile_add_extra(roaded, pextra);
80 }
82 }
83
84 nextra = next_extra_for_tile(roaded, EC_IRRIGATION, NULL, NULL);
85
86 if (nextra != NULL) {
87 struct tile *vtile;
88
89 vtile = tile_virtual_new(roaded);
90 tile_apply_activity(vtile, ACTIVITY_IRRIGATE, nextra);
91 irrig_bonus = -value;
93 irrig_bonus += city_tile_output(NULL, vtile, FALSE, o);
96 }
97
98 nextra = next_extra_for_tile(roaded, EC_MINE, NULL, NULL);
99
100 /* Same set of roads used with mine as with irrigation. */
101 if (nextra != NULL) {
102 struct tile *vtile;
103
104 vtile = tile_virtual_new(roaded);
105 tile_apply_activity(vtile, ACTIVITY_MINE, nextra);
106 mine_bonus = -value;
108 mine_bonus += city_tile_output(NULL, vtile, FALSE, o);
111 }
112
113 tile_virtual_destroy(roaded);
114
115 value += MAX(0, MAX(mine_bonus, irrig_bonus)) / 2;
116
117 return value;
118}
119
125
126/************************************************************************/
130static bool check_native_area(const struct unit_type *utype,
131 const struct tile *ptile,
132 int min_area)
133{
134 int tiles = 1; /* There's the central tile already. */
135 struct tile_list *tlist = tile_list_new();
136 struct tile *central = tile_virtual_new(ptile); /* Non-const virtual tile */
137 struct dbv handled;
138
139 dbv_init(&handled, MAP_INDEX_SIZE);
140
141 tile_list_append(tlist, central);
142
143 while (tile_list_size(tlist) > 0 && tiles < min_area) {
144 tile_list_iterate(tlist, ptile2) {
145 adjc_iterate(&(wld.map), ptile2, ptile3) {
146 int idx = tile_index(ptile3);
147
148 if (!dbv_isset(&handled, idx) && is_native_tile(utype, ptile3)) {
149 tiles++;
150 tile_list_append(tlist, ptile3);
151 dbv_set(&handled, idx);
152 if (tiles >= min_area) {
153 /* Break out when we already know that area is sufficient. */
154 break;
155 }
156 }
158
159 tile_list_remove(tlist, ptile2);
160
161 if (tiles >= min_area) {
162 /* Break out when we already know that area is sufficient. */
163 break;
164 }
166 }
167
168 tile_list_destroy(tlist);
169
170 dbv_free(&handled);
171
172 tile_virtual_destroy(central);
173
174 return tiles >= min_area;
175}
176
177/************************************************************************/
187static bool is_valid_start_pos(const struct tile *ptile, const void *dataptr)
188{
189 const struct start_filter_data *pdata = dataptr;
190 struct islands_data_type *island;
191 int cont_size, cont = tile_continent(ptile);
192
193 /* Only start on certain terrain types. */
194 if (pdata->value[tile_index(ptile)] < pdata->min_value) {
195 return FALSE;
196 }
197
198 fc_assert_ret_val(cont > 0, FALSE);
199 if (islands[islands_index[cont]].starters == 0) {
200 return FALSE;
201 }
202
203 /* Don't start on a hut. */
204 /* FIXME: for HUT_NOTHING might be valid */
205 if (hut_on_tile(ptile)) {
206 return FALSE;
207 }
208
209 /* Has to be native tile for initial unit */
210 if (!is_native_tile(pdata->initial_unit, ptile)) {
211 return FALSE;
212 }
213
214 /* Check native area size. */
215 if (!check_native_area(pdata->initial_unit, ptile,
216 terrain_control.min_start_native_area)) {
217 return FALSE;
218 }
219
220 if (game.server.start_city && terrain_has_flag(tile_terrain(ptile), TER_NO_CITIES)) {
221 return FALSE;
222 }
223
224 /* A longstanding bug allowed starting positions to exist on poles,
225 * sometimes. This hack prevents it by setting a fixed distance from
226 * the pole (dependent on map temperature) that a start pos must be.
227 * Cold and frozen tiles are not allowed for start pos placement. */
228 if (tmap_is(ptile, TT_NHOT)) {
229 return FALSE;
230 }
231
232 /* Don't start too close to someone else. */
233 cont_size = get_continent_size(cont);
234 island = islands + islands_index[cont];
236 struct tile *tile1 = startpos_tile(psp);
237
238 if ((tile_continent(ptile) == tile_continent(tile1)
239 && (real_map_distance(ptile, tile1) * 1000 / pdata->min_value
240 <= (sqrt(cont_size / island->total))))
241 || (real_map_distance(ptile, tile1) * 1000 / pdata->min_value < 5)) {
242 return FALSE;
243 }
245 return TRUE;
246}
247
248/************************************************************************/
251static int compare_islands(const void *A_, const void *B_)
252{
253 const struct islands_data_type *A = A_, *B = B_;
254
255 return B->goodies - A->goodies;
256}
257
258/************************************************************************/
261static void initialize_isle_data(void)
262{
263 int nr;
264
265 islands = fc_malloc((wld.map.num_continents + 1) * sizeof(*islands));
267 * sizeof(*islands_index));
268
269 /* islands[0] is unused. */
270 for (nr = 1; nr <= wld.map.num_continents; nr++) {
271 islands[nr].id = nr;
273 islands[nr].goodies = 0;
274 islands[nr].starters = 0;
275 islands[nr].total = 0;
276 }
277}
278
279/************************************************************************/
282static bool filter_starters(const struct tile *ptile, const void *data)
283{
284 return terrain_has_flag(tile_terrain(ptile), TER_STARTER);
285}
286
287/************************************************************************/
300 struct unit_type *initial_unit)
301{
302 struct tile *ptile;
303 int k, sum;
304 struct start_filter_data data;
305 int *tile_value_aux = NULL;
306 int *tile_value = NULL;
307 int min_goodies_per_player = 1500;
308 int total_goodies = 0;
309 /* This is factor is used to maximize land used in extreme little maps */
310 float efactor = player_count() / map_size_checked() / 4;
311 bool failure = FALSE;
312 bool is_tmap = temperature_is_initialized();
313 const struct civ_map *nmap = &(wld.map);
314
315 if (wld.map.num_continents < 1) {
316 /* Currently we can only place starters on land terrain, so fail
317 * immediately if there isn't any on the map. */
318 log_verbose("Map has no land, so cannot assign start positions!");
319 return FALSE;
320 }
321
322 if (!is_tmap) {
323 /* The temperature map has already been destroyed by the time start
324 * positions have been placed. We check for this and then create a
325 * false temperature map. This is used in the tmap_is() call above.
326 * We don't create a "real" map here because that requires the height
327 * map and other information which has already been destroyed. */
329 }
330
331 /* If the default is given, just use MAPSTARTPOS_VARIABLE. */
332 if (MAPSTARTPOS_DEFAULT == mode) {
333 log_verbose("Using startpos=VARIABLE");
335 }
336
337 tile_value_aux = fc_calloc(MAP_INDEX_SIZE, sizeof(*tile_value_aux));
338 tile_value = fc_calloc(MAP_INDEX_SIZE, sizeof(*tile_value));
339
340 /* Get the tile value */
341 whole_map_iterate(&(wld.map), value_tile) {
342 tile_value_aux[tile_index(value_tile)] = get_tile_value(value_tile);
344
345 /* Select the best tiles */
346 whole_map_iterate(&(wld.map), value_tile) {
347 int this_tile_value = tile_value_aux[tile_index(value_tile)];
348 int lcount = 0, bcount = 0;
349
350 /* Check all tiles within the default city radius */
351 city_tile_iterate(nmap, CITY_MAP_DEFAULT_RADIUS_SQ, value_tile, ptile1) {
352 if (this_tile_value > tile_value_aux[tile_index(ptile1)]) {
353 lcount++;
354 } else if (this_tile_value < tile_value_aux[tile_index(ptile1)]) {
355 bcount++;
356 }
358
359 if (lcount <= bcount) {
360 this_tile_value = 0;
361 }
362 tile_value[tile_index(value_tile)] = 100 * this_tile_value;
364 /* Get an average value */
365 smooth_int_map(tile_value, TRUE);
366
368
369 /* Only consider tiles marked as 'starter terrains' by ruleset */
370 whole_map_iterate(&(wld.map), starter_tile) {
371 if (!filter_starters(starter_tile, NULL)) {
372 tile_value[tile_index(starter_tile)] = 0;
373 } else {
374 /* Oceanic terrain cannot be starter terrain currently */
375 fc_assert_action(tile_continent(starter_tile) > 0, continue);
376 islands[tile_continent(starter_tile)].goodies += tile_value[tile_index(starter_tile)];
377 total_goodies += tile_value[tile_index(starter_tile)];
378 }
380
381 /* evaluate the best places on the map */
382 adjust_int_map_filtered(tile_value, 1000, NULL, filter_starters);
383
384 /* Sort the islands so the best ones come first. Note that islands[0] is
385 * unused so we just skip it. */
386 qsort(islands + 1, wld.map.num_continents,
387 sizeof(*islands), compare_islands);
388
389 /* If we can't place starters according to the first choice, change the
390 * choice. */
391 if (MAPSTARTPOS_SINGLE == mode
392 && wld.map.num_continents < player_count() + 3) {
393 log_verbose("Not enough continents; falling back to startpos=2or3");
394 mode = MAPSTARTPOS_2or3;
395 }
396
397 if (MAPSTARTPOS_2or3 == mode
398 && wld.map.num_continents < player_count() / 2 + 4) {
399 log_verbose("Not enough continents; falling back to startpos=VARIABLE");
401 }
402
403 if (MAPSTARTPOS_ALL == mode
404 && (islands[1].goodies < player_count() * min_goodies_per_player
405 || islands[1].goodies < total_goodies * (0.5 + 0.8 * efactor)
406 / (1 + efactor))) {
407 log_verbose("No good enough island; falling back to startpos=VARIABLE");
409 }
410
411 /* the variable way is the last possibility */
412 if (MAPSTARTPOS_VARIABLE == mode) {
413 min_goodies_per_player = total_goodies * (0.65 + 0.8 * efactor)
414 / (1 + efactor) / player_count();
415 }
416
417 {
418 int nr, to_place = player_count(), first = 1;
419
420 /* Initialize islands_index */
421 for (nr = 1; nr <= wld.map.num_continents; nr++) {
422 islands_index[islands[nr].id] = nr;
423 }
424
425 /* When placing a fixed number of players per island, for fairness, try
426 * to avoid sets of populated islands where there is more than 10%
427 * variation in "goodness" within the entire set. (Fallback if that fails:
428 * place players on the worst available islands.) */
429 if (MAPSTARTPOS_SINGLE == mode || MAPSTARTPOS_2or3 == mode) {
430 float var_goodies, best = HUGE_VAL;
431 int num_islands = (MAPSTARTPOS_SINGLE == mode
432 ? player_count() : player_count() / 2);
433
434 for (nr = 1; nr <= 1 + wld.map.num_continents - num_islands; nr++) {
435 if (islands[nr + num_islands - 1].goodies < min_goodies_per_player) {
436 break;
437 }
438 var_goodies
439 = (islands[nr].goodies - islands[nr + num_islands - 1].goodies)
440 / (islands[nr + num_islands - 1].goodies);
441
442 if (var_goodies < best * 0.9) {
443 best = var_goodies;
444 first = nr;
445 }
446 }
447 }
448
449 /* set starters per isle */
450 if (MAPSTARTPOS_ALL == mode) {
451 islands[1].starters = to_place;
452 islands[1].total = to_place;
453 to_place = 0;
454 }
455 for (nr = 1; nr <= wld.map.num_continents; nr++) {
456 if (MAPSTARTPOS_SINGLE == mode && 0 < to_place && nr >= first) {
457 islands[nr].starters = 1;
458 islands[nr].total = 1;
459 to_place--;
460 }
461 if (MAPSTARTPOS_2or3 == mode && 0 < to_place && nr >= first) {
462 islands[nr].starters = 2 + (nr == 1 ? (player_count() % 2) : 0);
463 to_place -= islands[nr].total = islands[nr].starters;
464 }
465
466 if (MAPSTARTPOS_VARIABLE == mode && 0 < to_place) {
467 islands[nr].starters = MAX(1, islands[nr].goodies
468 / MAX(1, min_goodies_per_player));
469 to_place -= islands[nr].total = islands[nr].starters;
470 }
471 }
472 }
473
474 data.value = tile_value;
475 data.min_value = 900;
476 data.initial_unit = initial_unit;
477 sum = 0;
478 for (k = 1; k <= wld.map.num_continents; k++) {
479 sum += islands[islands_index[k]].starters;
480 if (islands[islands_index[k]].starters != 0) {
481 log_verbose("starters on isle %i", k);
482 }
483 }
485
486 /* now search for the best place and set start_positions */
487 while (map_startpos_count() < player_count()) {
488 if ((ptile = rand_map_pos_filtered(&(wld.map), &data,
491 log_debug("Adding (%d, %d) as starting position %d, %d goodies on "
492 "islands.", TILE_XY(ptile), map_startpos_count(),
493 islands[islands_index[(int) tile_continent(ptile)]].goodies);
494 (void) map_startpos_new(ptile);
495 } else {
496 data.min_value *= 0.95;
497 if (data.min_value <= 10) {
498 log_normal(_("The server appears to have gotten into an infinite "
499 "loop in the allocation of starting positions.\nMaybe "
500 "the number of players is too high for this map."));
501 failure = TRUE;
502 break;
503 }
504 }
505 }
506
507 free(islands);
508 free(islands_index);
509 islands = NULL;
510 islands_index = NULL;
511
512 if (!is_tmap) {
513 destroy_tmap();
514 }
515
516 FC_FREE(tile_value_aux);
517 FC_FREE(tile_value);
518
519 return !failure;
520}
void dbv_init(struct dbv *pdbv, int bits)
Definition bitvector.c:50
void dbv_set(struct dbv *pdbv, int bit)
Definition bitvector.c:144
bool dbv_isset(const struct dbv *pdbv, int bit)
Definition bitvector.c:120
void dbv_free(struct dbv *pdbv)
Definition bitvector.c:95
int city_tile_output(const struct city *pcity, const struct tile *ptile, bool is_celebrating, Output_type_id otype)
Definition city.c:1259
#define output_type_iterate(output)
Definition city.h:821
#define city_tile_iterate(_nmap, _radius_sq, _city_tile, _tile)
Definition city.h:222
#define CITY_MAP_DEFAULT_RADIUS_SQ
Definition city.h:74
#define city_tile_iterate_end
Definition city.h:230
#define output_type_iterate_end
Definition city.h:827
struct extra_type * next_extra_for_tile(const struct tile *ptile, enum extra_cause cause, const struct player *pplayer, const struct unit *punit)
Definition extras.c:740
bool hut_on_tile(const struct tile *ptile)
Definition extras.c:672
#define extra_road_get(_e_)
Definition extras.h:185
#define extra_type_by_cause_iterate_end
Definition extras.h:315
#define extra_type_by_cause_iterate(_cause, _extra)
Definition extras.h:309
@ RPT_CERTAIN
Definition fc_types.h:586
signed short Continent_id
Definition fc_types.h:342
#define _(String)
Definition fcintl.h:67
struct civ_game game
Definition game.c:57
struct world wld
Definition game.c:58
#define log_verbose(message,...)
Definition log.h:109
#define fc_assert_ret_val(condition, val)
Definition log.h:194
#define fc_assert_action(condition, action)
Definition log.h:187
#define log_debug(message,...)
Definition log.h:115
#define log_normal(message,...)
Definition log.h:107
struct startpos * map_startpos_new(struct tile *ptile)
Definition map.c:1669
struct tile * startpos_tile(const struct startpos *psp)
Definition map.c:1479
struct tile * rand_map_pos_filtered(const struct civ_map *nmap, void *data, bool(*filter)(const struct tile *ptile, const void *data))
Definition map.c:1099
int map_startpos_count(void)
Definition map.c:1656
int real_map_distance(const struct tile *tile0, const struct tile *tile1)
Definition map.c:628
struct terrain_misc terrain_control
Definition map.c:69
#define map_startpos_iterate(NAME_psp)
Definition map.h:124
#define adjc_iterate_end
Definition map.h:427
#define MAP_INDEX_SIZE
Definition map.h:131
#define adjc_iterate(nmap, center_tile, itr_tile)
Definition map.h:422
#define map_startpos_iterate_end
Definition map.h:127
#define whole_map_iterate(_map, _tile)
Definition map.h:539
#define map_size_checked()
Definition map.h:265
#define whole_map_iterate_end
Definition map.h:548
map_startpos
Definition map_types.h:55
@ MAPSTARTPOS_VARIABLE
Definition map_types.h:60
@ MAPSTARTPOS_2or3
Definition map_types.h:58
@ MAPSTARTPOS_ALL
Definition map_types.h:59
@ MAPSTARTPOS_DEFAULT
Definition map_types.h:56
@ MAPSTARTPOS_SINGLE
Definition map_types.h:57
int get_continent_size(Continent_id id)
void smooth_int_map(int *int_map, bool zeroes_at_edges)
void adjust_int_map_filtered(int *int_map, int int_map_max, void *data, bool(*filter)(const struct tile *ptile, const void *data))
#define fc_calloc(n, esz)
Definition mem.h:38
#define FC_FREE(ptr)
Definition mem.h:41
#define fc_malloc(sz)
Definition mem.h:34
bool is_native_tile(const struct unit_type *punittype, const struct tile *ptile)
Definition movement.c:316
int player_count(void)
Definition player.c:808
bool are_reqs_active(const struct req_context *context, const struct player *other_player, const struct requirement_vector *reqs, const enum req_problem_type prob_type)
bool road_can_be_built(const struct road_type *proad, const struct tile *ptile)
Definition road.c:200
#define MAX(x, y)
Definition shared.h:54
static int compare_islands(const void *A_, const void *B_)
Definition startpos.c:251
bool create_start_positions(enum map_startpos mode, struct unit_type *initial_unit)
Definition startpos.c:299
static bool filter_starters(const struct tile *ptile, const void *data)
Definition startpos.c:282
static void initialize_isle_data(void)
Definition startpos.c:261
static int * islands_index
Definition startpos.c:46
static struct islands_data_type * islands
Definition startpos.c:45
static int get_tile_value(struct tile *ptile)
Definition startpos.c:51
static bool check_native_area(const struct unit_type *utype, const struct tile *ptile, int min_area)
Definition startpos.c:130
static bool is_valid_start_pos(const struct tile *ptile, const void *dataptr)
Definition startpos.c:187
struct civ_game::@30::@34 server
bool start_city
Definition game.h:189
int num_continents
Definition map_types.h:78
Continent_id id
Definition startpos.c:39
const struct tile * tile
struct unit_type * initial_unit
Definition startpos.c:122
Definition tile.h:49
struct civ_map map
#define TRUE
Definition support.h:46
#define FALSE
Definition support.h:47
bool temperature_is_initialized(void)
void destroy_tmap(void)
bool tmap_is(const struct tile *ptile, temperature_type tt)
void create_tmap(bool real)
#define TT_NHOT
#define terrain_has_flag(terr, flag)
Definition terrain.h:269
void tile_add_extra(struct tile *ptile, const struct extra_type *pextra)
Definition tile.c:940
void tile_virtual_destroy(struct tile *vtile)
Definition tile.c:1018
bool tile_apply_activity(struct tile *ptile, Activity_type_id act, struct extra_type *tgt)
Definition tile.c:658
struct tile * tile_virtual_new(const struct tile *ptile)
Definition tile.c:966
#define tile_index(_pt_)
Definition tile.h:87
#define tile_list_iterate(tile_list, ptile)
Definition tile.h:72
#define tile_terrain(_tile)
Definition tile.h:109
#define TILE_XY(ptile)
Definition tile.h:42
#define tile_list_iterate_end
Definition tile.h:74
#define tile_continent(_tile)
Definition tile.h:91
struct unit_type * get_role_unit(int role, int role_index)
Definition unittype.c:2301
int num_role_units(int role)
Definition unittype.c:2251