Do not build gtk3.22-client by default
[freeciv.git] / server / score.c
blob707c9d8a508ee5205b30f86b4f075823a67d4ae2
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.
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 ***********************************************************************/
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
18 #include <stdio.h>
19 #include <string.h>
21 /* utility */
22 #include "bitvector.h"
23 #include "log.h"
24 #include "mem.h"
25 #include "shared.h"
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"
38 /* server */
39 #include "plrhand.h"
40 #include "score.h"
41 #include "srv_main.h"
43 static int get_spaceship_score(const struct player *pplayer);
45 /**************************************************************************
46 Allocates, fills and returns a land area claim map.
47 Call free_landarea_map(&cmap) to free allocated memory.
48 **************************************************************************/
50 #define USER_AREA_MULT 1000
52 struct claim_map {
53 struct {
54 int landarea, settledarea;
55 } player[MAX_NUM_PLAYER_SLOTS];
58 /**************************************************************************
59 Land Area Debug...
60 **************************************************************************/
62 #define LAND_AREA_DEBUG 0
64 #if LAND_AREA_DEBUG >= 2
66 /**************************************************************************
67 Return one character representation of the turn number 'when'.
68 If value of 'when' is out of supported range, '?' is returned.
69 **************************************************************************/
70 static char when_char(int when)
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'
82 return (when >= 0 && when < sizeof(list)) ? list[when] : '?';
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.
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); \
106 printf(" %d\n", nat_y % 10); \
110 /**************************************************************************
111 Prints the landarea map to stdout (a debugging tool).
112 *******************************************o*******************************/
113 static void print_landarea_map(struct claim_map *pcmap, int turn)
115 int p;
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;
122 } player_slots_iterate_end;
124 if (turn == 0) {
125 putchar('\n');
128 if (turn == 0) {
129 printf("Player Info...\n");
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' : '-');
144 printf("Turn %d (%c)...\n", turn, when_char (turn));
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);
152 printf(".when\n ");
153 WRITE_MAP_DATA("%c", when_char(pcmap->claims[map_pos_to_index(x, y)].when));
156 #endif /* LAND_AREA_DEBUG > 2 */
158 /****************************************************************************
159 Count landarea, settled area, and claims map for all players.
160 ****************************************************************************/
161 static void build_landarea_map(struct claim_map *pcmap)
163 bv_player *claims = fc_calloc(MAP_INDEX_SIZE, sizeof(*claims));
165 memset(pcmap, 0, sizeof(*pcmap));
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);
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;
178 whole_map_iterate(ptile) {
179 struct player *owner = NULL;
180 bv_player *pclaim = &claims[tile_index(ptile)];
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++;
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);
203 if (owner) {
204 pcmap->player[player_index(owner)].landarea++;
206 } whole_map_iterate_end;
208 FC_FREE(claims);
210 #if LAND_AREA_DEBUG >= 2
211 print_landarea_map(pcmap, turn);
212 #endif
215 /**************************************************************************
216 Returns the given player's land and settled areas from a claim map.
217 **************************************************************************/
218 static void get_player_landarea(struct claim_map *pcmap,
219 struct player *pplayer,
220 int *return_landarea,
221 int *return_settledarea)
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
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
241 #if LAND_AREA_DEBUG >= 1
242 printf("\n");
243 #endif
247 /**************************************************************************
248 Calculates the civilization score for the player.
249 **************************************************************************/
250 void calc_civ_score(struct player *pplayer)
252 const struct research *presearch;
253 struct city *wonder_city;
254 int landarea = 0, settledarea = 0;
255 static struct claim_map cmap;
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);
279 if (is_barbarian(pplayer)) {
280 return;
283 city_list_iterate(pplayer->cities, pcity) {
284 int bonus;
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];
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;
305 build_landarea_map(&cmap);
307 get_player_landarea(&cmap, pplayer, &landarea, &settledarea);
308 pplayer->score.landarea = landarea;
309 pplayer->score.settledarea = settledarea;
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++;
316 } advance_index_iterate_end;
317 pplayer->score.techs += research_get(pplayer)->future_tech * 5 / 2;
319 unit_list_iterate(pplayer->units, punit) {
320 if (is_military_unit(punit)) {
321 pplayer->score.units++;
323 } unit_list_iterate_end
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++;
331 } improvement_iterate_end;
333 pplayer->score.spaceship = pplayer->spaceship.state;
335 pplayer->score.game = get_civ_score(pplayer);
338 /**************************************************************************
339 Return the score given by the units stats.
340 **************************************************************************/
341 static int get_units_score(const struct player *pplayer)
343 return (pplayer->score.units_built / 10
344 + pplayer->score.units_killed / 3);
347 /**************************************************************************
348 Return the civilization score (a numerical value) for the player.
349 **************************************************************************/
350 int get_civ_score(const struct player *pplayer)
352 /* We used to count pplayer->score.happy here too, but this is too easily
353 * manipulated by players at the endrturn. */
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);
362 /**************************************************************************
363 Return the spaceship score
364 **************************************************************************/
365 static int get_spaceship_score(const struct player *pplayer)
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)(100 * pplayer->spaceship.habitation
371 * pplayer->spaceship.success_rate);
372 } else {
373 return 0;
377 /**************************************************************************
378 Return the total number of citizens in the player's nation.
379 **************************************************************************/
380 int total_player_citizens(const struct player *pplayer)
382 int count = (pplayer->score.happy
383 + pplayer->score.content
384 + pplayer->score.unhappy
385 + pplayer->score.angry);
387 specialist_type_iterate(sp) {
388 count += pplayer->score.specialists[sp];
389 } specialist_type_iterate_end;
391 return count;
394 /**************************************************************************
395 At the end of a game, figure the winners and losers of the game and
396 output to a suitable place.
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.
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".
410 Barbarians do not count as winners or losers.
412 If interrupt is true, rank players by team score rather than by alive/dead
413 status.
414 **************************************************************************/
415 void rank_users(bool interrupt)
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;
424 /* don't output ranking info if we haven't enabled it via cmdline */
425 if (!srvarg.ranklog_filename) {
426 return;
429 fp = fc_fopen(srvarg.ranklog_filename, "w");
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;
438 /* initialize plr_state */
439 for (i = 0; i < player_slot_count(); i++) {
440 plr_state[i] = VS_NONE;
443 /* do we have a spacerace winner? */
444 players_iterate(pplayer) {
445 if (pplayer->spaceship.state == SSHIP_ARRIVED) {
446 spacerace_winner = pplayer;
447 break;
449 } players_iterate_end;
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);
458 } players_iterate_end;
461 if (interrupt == FALSE) {
462 /* game ended for a victory condition */
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;
475 } players_iterate_end;
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;
484 } players_iterate_end;
486 } players_iterate_end;
487 } else {
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);
499 } player_list_iterate_end;
500 if (t_score > t_winner_score) {
501 t_winner = pteam;
502 t_winner_score = t_score;
504 } teams_iterate_end;
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;
514 } players_iterate_end;
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));
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));
536 } players_iterate_end;
537 fprintf(fp, "\n");
539 fclose(fp);