Galactic Bloodshed
analysis.cc
Go to the documentation of this file.
1 // Copyright 2014 The Galactic Bloodshed Authors. All rights reserved.
2 // Use of this source code is governed by a license that can be
3 // found in the COPYING file.
4 
5 #include "gb/commands/analysis.h"
6 
7 #include <cctype>
8 #include <cstdio>
9 #include <cstdlib>
10 
11 #include "gb/GB_server.h"
12 #include "gb/buffers.h"
13 #include "gb/files_shl.h"
14 #include "gb/getplace.h"
15 #include "gb/max.h"
16 #include "gb/races.h"
17 #include "gb/ships.h"
18 #include "gb/tweakables.h"
19 #include "gb/vars.h"
20 
21 #define CARE 5
22 struct anal_sect {
23  int x, y;
24  int value;
25  int des;
26 };
27 
28 static void do_analysis(GameObj &, int, int, int, starnum_t, planetnum_t);
29 static void Insert(int, struct anal_sect[], int, int, int, int);
30 static void PrintTop(GameObj &, struct anal_sect[], const char *);
31 
32 void analysis(const command_t &argv, GameObj &g) {
33  int sector_type = -1; /* -1 does analysis on all types */
34  placetype where; /* otherwise on specific type */
35  int do_player = -1;
36  const char *p;
37  int mode = 1; /* does top five. 0 does low five */
38 
39  size_t i = 1;
40  do {
41  where.level = g.level;
42  where.snum = g.snum;
43  where.pnum = g.pnum;
44 
45  p = argv[1].c_str();
46  if (*p == '-') /* Must use 'd' to do an analysis on */
47  { /* desert sectors to avoid confusion */
48  p++; /* with the '-' for the mode type */
49  i++;
50  mode = 0;
51  }
52  switch (*p) {
53  case CHAR_SEA:
54  sector_type = SectorType::SEC_SEA;
55  break;
56  case CHAR_LAND:
57  sector_type = SectorType::SEC_LAND;
58  break;
59  case CHAR_MOUNT:
60  sector_type = SectorType::SEC_MOUNT;
61  break;
62  case CHAR_GAS:
63  sector_type = SectorType::SEC_GAS;
64  break;
65  case CHAR_ICE:
66  sector_type = SectorType::SEC_ICE;
67  break;
68  case CHAR_FOREST:
69  sector_type = SectorType::SEC_FOREST;
70  break;
71  case 'd':
72  sector_type = SectorType::SEC_DESERT;
73  break;
74  case CHAR_PLATED:
75  sector_type = SectorType::SEC_PLATED;
76  break;
77  case CHAR_WASTED:
78  sector_type = SectorType::SEC_WASTED;
79  break;
80  }
81  if (sector_type != -1 && mode == 1) {
82  i++;
83  }
84 
85  p = argv[i].c_str();
86  if (isdigit(*p)) {
87  do_player = atoi(p);
88  if (do_player > Num_races) {
89  g.out << "No such player #.\n";
90  return;
91  }
92  where.level = g.level;
93  where.snum = g.snum;
94  where.pnum = g.pnum;
95  i++;
96  }
97  p = argv[i].c_str();
98  if (i < argv.size() && (isalpha(*p) || *p == '/')) {
99  where = getplace(g, argv[i], 0);
100  if (where.err) continue;
101  }
102 
103  switch (where.level) {
104  case ScopeLevel::LEVEL_UNIV:
105  case ScopeLevel::LEVEL_SHIP:
106  g.out << "You can only analyze planets.\n";
107  break;
108  case ScopeLevel::LEVEL_PLAN:
109  do_analysis(g, do_player, mode, sector_type, where.snum, where.pnum);
110  break;
111  case ScopeLevel::LEVEL_STAR:
112  for (planetnum_t pnum = 0; pnum < Stars[where.snum]->numplanets; pnum++)
113  do_analysis(g, do_player, mode, sector_type, where.snum, pnum);
114  break;
115  }
116  } while (false);
117 }
118 
119 static void do_analysis(GameObj &g, int ThisPlayer, int mode, int sector_type,
120  starnum_t Starnum, planetnum_t Planetnum) {
121  player_t Playernum = g.player;
122  governor_t Governor = g.governor;
123  racetype *Race;
124  int x;
125  int y;
126  int p;
127  int i;
128  double compat;
129  struct anal_sect Res[CARE];
130  struct anal_sect Eff[CARE];
131  struct anal_sect Frt[CARE];
132  struct anal_sect Mob[CARE];
133  struct anal_sect Troops[CARE];
134  struct anal_sect Popn[CARE];
135  struct anal_sect mPopn[CARE];
136  int TotalCrys;
137  int PlayCrys[MAXPLAYERS + 1];
138  int TotalTroops;
139  int PlayTroops[MAXPLAYERS + 1];
140  int TotalPopn;
141  int PlayPopn[MAXPLAYERS + 1];
142  int TotalMob;
143  int PlayMob[MAXPLAYERS + 1];
144  int TotalEff;
145  int PlayEff[MAXPLAYERS + 1];
146  int TotalRes;
147  int PlayRes[MAXPLAYERS + 1];
148  int TotalSect;
149  int PlaySect[MAXPLAYERS + 1][SectorType::SEC_WASTED + 1];
150  int PlayTSect[MAXPLAYERS + 1];
151  int TotalWasted;
152  int WastedSect[MAXPLAYERS + 1];
153  int Sect[SectorType::SEC_WASTED + 1];
154  static char SectTypes[] = {CHAR_SEA, CHAR_LAND, CHAR_MOUNT,
155  CHAR_GAS, CHAR_ICE, CHAR_FOREST,
156  CHAR_DESERT, CHAR_PLATED, CHAR_WASTED};
157 
158  for (i = 0; i < CARE; i++)
159  Res[i].value = Eff[i].value = Frt[i].value = Mob[i].value =
160  Troops[i].value = Popn[i].value = mPopn[i].value = -1;
161 
162  TotalWasted = TotalCrys = TotalPopn = TotalMob = TotalTroops = TotalEff =
163  TotalRes = TotalSect = 0;
164  for (p = 0; p <= Num_races; p++) {
165  PlayTroops[p] = PlayPopn[p] = PlayMob[p] = PlayEff[p] = PlayCrys[p] =
166  PlayRes[p] = PlayTSect[p] = 0;
167  WastedSect[p] = 0;
168  for (i = 0; i <= SectorType::SEC_WASTED; i++) PlaySect[p][i] = 0;
169  }
170 
171  for (i = 0; i <= SectorType::SEC_WASTED; i++) Sect[i] = 0;
172 
173  Race = races[Playernum - 1];
174  const auto planet = getplanet(Starnum, Planetnum);
175 
176  if (!planet.info[Playernum - 1].explored) {
177  return;
178  }
179  auto smap = getsmap(planet);
180 
181  compat = compatibility(planet, Race);
182 
183  TotalSect = planet.Maxx * planet.Maxy;
184  for (x = planet.Maxx - 1; x >= 0; x--) {
185  for (y = planet.Maxy - 1; y >= 0; y--) {
186  auto &sect = smap.get(x, y);
187  p = sect.owner;
188 
189  PlayEff[p] += sect.eff;
190  PlayMob[p] += sect.mobilization;
191  PlayRes[p] += sect.resource;
192  PlayPopn[p] += sect.popn;
193  PlayTroops[p] += sect.troops;
194  PlaySect[p][sect.condition]++;
195  PlayTSect[p]++;
196  TotalEff += sect.eff;
197  TotalMob += sect.mobilization;
198  TotalRes += sect.resource;
199  TotalPopn += sect.popn;
200  TotalTroops += sect.troops;
201  Sect[sect.condition]++;
202 
203  if (sect.condition == SectorType::SEC_WASTED) {
204  WastedSect[p]++;
205  TotalWasted++;
206  }
207  if (sect.crystals && Race->tech >= TECH_CRYSTAL) {
208  PlayCrys[p]++;
209  TotalCrys++;
210  }
211 
212  if (sector_type == -1 || sector_type == sect.condition) {
213  if (ThisPlayer < 0 || ThisPlayer == p) {
214  Insert(mode, Res, x, y, sect.condition, (int)sect.resource);
215  Insert(mode, Eff, x, y, sect.condition, (int)sect.eff);
216  Insert(mode, Mob, x, y, sect.condition, (int)sect.mobilization);
217  Insert(mode, Frt, x, y, sect.condition, (int)sect.fert);
218  Insert(mode, Popn, x, y, sect.condition, (int)sect.popn);
219  Insert(mode, Troops, x, y, sect.condition, (int)sect.troops);
220  Insert(mode, mPopn, x, y, sect.condition,
221  maxsupport(Race, sect, compat, (int)planet.conditions[TOXIC]));
222  }
223  }
224  }
225  }
226 
227  sprintf(buf, "\nAnalysis of /%s/%s:\n", Stars[Starnum]->name,
228  Stars[Starnum]->pnames[Planetnum]);
229  notify(Playernum, Governor, buf);
230  sprintf(buf, "%s %d", (mode ? "Highest" : "Lowest"), CARE);
231  switch (sector_type) {
232  case -1:
233  sprintf(buf, "%s of all", buf);
234  break;
235  case SectorType::SEC_SEA:
236  sprintf(buf, "%s Ocean", buf);
237  break;
238  case SectorType::SEC_LAND:
239  sprintf(buf, "%s Land", buf);
240  break;
241  case SectorType::SEC_MOUNT:
242  sprintf(buf, "%s Mountain", buf);
243  break;
244  case SectorType::SEC_GAS:
245  sprintf(buf, "%s Gas", buf);
246  break;
247  case SectorType::SEC_ICE:
248  sprintf(buf, "%s Ice", buf);
249  break;
250  case SectorType::SEC_FOREST:
251  sprintf(buf, "%s Forest", buf);
252  break;
253  case SectorType::SEC_DESERT:
254  sprintf(buf, "%s Desert", buf);
255  break;
256  case SectorType::SEC_PLATED:
257  sprintf(buf, "%s Plated", buf);
258  break;
259  case SectorType::SEC_WASTED:
260  sprintf(buf, "%s Wasted", buf);
261  break;
262  }
263  notify(Playernum, Governor, buf);
264  if (ThisPlayer < 0)
265  sprintf(buf, " sectors.\n");
266  else if (ThisPlayer == 0)
267  sprintf(buf, " sectors that are unoccupied.\n");
268  else
269  sprintf(buf, " sectors owned by %d.\n", ThisPlayer);
270  notify(Playernum, Governor, buf);
271 
272  PrintTop(g, Troops, "Troops");
273  PrintTop(g, Res, "Res");
274  PrintTop(g, Eff, "Eff");
275  PrintTop(g, Frt, "Frt");
276  PrintTop(g, Mob, "Mob");
277  PrintTop(g, Popn, "Popn");
278  PrintTop(g, mPopn, "^Popn");
279 
280  g.out << "\n";
281  sprintf(buf, "%2s %3s %7s %6s %5s %5s %5s %2s", "Pl", "sec", "popn", "troops",
282  "a.eff", "a.mob", "res", "x");
283  notify(Playernum, Governor, buf);
284 
285  for (i = 0; i <= SectorType::SEC_WASTED; i++) {
286  sprintf(buf, "%4c", SectTypes[i]);
287  notify(Playernum, Governor, buf);
288  }
289  notify(Playernum, Governor,
290  "\n----------------------------------------------"
291  "---------------------------------\n");
292  for (p = 0; p <= Num_races; p++)
293  if (PlayTSect[p] != 0) {
294  sprintf(buf, "%2d %3d %7d %6d %5.1lf %5.1lf %5d %2d", p, PlayTSect[p],
295  PlayPopn[p], PlayTroops[p], (double)PlayEff[p] / PlayTSect[p],
296  (double)PlayMob[p] / PlayTSect[p], PlayRes[p], PlayCrys[p]);
297  notify(Playernum, Governor, buf);
298  for (i = 0; i <= SectorType::SEC_WASTED; i++) {
299  sprintf(buf, "%4d", PlaySect[p][i]);
300  notify(Playernum, Governor, buf);
301  }
302  g.out << "\n";
303  }
304  notify(Playernum, Governor,
305  "------------------------------------------------"
306  "-------------------------------\n");
307  sprintf(buf, "%2s %3d %7d %6d %5.1lf %5.1lf %5d %2d", "Tl", TotalSect,
308  TotalPopn, TotalTroops, (double)TotalEff / TotalSect,
309  (double)TotalMob / TotalSect, TotalRes, TotalCrys);
310  notify(Playernum, Governor, buf);
311  for (i = 0; i <= SectorType::SEC_WASTED; i++) {
312  sprintf(buf, "%4d", Sect[i]);
313  notify(Playernum, Governor, buf);
314  }
315  g.out << "\n";
316 }
317 
318 static void Insert(int mode, struct anal_sect arr[], int x, int y, int des,
319  int value) {
320  for (int i = 0; i < CARE; i++)
321  if ((mode && arr[i].value < value) ||
322  (!mode && (arr[i].value > value || arr[i].value == -1))) {
323  for (int j = CARE - 1; j > i; j--) arr[j] = arr[j - 1];
324  arr[i].value = value;
325  arr[i].x = x;
326  arr[i].y = y;
327  arr[i].des = des;
328  return;
329  }
330 }
331 
332 static void PrintTop(GameObj &g, struct anal_sect arr[], const char *name) {
333  player_t Playernum = g.player;
334  governor_t Governor = g.governor;
335 
336  sprintf(buf, "%8s:", name);
337  notify(Playernum, Governor, buf);
338  for (int i = 0; i < CARE && arr[i].value != -1; i++) {
339  sprintf(buf, "%5d%c(%2d,%2d)", arr[i].value, Dessymbols[arr[i].des],
340  arr[i].x, arr[i].y);
341  notify(Playernum, Governor, buf);
342  }
343  g.out << "\n";
344 }
#define CARE
Definition: analysis.cc:21
Planet getplanet(const starnum_t star, const planetnum_t pnum)
Definition: files_shl.cc:335
static void do_analysis(GameObj &, int, int, int, starnum_t, planetnum_t)
Definition: analysis.cc:119
int value
Definition: analysis.cc:24
static void PrintTop(GameObj &, struct anal_sect[], const char *)
Definition: analysis.cc:332
#define TOXIC
Definition: tweakables.h:39
double compatibility(const Planet &planet, const Race *race)
Definition: max.cc:37
void analysis(const command_t &argv, GameObj &g)
Definition: analysis.cc:32
static void Insert(int, struct anal_sect[], int, int, int, int)
Definition: analysis.cc:318
SectorMap getsmap(const Planet &p)
Definition: files_shl.cc:522
#define TECH_CRYSTAL
Definition: races.h:133
int maxsupport(const Race *r, const Sector &s, const double c, const int toxic)
Definition: max.cc:26
#define MAXPLAYERS
Definition: vars.h:45
int des
Definition: analysis.cc:25