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