Fix a shipper metadata bug.
[vms-empire.git] / game.c
1 /*
2  *    Copyright (C) 1987, 1988 Chuck Simmons
3  * 
4  * See the file COPYING, distributed with empire, for restriction
5  * and warranty information.
6  */
7
8 /*
9 game.c -- Routines to initialize, save, and restore a game.
10 */
11
12 #include <string.h>
13 #include <stdlib.h>
14 #include <stdio.h>
15 #include <ctype.h>
16 #include "empire.h"
17 #include "extern.h"
18
19 count_t remove_land(loc_t loc, count_t num_land);
20 bool select_cities(void);
21 bool find_next(loc_t *mapi);
22 bool good_cont(loc_t mapi);
23 bool xread(FILE *f, char *buf, int size);
24 bool xwrite(FILE *f, char *buf, int size);
25 void stat_display( char *mbuf, int round);
26
27 /*
28 Initialize a new game.  Here we generate a new random map, put cities
29 on the map, select cities for each opponent, and zero out the lists of
30 pieces on the board.
31 */
32
33 void init_game(void)
34 {
35     void make_map(void), place_cities(void);
36
37     count_t i;
38
39     kill_display (); /* nothing on screen */
40     automove = false;
41     resigned = false;
42     debug = false;
43     print_debug = false;
44     print_vmap = false;
45     trace_pmap = false;
46     save_movie = false;
47     win = no_win;
48     date = 0; /* no date yet */
49     user_score = 0;
50     comp_score = 0;
51         
52     for (i = 0; i < MAP_SIZE; i++) {
53         user_map[i].contents = ' '; /* nothing seen yet */
54         user_map[i].seen = 0;
55         comp_map[i].contents = ' ';
56         comp_map[i].seen = 0;
57     }
58     for (i = 0; i < NUM_OBJECTS; i++) {
59         user_obj[i] = NULL;
60         comp_obj[i] = NULL;
61     }
62     free_list = NULL; /* nothing free yet */
63     for (i = 0; i < LIST_SIZE; i++) { /* for each object */
64         piece_info_t *obj = &(object[i]);
65         obj->hits = 0; /* mark object as dead */
66         obj->owner = UNOWNED;
67         LINK (free_list, obj, piece_link); 
68     }
69
70     make_map (); /* make land and water */
71
72     do {
73         for (i = 0; i < MAP_SIZE; i ++) { /* remove cities */
74             if (map[i].contents == MAP_CITY)
75                 map[i].contents = MAP_LAND; /* land */
76         }
77         place_cities (); /* place cities on map */
78     } while
79         (!select_cities ()); /* choose a city for each player */
80 }
81
82 /*
83 Create a map.  To do this, we first randomly assign heights to each
84 map location.  Then we smooth these heights.  The more we smooth,
85 the better the land and water will clump together.  Then we decide
86 how high the land will be.  We attempt to choose enough land to meet
87 some required percentage.
88
89 There are two parameters to this algorithm:  the amount we will smooth,
90 and the ratio of land to water.  The user can provide these numbers
91 at program start up.
92 */
93
94 #define MAX_HEIGHT 999  /* highest height */
95
96 /* these arrays give some compilers problems when they are automatic */
97 static int height[2][MAP_SIZE];
98 static int height_count[MAX_HEIGHT+1];
99
100 void make_map(void)
101 {
102     int from, to, k;
103     count_t i, j, sum;
104     loc_t loc;
105
106     for (i = 0; i < MAP_SIZE; i++) /* fill map with random sand */
107         height[0][i] = irand (MAX_HEIGHT);
108
109     from = 0;
110     to = 1;
111     for (i = 0; i < SMOOTH; i++) { /* smooth the map */
112         for (j = 0; j < MAP_SIZE; j++) {
113             sum = height[from][j];
114             for (k = 0; k < 8; k++) {
115                 loc = j + dir_offset[k];
116                 /* edges get smoothed in a wierd fashion */
117                 if (loc < 0 || loc >= MAP_SIZE) loc = j;
118                 sum += height[from][loc];
119             }
120             height[to][j] = sum / 9;
121         }
122         k = to; /* swap to and from */
123         to = from;
124         from = k;
125     }
126
127     /* count the number of cells at each height */
128     for (i = 0; i <= MAX_HEIGHT; i++)
129         height_count[i] = 0;
130
131     for (i = 0; i <= MAP_SIZE; i++)
132         height_count[height[from][i]]++;
133
134     /* find the water line */
135     loc = MAX_HEIGHT; /* default to all water */
136     sum = 0;
137     for (i = 0; i <= MAX_HEIGHT; i++) {
138         sum += height_count[i];
139         if (sum * 100 / MAP_SIZE > WATER_RATIO && sum >= NUM_CITY) {
140             loc = i; /* this is last height that is water */
141             break;
142         }
143     }
144
145     /* mark the land and water */
146     for (i = 0; i < MAP_SIZE; i ++) {
147         if (height[from][i] > loc)
148             map[i].contents = MAP_LAND;
149         else map[i].contents = MAP_SEA;
150
151         map[i].objp = NULL; /* nothing in cell yet */
152         map[i].cityp = NULL;
153
154         j = loc_col (i);
155         k = loc_row (i);
156
157         map[i].on_board = !(j == 0 || j == MAP_WIDTH-1 
158                             || k == 0 || k == MAP_HEIGHT-1);
159     }
160 }
161
162 /*
163 Randomly place cities on the land.  There is a minimum distance that
164 should exist between cities.  We maintain a list of acceptable land cells
165 on which a city may be placed.  We randomly choose elements from this
166 list until all the cities are placed.  After each choice of a land cell
167 for a city, we remove land cells which are too close to the city.
168 */
169
170 /* avoid compiler problems with large automatic arrays */
171 static loc_t land[MAP_SIZE];
172
173 void place_cities(void)
174 {
175     count_t regen_land();
176
177     count_t placed, i;
178     loc_t loc;
179     count_t num_land;
180
181     num_land = 0; /* nothing in land array yet */
182     placed = 0; /* nothing placed yet */
183     while (placed < NUM_CITY) {
184         while (num_land == 0) num_land = regen_land (placed);
185         i = irand (num_land-1); /* select random piece of land */
186         loc = land[i];
187                 
188         city[placed].loc = loc;
189         city[placed].owner = UNOWNED;
190         city[placed].work = 0;
191         city[placed].prod = NOPIECE;
192                 
193         for (i = 0; i < NUM_OBJECTS; i++)
194             city[placed].func[i] = NOFUNC; /* no function */
195                         
196         map[loc].contents = MAP_CITY;
197         map[loc].cityp = &(city[placed]);
198         placed++;
199
200         /* Now remove any land too close to selected land. */
201         num_land = remove_land (loc, num_land);
202     }
203 }
204
205 /*
206 When we run out of available land, we recreate our land list.  We
207 put all land in the list, decrement the min_city_dist, and then
208 remove any land which is too close to a city.
209 */
210
211 count_t regen_land(count_t placed)
212 {
213     count_t num_land;
214     count_t i;
215
216     num_land = 0;
217     for (i = 0; i < MAP_SIZE; i++) {
218         if (map[i].on_board && map[i].contents == MAP_LAND) {
219             land[num_land] = i; /* remember piece of land */
220             num_land++; /* remember number of pieces */
221         }
222     }
223     if (placed > 0) { /* don't decrement 1st time */
224         MIN_CITY_DIST -= 1;
225         ASSERT (MIN_CITY_DIST >= 0);
226     }
227     for (i = 0; i < placed; i++) { /* for each placed city */
228         num_land = remove_land (city[i].loc, num_land);
229     }
230     return (num_land);
231 }
232
233 /*
234 Remove land that is too close to a city.
235 */
236
237 count_t remove_land(loc_t loc, count_t num_land)
238 {
239     count_t new, i;
240
241     new = 0; /* nothing kept yet */
242     for (i = 0; i < num_land; i++) {
243         if (dist (loc, land[i]) >= MIN_CITY_DIST) {
244             land[new] = land[i];
245             new++;
246         }
247     }
248     return (new);
249 }
250
251 /*
252 Here we select the cities for the user and the computer.  Our choice of
253 cities will be heavily dependent on the difficulty level the user desires.
254
255 Our algorithm will not guarantee that either player will eventually be
256 able to move armies to any continent on the map.  There may be continents
257 which are unreachable by sea.  Consider the case of an island in a lake.
258 If the lake has no shore cities, then there is no way for a boat to reach
259 the island.  Our hope is that there will be enough water on the map, or enough
260 land, and that there will be enough cities, to make this case extremely rare.
261
262 First we make a list of continents which contain at least two cities, one
263 or more of which is on the coast.  If there are no such continents, we return
264 false, and our caller should decide again where cities should be placed
265 on the map.  While making this list, we will rank the continents.  Our ranking
266 is based on the thought that shore cities are better than inland cities,
267 that any city is very important, and that the land area of a continent
268 is mildly important.  Usually, we expect every continent to have a different
269 ranking.  It will be unusual to have two continents with the same land area,
270 the same number of shore cities, and the same number of inland cities.  When
271 this is not the case, the first city encountered will be given the highest
272 rank.
273
274 We then rank pairs of continents.  We tell the user the number of different
275 ranks, and ask the user what rank they want to use.  This is how the
276 user specifies the difficulty level.  Using that pair, we have now decided
277 on a continent for each player.  We now choose a random city on each continent,
278 making sure the cities are not the same.
279 */
280
281 #define MAX_CONT 10 /* most continents we will allow */
282
283 typedef struct cont { /* a continent */
284         long value; /* value of continent */
285         int ncity; /* number of cities */
286         city_info_t * cityp[NUM_CITY]; /* pointer to city */
287 } cont_t;
288
289 typedef struct pair {
290         long value; /* value of pair for user */
291         int user_cont; /* index to user continent */
292         int comp_cont; /* index to computer continent */
293 } pair_t;
294
295 static int marked[MAP_SIZE]; /* list of examine cells */
296 static int ncont; /* number of continents */
297 static cont_t cont_tab[MAX_CONT]; /* list of good continenets */
298 static int rank_tab[MAX_CONT]; /* indices to cont_tab in order of rank */
299 static pair_t pair_tab[MAX_CONT*MAX_CONT]; /* ranked pairs of continents */
300
301 bool select_cities(void)
302 {
303     void find_cont(void), make_pair(void);
304
305     loc_t compi, useri;
306     city_info_t *compp, *userp;
307     int comp_cont, user_cont;
308     int pair;
309
310     find_cont (); /* find and rank the continents */
311     if (ncont == 0) return (false); /* there are no good continents */
312
313     make_pair (); /* create list of ranked pairs */
314
315     (void) sprintf (jnkbuf,
316                     "Choose a difficulty level where 0 is easy and %d is hard: ",
317                     ncont*ncont-1);
318
319     pair = get_range (jnkbuf, 0, ncont*ncont-1);
320     comp_cont = pair_tab[pair].comp_cont;
321     user_cont = pair_tab[pair].user_cont;
322
323     compi = irand ((long)cont_tab[comp_cont].ncity);
324     compp = cont_tab[comp_cont].cityp[compi];
325
326     do { /* select different user city */
327         useri = irand ((long)cont_tab[user_cont].ncity);
328         userp = cont_tab[user_cont].cityp[useri];
329     } while (userp == compp);
330
331     topmsg(1, "Your city is at %d.", loc_disp(userp->loc));
332     delay (); /* let user see output before we set_prod */
333
334     /* update city and map */
335     compp->owner = COMP;
336     compp->prod = ARMY;
337     compp->work = 0;
338     scan (comp_map, compp->loc);
339
340     userp->owner = USER;
341     userp->work = 0;
342     scan (user_map, userp->loc);
343     set_prod (userp);
344     return (true);
345 }
346
347 /*
348 Find all continents with 2 cities or more, one of which must be a shore
349 city.  We rank the continents.
350 */
351
352 void find_cont(void)
353 {
354     loc_t i;
355     loc_t mapi;
356
357     for (i = 0; i < MAP_SIZE; i++) marked[i] = 0; /* nothing marked yet */
358
359     ncont = 0; /* no continents found yet */
360     mapi = 0;
361
362     while (ncont < MAX_CONT)
363         if (!find_next (&mapi))
364             return; /* all found */
365 }
366
367 /*
368 Find the next continent and insert it in the rank table.
369 If there are no more continents, we return false.
370 */
371
372 bool find_next(loc_t *mapi)
373 {
374     count_t i;
375     long val;
376
377     for (;;) {
378         if (*mapi >= MAP_SIZE) return (false);
379
380         if (!map[*mapi].on_board || marked[*mapi]
381             || map[*mapi].contents == MAP_SEA)
382             *mapi += 1;
383         else if (good_cont (*mapi)) {
384             rank_tab[ncont] = ncont; /* insert cont in rank tab */
385             val = cont_tab[ncont].value;
386
387             for (i = ncont; i > 0; i--) { /* bubble up new rank */
388                 if (val > cont_tab[rank_tab[i-1]].value) {
389                     rank_tab[i] = rank_tab[i-1];
390                     rank_tab[i-1] = ncont;
391                 }
392                 else break;
393             }
394             ncont++; /* count continents */
395             return (true);
396         }
397     }
398 }
399
400 /*
401 Map out the current continent.  We mark every piece of land on the continent,
402 count the cities, shore cities, and land area of the continent.  If the
403 continent contains 2 cities and a shore city, we set the value of the
404 continent and return true.  Otherwise we return false.
405 */
406
407 static count_t ncity, nland, nshore;
408 static void mark_cont(loc_t);
409
410 bool good_cont(loc_t mapi)
411 {
412     long val;
413
414     ncity = 0; /* nothing seen yet */
415     nland = 0;
416     nshore = 0;
417
418     mark_cont (mapi);
419
420     if (nshore < 1 || ncity < 2) return (false);
421
422     /* The first two cities, one of which must be a shore city,
423        don't contribute to the value.  Otherwise shore cities are
424        worth 3/2 an inland city.  A city is worth 1000 times as much
425        as land area. */
426
427     if (ncity == nshore) val = (nshore - 2) * 3;
428     else val = (nshore-1) * 3 + (ncity - nshore - 1) * 2;
429
430     val *= 1000; /* cities are worth a lot */
431     val += nland;
432     cont_tab[ncont].value = val;
433     cont_tab[ncont].ncity = ncity;
434     return (true);
435 }
436
437 /*
438 Mark a continent.  This recursive algorithm marks the current square
439 and counts it if it is land or city.  If it is city, we also check
440 to see if it is a shore city, and we install it in the list of
441 cities for the continent.  We then examine each surrounding cell.
442 */
443
444 static void
445 mark_cont(loc_t mapi)
446 {
447     int i;
448
449     if (marked[mapi] 
450         || map[mapi].contents == MAP_SEA
451         || !map[mapi].on_board)
452         return;
453
454     marked[mapi] = 1; /* mark this cell seen */
455     nland++; /* count land on continent */
456
457     if (map[mapi].contents == MAP_CITY) { /* a city? */
458         cont_tab[ncont].cityp[ncity] = map[mapi].cityp;
459         ncity++;
460         if (rmap_shore (mapi)) nshore++;
461     }
462
463     for (i = 0; i < 8; i++) /* look at surrounding squares */
464         mark_cont (mapi + dir_offset[i]);
465 }
466
467 /*
468 Create a list of pairs of continents in a ranked order.  The first
469 element in the list is the pair which is easiest for the user to
470 win with.  Our ranking is simply based on the difference in value
471 between the user's continent and the computer's continent.
472 */
473
474 void make_pair(void)
475 {
476     int i, j, k, npair;
477     long val;
478
479     npair = 0; /* none yet */
480
481     for (i = 0; i < ncont; i++)
482         for (j = 0; j < ncont; j++) { /* loop through all continents */
483             val = cont_tab[i].value - cont_tab[j].value;
484             pair_tab[npair].value = val;
485             pair_tab[npair].user_cont = i;
486             pair_tab[npair].comp_cont = j;
487
488             for (k = npair; k > 0; k--) { /* bubble up new rank */
489                 if (val > pair_tab[k-1].value) {
490                     pair_tab[k] = pair_tab[k-1];
491                     pair_tab[k-1].user_cont = i;
492                     pair_tab[k-1].comp_cont = j;
493                 }
494                 else break;
495             }
496             npair++; /* count pairs */
497         }
498 }
499
500 /*
501 Save a game.  We save the game in emp_save.dat.  Someday we may want
502 to ask the user for a file name.  If we cannot save the game, we will
503 tell the user why.
504 */
505
506 /* macro to save typing; write an array, return if it fails */
507 #define wbuf(buf) if (!xwrite (f, (char *)buf, sizeof (buf))) return
508 #define wval(val) if (!xwrite (f, (char *)&val, sizeof (val))) return
509
510 void save_game(void)
511 {
512     FILE *f; /* file to save game in */
513
514     f = fopen (savefile, "w"); /* open for output */
515     if (f == NULL) {
516         perror ("Cannot save saved game");
517         return;
518     }
519     wbuf (map);
520     wbuf (comp_map);
521     wbuf (user_map);
522     wbuf (city);
523     wbuf (object);
524     wbuf (user_obj);
525     wbuf (comp_obj);
526     wval (free_list);
527     wval (date);
528     wval (automove);
529     wval (resigned);
530     wval (debug);
531     wval (win);
532     wval (save_movie);
533     wval (user_score);
534     wval (comp_score);
535
536     (void) fclose (f);
537     topmsg (3, "Game saved.");
538 }
539
540 /*
541 Recover a saved game from emp_save.dat.
542 We return true if we succeed, otherwise false.
543 */
544
545 #define rbuf(buf) if (!xread (f, (char *)buf, sizeof(buf))) return (false);
546 #define rval(val) if (!xread (f, (char *)&val, sizeof(val))) return (false);
547
548 int restore_game(void)
549 {
550     void read_embark();
551         
552     FILE *f; /* file to save game in */
553     long i;
554     piece_info_t **list;
555     piece_info_t *obj;
556
557     f = fopen (savefile, "r"); /* open for input */
558     if (f == NULL) {
559         perror ("Cannot open saved game");
560         return (false);
561     }
562     rbuf (map);
563     rbuf (comp_map);
564     rbuf (user_map);
565     rbuf (city);
566     rbuf (object);
567     rbuf (user_obj);
568     rbuf (comp_obj);
569     rval (free_list);
570     rval (date);
571     rval (automove);
572     rval (resigned);
573     rval (debug);
574     rval (win);
575     rval (save_movie);
576     rval (user_score);
577     rval (comp_score);
578
579     /* Our pointers may not be valid because of source
580        changes or other things.  We recreate them. */
581         
582     free_list = NULL; /* zero all ptrs */
583     for (i = 0; i < MAP_SIZE; i++) {
584         map[i].cityp = NULL;
585         map[i].objp = NULL;
586     }
587     for (i = 0; i < LIST_SIZE; i++) {
588         object[i].loc_link.next = NULL;
589         object[i].loc_link.prev = NULL;
590         object[i].cargo_link.next = NULL;
591         object[i].cargo_link.prev = NULL;
592         object[i].piece_link.next = NULL;
593         object[i].piece_link.prev = NULL;
594         object[i].ship = NULL;
595         object[i].cargo = NULL;
596     }
597     for (i = 0; i < NUM_OBJECTS; i++) {
598         comp_obj[i] = NULL;
599         user_obj[i] = NULL;
600     }
601     /* put cities on map */
602     for (i = 0; i < NUM_CITY; i++)
603         map[city[i].loc].cityp = &(city[i]);
604         
605     /* put pieces in free list or on map and in object lists */
606     for (i = 0; i < LIST_SIZE; i++) {
607         obj = &(object[i]);
608         if (object[i].owner == UNOWNED || object[i].hits == 0) {
609             LINK (free_list, obj, piece_link);
610         }
611         else {
612             list = LIST (object[i].owner);
613             LINK (list[object[i].type], obj, piece_link);
614             LINK (map[object[i].loc].objp, obj, loc_link);
615         }
616     }
617         
618     /* Embark armies and fighters. */
619     read_embark (user_obj[TRANSPORT], ARMY);
620     read_embark (user_obj[CARRIER], FIGHTER);
621     read_embark (comp_obj[TRANSPORT], ARMY);
622     read_embark (comp_obj[CARRIER], FIGHTER);
623         
624     (void) fclose (f);
625     kill_display (); /* what we had is no longer good */
626     topmsg (3, "Game restored from save file.");
627     return (true);
628 }
629         
630 /*
631 Embark cargo on a ship.  We loop through the list of ships.
632 We then loop through the pieces at the ship's location until
633 the ship has the same amount of cargo it previously had.
634 */
635
636 void read_embark(piece_info_t *list, int piece_type)
637 {
638     void inconsistent(void);
639
640     piece_info_t *ship;
641     piece_info_t *obj;
642     int count;
643
644     for (ship = list; ship != NULL; ship = ship->piece_link.next) {
645         count = ship->count; /* get # of pieces we need */
646         if (count < 0) inconsistent ();
647         ship->count = 0; /* nothing on board yet */
648         for (obj = map[ship->loc].objp; obj && count;
649              obj = obj->loc_link.next) {
650             if (obj->ship == NULL && obj->type == piece_type) {
651                 embark (ship, obj);
652                 count -= 1;
653             }
654         }
655         if (count) inconsistent ();
656     }
657 }
658
659 void inconsistent(void)
660 {
661     (void) printf ("saved game is inconsistent.  Please remove it.\n");
662     exit (1);
663 }
664
665 /*
666 Write a buffer to a file.  If we cannot write everything, return false.
667 Also, tell the user why the write did not work if it didn't.
668 */
669
670 bool xwrite(FILE *f, char *buf, int size)
671 {
672     int bytes;
673  
674     bytes = fwrite (buf, 1, size, f);
675     if (bytes == -1) {
676         perror ("Write to save file failed");
677         return (false);
678     }
679     if (bytes != size) {
680         perror ("Cannot complete write to save file.\n");
681         return (false);
682     }
683     return (true);
684 }
685
686 /*
687 Read a buffer from a file.  If the read fails, we tell the user why
688 and return false.
689 */
690
691 bool xread(FILE *f, char *buf, int size)
692 {
693     int bytes;
694
695     bytes = fread (buf, 1, size, f);
696     if (bytes == -1) {
697         perror ("Read from save file failed");
698         return (false);
699     }
700     if (bytes != size) {
701         perror ("Saved file is too short.\n");
702         return (false);
703     }
704     return (true);
705 }
706
707 /*
708 Save a movie screen.  For each cell on the board, we write out
709 the character that would appear on either the user's or the
710 computer's screen.  This information is appended to 'empmovie.dat'.
711 */
712
713 extern char city_char[];
714 static char mapbuf[MAP_SIZE];
715
716 void
717 save_movie_screen(void)
718 {
719     FILE *f; /* file to save game in */
720     count_t i;
721     piece_info_t *p;
722
723     f = fopen ("empmovie.dat", "a"); /* open for append */
724     if (f == NULL) {
725         perror ("Cannot open empmovie.dat");
726         return;
727     }
728
729     for (i = 0; i < MAP_SIZE; i++) {
730         if (map[i].cityp) mapbuf[i] = city_char[map[i].cityp->owner];
731         else {
732             p = find_obj_at_loc (i);
733                         
734             if (!p) mapbuf[i] = map[i].contents;
735             else if (p->owner == USER)
736                 mapbuf[i] = piece_attr[p->type].sname;
737             else mapbuf[i] = tolower (piece_attr[p->type].sname);
738         }
739     }
740     wbuf (mapbuf);
741     (void) fclose (f);
742 }
743
744 /*
745 Replay a movie.  We read each buffer from the file and
746 print it using a zoomed display.
747 */
748
749 void
750 replay_movie(void)
751 {
752     void print_movie_cell();
753
754     FILE *f; /* file to save game in */
755     int row_inc, col_inc;
756     int r, c;
757     int round;
758
759         
760     f = fopen ("empmovie.dat", "r"); /* open for input */
761     if (f == NULL) {
762         perror ("Cannot open empmovie.dat");
763         return;
764     }
765     round = 0;
766     clear_screen ();
767     for (;;) {
768         if (fread ((char *)mapbuf, 1, sizeof (mapbuf), f) != sizeof (mapbuf))
769             break;
770         round += 1;
771                 
772         stat_display (mapbuf, round);
773                 
774         row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
775         col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
776         
777         for (r = 0; r < MAP_HEIGHT; r += row_inc)
778             for (c = 0; c < MAP_WIDTH; c += col_inc)
779                 print_movie_cell (mapbuf, r, c, row_inc, col_inc);
780                 
781         (void) redisplay ();
782         delay ();
783     }
784     (void) fclose (f);
785 }
786
787 /*
788 Display statistics about the game.  At the top of the screen we
789 print:
790
791 nn O  nn A  nn F  nn P  nn D  nn S  nn T  nn C  nn B  nn Z  xxxxx
792 nn X  nn a  nn f  nn p  nn d  nn s  nn t  nn c  nn b  nn z  xxxxx
793
794 There may be objects in cities and boats that aren't displayed.
795 The "xxxxx" field is the cumulative cost of building the hardware.
796 */
797
798 /* in declared order, with city first */
799 static char *pieces = "OAFPDSTCBZXafpdstcbz";
800
801 void stat_display(char *mbuf, int round)
802 {
803     count_t i;
804     int counts[2*NUM_OBJECTS+2];
805     int user_cost, comp_cost;
806     char *p;
807         
808     (void) memset ((char *)counts, '\0', sizeof (counts));
809         
810     for (i = 0; i < MAP_SIZE; i++) {
811         p = strchr (pieces, mbuf[i]);
812         if (p) counts[p-pieces] += 1;
813     }
814     user_cost = 0;
815     for (i = 1; i <= NUM_OBJECTS; i++)
816         user_cost += counts[i] * piece_attr[i-1].build_time;
817                 
818     comp_cost = 0;
819     for (i = NUM_OBJECTS+2; i <= 2*NUM_OBJECTS+1; i++)
820         comp_cost += counts[i] * piece_attr[i-NUM_OBJECTS-2].build_time;
821                 
822     for (i = 0; i < NUM_OBJECTS+1; i++) {
823         pos_str (1, (int) i * 6, "%2d %c  ", counts[i], pieces[i]);
824         pos_str (2,(int) i * 6, "%2d %c  ", counts[i+NUM_OBJECTS+1], pieces[i+NUM_OBJECTS+1]);
825     }
826
827     pos_str (1, (int) i * 6, "%5d", user_cost);
828     pos_str (2, (int) i * 6, "%5d", comp_cost);
829     pos_str (0, 0, "Round %3d", (round + 1) / 2);
830 }
831
832 /* end */