1 /**********************************************************************
2 Freeciv - Copyright (C) 2003 - The Freeciv Project
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 <stdio.h>
19 #include <string.h>
20
21 /* utility */
22 #include "bitvector.h"
23 #include "log.h"
24 #include "mem.h"
25 #include "shared.h"
26
27 /* common */
28 #include "culture.h"
29 #include "game.h"
30 #include "improvement.h"
31 #include "map.h"
32 #include "player.h"
33 #include "research.h"
34 #include "specialist.h"
35 #include "unit.h"
36 #include "unitlist.h"
37
38 /* server */
39 #include "plrhand.h"
40 #include "score.h"
41 #include "srv_main.h"
42
43 static int get_spaceship_score(const struct player *pplayer);
44
45 /**************************************************************************
46 Allocates, fills and returns a land area claim map.
47 Call free_landarea_map(&cmap) to free allocated memory.
48 **************************************************************************/
49
50 #define USER_AREA_MULT 1000
51
52 struct claim_map {
53 struct {
54 int landarea, settledarea;
55 } player[MAX_NUM_PLAYER_SLOTS];
56 };
57
58 /**************************************************************************
59 Land Area Debug...
60 **************************************************************************/
61
62 #define LAND_AREA_DEBUG 0
63
64 #if LAND_AREA_DEBUG >= 2
65
66 /**************************************************************************
67 Return one character representation of the turn number 'when'.
68 If value of 'when' is out of supported range, '?' is returned.
69 **************************************************************************/
when_char(int when)70 static char when_char(int when)
71 {
72 static char list[] = {
73 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
74 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
75 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
76 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
77 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
78 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x',
79 'y', 'z'
80 };
81
82 return (when >= 0 && when < sizeof(list)) ? list[when] : '?';
83 }
84
85 /*
86 * Writes the map_char_expr expression for each position on the map.
87 * map_char_expr is provided with the variables x,y to evaluate the
88 * position. The 'type' argument is used for formatting by printf; for
89 * instance it should be "%c" for characters. The data is printed in a
90 * native orientation to make it easier to read.
91 */
92 #define WRITE_MAP_DATA(type, map_char_expr) \
93 { \
94 int nat_x, nat_y; \
95 for (nat_x = 0; nat_x < map.xsize; nat_x++) { \
96 printf("%d", nat_x % 10); \
97 } \
98 putchar('\n'); \
99 for (nat_y = 0; nat_y < map.ysize; nat_y++) { \
100 printf("%d ", nat_y % 10); \
101 for (nat_x = 0; nat_x < map.xsize; nat_x++) { \
102 int x, y; \
103 NATIVE_TO_MAP_POS(&x, &y, nat_x,nat_y); \
104 printf(type, map_char_expr); \
105 } \
106 printf(" %d\n", nat_y % 10); \
107 } \
108 }
109
110 /**************************************************************************
111 Prints the landarea map to stdout (a debugging tool).
112 *******************************************o*******************************/
print_landarea_map(struct claim_map * pcmap,int turn)113 static void print_landarea_map(struct claim_map *pcmap, int turn)
114 {
115 int p;
116
117 player_slots_iterate(pslot) {
118 if (player_slot_index(pslot) >= 32 && player_slot_is_used(pslot)) {
119 log_error("Debugging not possible! Player slots >= 32 are used.");
120 return;
121 }
122 } player_slots_iterate_end;
123
124 if (turn == 0) {
125 putchar('\n');
126 }
127
128 if (turn == 0) {
129 printf("Player Info...\n");
130
131 for (p = 0; p < player_count(); p++) {
132 printf(".know (%d)\n ", p);
133 WRITE_MAP_DATA("%c",
134 BV_ISSET(pcmap->claims[map_pos_to_index(x, y)].know,
135 p) ? 'X' : '-');
136 printf(".cities (%d)\n ", p);
137 WRITE_MAP_DATA("%c",
138 BV_ISSET(pcmap->
139 claims[map_pos_to_index(x, y)].cities,
140 p) ? 'O' : '-');
141 }
142 }
143
144 printf("Turn %d (%c)...\n", turn, when_char (turn));
145
146 printf(".whom\n ");
147 WRITE_MAP_DATA((pcmap->claims[map_pos_to_index(x, y)].whom ==
148 32) ? "%c" : "%X",
149 (pcmap->claims[map_pos_to_index(x, y)].whom ==
150 32) ? '-' : pcmap->claims[map_pos_to_index(x, y)].whom);
151
152 printf(".when\n ");
153 WRITE_MAP_DATA("%c", when_char(pcmap->claims[map_pos_to_index(x, y)].when));
154 }
155
156 #endif /* LAND_AREA_DEBUG > 2 */
157
158 /****************************************************************************
159 Count landarea, settled area, and claims map for all players.
160 ****************************************************************************/
build_landarea_map(struct claim_map * pcmap)161 static void build_landarea_map(struct claim_map *pcmap)
162 {
163 bv_player *claims = fc_calloc(MAP_INDEX_SIZE, sizeof(*claims));
164
165 memset(pcmap, 0, sizeof(*pcmap));
166
167 /* First calculate claims: which tiles are owned by each player. */
168 players_iterate(pplayer) {
169 city_list_iterate(pplayer->cities, pcity) {
170 struct tile *pcenter = city_tile(pcity);
171
172 city_tile_iterate(city_map_radius_sq_get(pcity), pcenter, tile1) {
173 BV_SET(claims[tile_index(tile1)], player_index(city_owner(pcity)));
174 } city_tile_iterate_end;
175 } city_list_iterate_end;
176 } players_iterate_end;
177
178 whole_map_iterate(ptile) {
179 struct player *owner = NULL;
180 bv_player *pclaim = &claims[tile_index(ptile)];
181
182 if (is_ocean_tile(ptile)) {
183 /* Nothing. */
184 } else if (NULL != tile_city(ptile)) {
185 owner = city_owner(tile_city(ptile));
186 pcmap->player[player_index(owner)].settledarea++;
187 } else if (NULL != tile_worked(ptile)) {
188 owner = city_owner(tile_worked(ptile));
189 pcmap->player[player_index(owner)].settledarea++;
190 } else if (unit_list_size(ptile->units) > 0) {
191 /* Because of allied stacking these calculations are a bit off. */
192 owner = unit_owner(unit_list_get(ptile->units, 0));
193 if (BV_ISSET(*pclaim, player_index(owner))) {
194 pcmap->player[player_index(owner)].settledarea++;
195 }
196 }
197
198 if (BORDERS_DISABLED != game.info.borders) {
199 /* If borders are enabled, use owner information directly from the
200 * map. Otherwise use the calculations above. */
201 owner = tile_owner(ptile);
202 }
203 if (owner) {
204 pcmap->player[player_index(owner)].landarea++;
205 }
206 } whole_map_iterate_end;
207
208 FC_FREE(claims);
209
210 #if LAND_AREA_DEBUG >= 2
211 print_landarea_map(pcmap, turn);
212 #endif
213 }
214
215 /**************************************************************************
216 Returns the given player's land and settled areas from a claim map.
217 **************************************************************************/
get_player_landarea(struct claim_map * pcmap,struct player * pplayer,int * return_landarea,int * return_settledarea)218 static void get_player_landarea(struct claim_map *pcmap,
219 struct player *pplayer,
220 int *return_landarea,
221 int *return_settledarea)
222 {
223 if (pcmap && pplayer) {
224 #if LAND_AREA_DEBUG >= 1
225 printf("%-14s", player_name(pplayer));
226 #endif
227 if (return_landarea) {
228 *return_landarea
229 = USER_AREA_MULT * pcmap->player[player_index(pplayer)].landarea;
230 #if LAND_AREA_DEBUG >= 1
231 printf(" l=%d", *return_landarea / USER_AREA_MULT);
232 #endif
233 }
234 if (return_settledarea) {
235 *return_settledarea
236 = USER_AREA_MULT * pcmap->player[player_index(pplayer)].settledarea;
237 #if LAND_AREA_DEBUG >= 1
238 printf(" s=%d", *return_settledarea / USER_AREA_MULT);
239 #endif
240 }
241 #if LAND_AREA_DEBUG >= 1
242 printf("\n");
243 #endif
244 }
245 }
246
247 /**************************************************************************
248 Calculates the civilization score for the player.
249 **************************************************************************/
calc_civ_score(struct player * pplayer)250 void calc_civ_score(struct player *pplayer)
251 {
252 const struct research *presearch;
253 struct city *wonder_city;
254 int landarea = 0, settledarea = 0;
255 static struct claim_map cmap;
256
257 pplayer->score.happy = 0;
258 pplayer->score.content = 0;
259 pplayer->score.unhappy = 0;
260 pplayer->score.angry = 0;
261 specialist_type_iterate(sp) {
262 pplayer->score.specialists[sp] = 0;
263 } specialist_type_iterate_end;
264 pplayer->score.wonders = 0;
265 pplayer->score.techs = 0;
266 pplayer->score.techout = 0;
267 pplayer->score.landarea = 0;
268 pplayer->score.settledarea = 0;
269 pplayer->score.population = 0;
270 pplayer->score.cities = 0;
271 pplayer->score.units = 0;
272 pplayer->score.pollution = 0;
273 pplayer->score.bnp = 0;
274 pplayer->score.mfg = 0;
275 pplayer->score.literacy = 0;
276 pplayer->score.spaceship = 0;
277 pplayer->score.culture = player_culture(pplayer);
278
279 if (is_barbarian(pplayer)) {
280 return;
281 }
282
283 city_list_iterate(pplayer->cities, pcity) {
284 int bonus;
285
286 pplayer->score.happy += pcity->feel[CITIZEN_HAPPY][FEELING_FINAL];
287 pplayer->score.content += pcity->feel[CITIZEN_CONTENT][FEELING_FINAL];
288 pplayer->score.unhappy += pcity->feel[CITIZEN_UNHAPPY][FEELING_FINAL];
289 pplayer->score.angry += pcity->feel[CITIZEN_ANGRY][FEELING_FINAL];
290 specialist_type_iterate(sp) {
291 pplayer->score.specialists[sp] += pcity->specialists[sp];
292 } specialist_type_iterate_end;
293 pplayer->score.population += city_population(pcity);
294 pplayer->score.cities++;
295 pplayer->score.pollution += pcity->pollution;
296 pplayer->score.techout += pcity->prod[O_SCIENCE];
297 pplayer->score.bnp += pcity->surplus[O_TRADE];
298 pplayer->score.mfg += pcity->surplus[O_SHIELD];
299
300 bonus = get_final_city_output_bonus(pcity, O_SCIENCE) - 100;
301 bonus = CLIP(0, bonus, 100);
302 pplayer->score.literacy += (city_population(pcity) * bonus) / 100;
303 } city_list_iterate_end;
304
305 build_landarea_map(&cmap);
306
307 get_player_landarea(&cmap, pplayer, &landarea, &settledarea);
308 pplayer->score.landarea = landarea;
309 pplayer->score.settledarea = settledarea;
310
311 presearch = research_get(pplayer);
312 advance_index_iterate(A_FIRST, i) {
313 if (research_invention_state(presearch, i) == TECH_KNOWN) {
314 pplayer->score.techs++;
315 }
316 } advance_index_iterate_end;
317 pplayer->score.techs += research_get(pplayer)->future_tech * 5 / 2;
318
319 unit_list_iterate(pplayer->units, punit) {
320 if (is_military_unit(punit)) {
321 pplayer->score.units++;
322 }
323 } unit_list_iterate_end
324
325 improvement_iterate(i) {
326 if (is_great_wonder(i)
327 && (wonder_city = city_from_great_wonder(i))
328 && player_owns_city(pplayer, wonder_city)) {
329 pplayer->score.wonders++;
330 }
331 } improvement_iterate_end;
332
333 pplayer->score.spaceship = pplayer->spaceship.state;
334
335 pplayer->score.game = get_civ_score(pplayer);
336 }
337
338 /**************************************************************************
339 Return the score given by the units stats.
340 **************************************************************************/
get_units_score(const struct player * pplayer)341 static int get_units_score(const struct player *pplayer)
342 {
343 return (pplayer->score.units_built / 10
344 + pplayer->score.units_killed / 3);
345 }
346
347 /**************************************************************************
348 Return the civilization score (a numerical value) for the player.
349 **************************************************************************/
get_civ_score(const struct player * pplayer)350 int get_civ_score(const struct player *pplayer)
351 {
352 /* We used to count pplayer->score.happy here too, but this is too easily
353 * manipulated by players at the endturn. */
354 return (total_player_citizens(pplayer)
355 + pplayer->score.techs * 2
356 + pplayer->score.wonders * 5
357 + get_spaceship_score(pplayer)
358 + get_units_score(pplayer)
359 + pplayer->score.culture / 50);
360 }
361
362 /**************************************************************************
363 Return the spaceship score
364 **************************************************************************/
get_spaceship_score(const struct player * pplayer)365 static int get_spaceship_score(const struct player *pplayer)
366 {
367 if (pplayer->score.spaceship == SSHIP_ARRIVED) {
368 /* How much should a spaceship be worth?
369 * This gives 100 points per 10,000 citizens. */
370 return (int)((pplayer->spaceship.population
371 * pplayer->spaceship.success_rate) / 100.0);
372 } else {
373 return 0;
374 }
375 }
376
377 /**************************************************************************
378 Return the total number of citizens in the player's nation.
379 **************************************************************************/
total_player_citizens(const struct player * pplayer)380 int total_player_citizens(const struct player *pplayer)
381 {
382 int count = (pplayer->score.happy
383 + pplayer->score.content
384 + pplayer->score.unhappy
385 + pplayer->score.angry);
386
387 specialist_type_iterate(sp) {
388 count += pplayer->score.specialists[sp];
389 } specialist_type_iterate_end;
390
391 return count;
392 }
393
394 /**************************************************************************
395 At the end of a game, figure the winners and losers of the game and
396 output to a suitable place.
397
398 The definition of winners and losers: a winner is one who is alive at the
399 end of the game and has not surrendered, or in the case of a team game,
400 is alive or a teammate is alive and has not surrendered. A loser is
401 surrendered or dead. Exception: the winner of the spacerace and his
402 teammates will win of course.
403
404 In games ended by /endgame, endturn, or any other interruption not caused
405 by satisfaction of victory conditions, for each team is calculated the sum
406 of the scores of any belonging member which is alive and has not
407 surrendered; all the players in the team with the higest sum of scores win.
408 This condition is signaled to the function by the boolean "interrupt".
409
410 Barbarians do not count as winners or losers.
411
412 If interrupt is true, rank players by team score rather than by alive/dead
413 status.
414 **************************************************************************/
rank_users(bool interrupt)415 void rank_users(bool interrupt)
416 {
417 FILE *fp;
418 int i, t_winner_score = 0;
419 enum victory_state { VS_NONE, VS_LOSER, VS_WINNER };
420 enum victory_state plr_state[player_slot_count()];
421 struct player *spacerace_winner = NULL;
422 struct team *t_winner = NULL;
423
424 /* don't output ranking info if we haven't enabled it via cmdline */
425 if (!srvarg.ranklog_filename) {
426 return;
427 }
428
429 fp = fc_fopen(srvarg.ranklog_filename, "w");
430
431 /* don't fail silently, at least print an error */
432 if (!fp) {
433 log_error("couldn't open ranking log file: \"%s\"",
434 srvarg.ranklog_filename);
435 return;
436 }
437
438 /* initialize plr_state */
439 for (i = 0; i < player_slot_count(); i++) {
440 plr_state[i] = VS_NONE;
441 }
442
443 /* do we have a spacerace winner? */
444 players_iterate(pplayer) {
445 if (pplayer->spaceship.state == SSHIP_ARRIVED) {
446 spacerace_winner = pplayer;
447 break;
448 }
449 } players_iterate_end;
450
451 /* make this easy: if we have a spacerace winner, then treat all others
452 * who are still alive as surrendered */
453 if (spacerace_winner) {
454 players_iterate(pplayer) {
455 if (pplayer != spacerace_winner) {
456 player_status_add(pplayer, PSTATUS_SURRENDER);
457 }
458 } players_iterate_end;
459 }
460
461 if (interrupt == FALSE) {
462 /* game ended for a victory condition */
463
464 /* first pass: locate those alive who haven't surrendered, set them to
465 * win; barbarians won't count, and everybody else is a loser for now. */
466 players_iterate(pplayer) {
467 if (is_barbarian(pplayer)) {
468 plr_state[player_index(pplayer)] = VS_NONE;
469 } else if (pplayer->is_alive
470 && !player_status_check(pplayer, PSTATUS_SURRENDER)) {
471 plr_state[player_index(pplayer)] = VS_WINNER;
472 } else {
473 plr_state[player_index(pplayer)] = VS_LOSER;
474 }
475 } players_iterate_end;
476
477 /* second pass: find the teammates of those winners, they win too. */
478 players_iterate(pplayer) {
479 if (plr_state[player_index(pplayer)] == VS_WINNER) {
480 players_iterate(aplayer) {
481 if (aplayer->team == pplayer->team) {
482 plr_state[player_index(aplayer)] = VS_WINNER;
483 }
484 } players_iterate_end;
485 }
486 } players_iterate_end;
487 } else {
488
489 /* game ended via endturn */
490 /* i) determine the winner team */
491 teams_iterate(pteam) {
492 int t_score = 0;
493 const struct player_list *members = team_members(pteam);
494 player_list_iterate(members, pplayer) {
495 if (pplayer->is_alive
496 && !player_status_check(pplayer, PSTATUS_SURRENDER)) {
497 t_score += get_civ_score(pplayer);
498 }
499 } player_list_iterate_end;
500 if (t_score > t_winner_score) {
501 t_winner = pteam;
502 t_winner_score = t_score;
503 }
504 } teams_iterate_end;
505
506 /* ii) set all the members of the team as winners, the others as losers */
507 players_iterate(pplayer) {
508 if (pplayer->team == t_winner) {
509 plr_state[player_index(pplayer)] = VS_WINNER;
510 } else {
511 /* if no winner team is found (each one as same score) all them lose */
512 plr_state[player_index(pplayer)] = VS_LOSER;
513 }
514 } players_iterate_end;
515 }
516
517 /* write out ranking information to file */
518 fprintf(fp, "turns: %d\n", game.info.turn);
519 fprintf(fp, "winners: ");
520 players_iterate(pplayer) {
521 if (plr_state[player_index(pplayer)] == VS_WINNER) {
522 fprintf(fp, "%s,%s,%s,%i,, ", pplayer->ranked_username,
523 player_name(pplayer),
524 pplayer->username,
525 get_civ_score(pplayer));
526 }
527 } players_iterate_end;
528 fprintf(fp, "\nlosers: ");
529 players_iterate(pplayer) {
530 if (plr_state[player_index(pplayer)] == VS_LOSER) {
531 fprintf(fp, "%s,%s,%s,%i,, ", pplayer->ranked_username,
532 player_name(pplayer),
533 pplayer->username,
534 get_civ_score(pplayer));
535 }
536 } players_iterate_end;
537 fprintf(fp, "\n");
538
539 fclose(fp);
540 }
541