We can add a date to the history.
[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 == '*')
75                 map[i].contents = '+'; /* 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 = '+'; /* land */
149         else map[i].contents = '.'; /* water */
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 = '*';
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 == '+') {
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 == '.') *mapi += 1;
382         else if (good_cont (*mapi)) {
383             rank_tab[ncont] = ncont; /* insert cont in rank tab */
384             val = cont_tab[ncont].value;
385
386             for (i = ncont; i > 0; i--) { /* bubble up new rank */
387                 if (val > cont_tab[rank_tab[i-1]].value) {
388                     rank_tab[i] = rank_tab[i-1];
389                     rank_tab[i-1] = ncont;
390                 }
391                 else break;
392             }
393             ncont++; /* count continents */
394             return (true);
395         }
396     }
397 }
398
399 /*
400 Map out the current continent.  We mark every piece of land on the continent,
401 count the cities, shore cities, and land area of the continent.  If the
402 continent contains 2 cities and a shore city, we set the value of the
403 continent and return true.  Otherwise we return false.
404 */
405
406 static count_t ncity, nland, nshore;
407 static void mark_cont(loc_t);
408
409 bool good_cont(loc_t mapi)
410 {
411     long val;
412
413     ncity = 0; /* nothing seen yet */
414     nland = 0;
415     nshore = 0;
416
417     mark_cont (mapi);
418
419     if (nshore < 1 || ncity < 2) return (false);
420
421     /* The first two cities, one of which must be a shore city,
422        don't contribute to the value.  Otherwise shore cities are
423        worth 3/2 an inland city.  A city is worth 1000 times as much
424        as land area. */
425
426     if (ncity == nshore) val = (nshore - 2) * 3;
427     else val = (nshore-1) * 3 + (ncity - nshore - 1) * 2;
428
429     val *= 1000; /* cities are worth a lot */
430     val += nland;
431     cont_tab[ncont].value = val;
432     cont_tab[ncont].ncity = ncity;
433     return (true);
434 }
435
436 /*
437 Mark a continent.  This recursive algorithm marks the current square
438 and counts it if it is land or city.  If it is city, we also check
439 to see if it is a shore city, and we install it in the list of
440 cities for the continent.  We then examine each surrounding cell.
441 */
442
443 static void
444 mark_cont(loc_t mapi)
445 {
446     int i;
447
448     if (marked[mapi] || map[mapi].contents == '.'
449         || !map[mapi].on_board) return;
450
451     marked[mapi] = 1; /* mark this cell seen */
452     nland++; /* count land on continent */
453
454     if (map[mapi].contents == '*') { /* a city? */
455         cont_tab[ncont].cityp[ncity] = map[mapi].cityp;
456         ncity++;
457         if (rmap_shore (mapi)) nshore++;
458     }
459
460     for (i = 0; i < 8; i++) /* look at surrounding squares */
461         mark_cont (mapi + dir_offset[i]);
462 }
463
464 /*
465 Create a list of pairs of continents in a ranked order.  The first
466 element in the list is the pair which is easiest for the user to
467 win with.  Our ranking is simply based on the difference in value
468 between the user's continent and the computer's continent.
469 */
470
471 void make_pair(void)
472 {
473     int i, j, k, npair;
474     long val;
475
476     npair = 0; /* none yet */
477
478     for (i = 0; i < ncont; i++)
479         for (j = 0; j < ncont; j++) { /* loop through all continents */
480             val = cont_tab[i].value - cont_tab[j].value;
481             pair_tab[npair].value = val;
482             pair_tab[npair].user_cont = i;
483             pair_tab[npair].comp_cont = j;
484
485             for (k = npair; k > 0; k--) { /* bubble up new rank */
486                 if (val > pair_tab[k-1].value) {
487                     pair_tab[k] = pair_tab[k-1];
488                     pair_tab[k-1].user_cont = i;
489                     pair_tab[k-1].comp_cont = j;
490                 }
491                 else break;
492             }
493             npair++; /* count pairs */
494         }
495 }
496
497 /*
498 Save a game.  We save the game in emp_save.dat.  Someday we may want
499 to ask the user for a file name.  If we cannot save the game, we will
500 tell the user why.
501 */
502
503 /* macro to save typing; write an array, return if it fails */
504 #define wbuf(buf) if (!xwrite (f, (char *)buf, sizeof (buf))) return
505 #define wval(val) if (!xwrite (f, (char *)&val, sizeof (val))) return
506
507 void save_game(void)
508 {
509     FILE *f; /* file to save game in */
510
511     f = fopen (savefile, "w"); /* open for output */
512     if (f == NULL) {
513         perror ("Cannot save saved game");
514         return;
515     }
516     wbuf (map);
517     wbuf (comp_map);
518     wbuf (user_map);
519     wbuf (city);
520     wbuf (object);
521     wbuf (user_obj);
522     wbuf (comp_obj);
523     wval (free_list);
524     wval (date);
525     wval (automove);
526     wval (resigned);
527     wval (debug);
528     wval (win);
529     wval (save_movie);
530     wval (user_score);
531     wval (comp_score);
532
533     (void) fclose (f);
534     topmsg (3, "Game saved.");
535 }
536
537 /*
538 Recover a saved game from emp_save.dat.
539 We return true if we succeed, otherwise false.
540 */
541
542 #define rbuf(buf) if (!xread (f, (char *)buf, sizeof(buf))) return (false);
543 #define rval(val) if (!xread (f, (char *)&val, sizeof(val))) return (false);
544
545 int restore_game(void)
546 {
547     void read_embark();
548         
549     FILE *f; /* file to save game in */
550     long i;
551     piece_info_t **list;
552     piece_info_t *obj;
553
554     f = fopen (savefile, "r"); /* open for input */
555     if (f == NULL) {
556         perror ("Cannot open saved game");
557         return (false);
558     }
559     rbuf (map);
560     rbuf (comp_map);
561     rbuf (user_map);
562     rbuf (city);
563     rbuf (object);
564     rbuf (user_obj);
565     rbuf (comp_obj);
566     rval (free_list);
567     rval (date);
568     rval (automove);
569     rval (resigned);
570     rval (debug);
571     rval (win);
572     rval (save_movie);
573     rval (user_score);
574     rval (comp_score);
575
576     /* Our pointers may not be valid because of source
577        changes or other things.  We recreate them. */
578         
579     free_list = NULL; /* zero all ptrs */
580     for (i = 0; i < MAP_SIZE; i++) {
581         map[i].cityp = NULL;
582         map[i].objp = NULL;
583     }
584     for (i = 0; i < LIST_SIZE; i++) {
585         object[i].loc_link.next = NULL;
586         object[i].loc_link.prev = NULL;
587         object[i].cargo_link.next = NULL;
588         object[i].cargo_link.prev = NULL;
589         object[i].piece_link.next = NULL;
590         object[i].piece_link.prev = NULL;
591         object[i].ship = NULL;
592         object[i].cargo = NULL;
593     }
594     for (i = 0; i < NUM_OBJECTS; i++) {
595         comp_obj[i] = NULL;
596         user_obj[i] = NULL;
597     }
598     /* put cities on map */
599     for (i = 0; i < NUM_CITY; i++)
600         map[city[i].loc].cityp = &(city[i]);
601         
602     /* put pieces in free list or on map and in object lists */
603     for (i = 0; i < LIST_SIZE; i++) {
604         obj = &(object[i]);
605         if (object[i].owner == UNOWNED || object[i].hits == 0) {
606             LINK (free_list, obj, piece_link);
607         }
608         else {
609             list = LIST (object[i].owner);
610             LINK (list[object[i].type], obj, piece_link);
611             LINK (map[object[i].loc].objp, obj, loc_link);
612         }
613     }
614         
615     /* Embark armies and fighters. */
616     read_embark (user_obj[TRANSPORT], ARMY);
617     read_embark (user_obj[CARRIER], FIGHTER);
618     read_embark (comp_obj[TRANSPORT], ARMY);
619     read_embark (comp_obj[CARRIER], FIGHTER);
620         
621     (void) fclose (f);
622     kill_display (); /* what we had is no longer good */
623     topmsg (3, "Game restored from save file.");
624     return (true);
625 }
626         
627 /*
628 Embark cargo on a ship.  We loop through the list of ships.
629 We then loop through the pieces at the ship's location until
630 the ship has the same amount of cargo it previously had.
631 */
632
633 void read_embark(piece_info_t *list, int piece_type)
634 {
635     void inconsistent(void);
636
637     piece_info_t *ship;
638     piece_info_t *obj;
639     int count;
640
641     for (ship = list; ship != NULL; ship = ship->piece_link.next) {
642         count = ship->count; /* get # of pieces we need */
643         if (count < 0) inconsistent ();
644         ship->count = 0; /* nothing on board yet */
645         for (obj = map[ship->loc].objp; obj && count;
646              obj = obj->loc_link.next) {
647             if (obj->ship == NULL && obj->type == piece_type) {
648                 embark (ship, obj);
649                 count -= 1;
650             }
651         }
652         if (count) inconsistent ();
653     }
654 }
655
656 void inconsistent(void)
657 {
658     (void) printf ("saved game is inconsistent.  Please remove it.\n");
659     exit (1);
660 }
661
662 /*
663 Write a buffer to a file.  If we cannot write everything, return false.
664 Also, tell the user why the write did not work if it didn't.
665 */
666
667 bool xwrite(FILE *f, char *buf, int size)
668 {
669     int bytes;
670  
671     bytes = fwrite (buf, 1, size, f);
672     if (bytes == -1) {
673         perror ("Write to save file failed");
674         return (false);
675     }
676     if (bytes != size) {
677         perror ("Cannot complete write to save file.\n");
678         return (false);
679     }
680     return (true);
681 }
682
683 /*
684 Read a buffer from a file.  If the read fails, we tell the user why
685 and return false.
686 */
687
688 bool xread(FILE *f, char *buf, int size)
689 {
690     int bytes;
691
692     bytes = fread (buf, 1, size, f);
693     if (bytes == -1) {
694         perror ("Read from save file failed");
695         return (false);
696     }
697     if (bytes != size) {
698         perror ("Saved file is too short.\n");
699         return (false);
700     }
701     return (true);
702 }
703
704 /*
705 Save a movie screen.  For each cell on the board, we write out
706 the character that would appear on either the user's or the
707 computer's screen.  This information is appended to 'empmovie.dat'.
708 */
709
710 extern char city_char[];
711 static char mapbuf[MAP_SIZE];
712
713 void
714 save_movie_screen(void)
715 {
716     FILE *f; /* file to save game in */
717     count_t i;
718     piece_info_t *p;
719
720     f = fopen ("empmovie.dat", "a"); /* open for append */
721     if (f == NULL) {
722         perror ("Cannot open empmovie.dat");
723         return;
724     }
725
726     for (i = 0; i < MAP_SIZE; i++) {
727         if (map[i].cityp) mapbuf[i] = city_char[map[i].cityp->owner];
728         else {
729             p = find_obj_at_loc (i);
730                         
731             if (!p) mapbuf[i] = map[i].contents;
732             else if (p->owner == USER)
733                 mapbuf[i] = piece_attr[p->type].sname;
734             else mapbuf[i] = tolower (piece_attr[p->type].sname);
735         }
736     }
737     wbuf (mapbuf);
738     (void) fclose (f);
739 }
740
741 /*
742 Replay a movie.  We read each buffer from the file and
743 print it using a zoomed display.
744 */
745
746 void
747 replay_movie(void)
748 {
749     void print_movie_cell();
750
751     FILE *f; /* file to save game in */
752     int row_inc, col_inc;
753     int r, c;
754     int round;
755
756         
757     f = fopen ("empmovie.dat", "r"); /* open for input */
758     if (f == NULL) {
759         perror ("Cannot open empmovie.dat");
760         return;
761     }
762     round = 0;
763     clear_screen ();
764     for (;;) {
765         if (fread ((char *)mapbuf, 1, sizeof (mapbuf), f) != sizeof (mapbuf))
766             break;
767         round += 1;
768                 
769         stat_display (mapbuf, round);
770                 
771         row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
772         col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
773         
774         for (r = 0; r < MAP_HEIGHT; r += row_inc)
775             for (c = 0; c < MAP_WIDTH; c += col_inc)
776                 print_movie_cell (mapbuf, r, c, row_inc, col_inc);
777                 
778         (void) redisplay ();
779         delay ();
780     }
781     (void) fclose (f);
782 }
783
784 /*
785 Display statistics about the game.  At the top of the screen we
786 print:
787
788 nn O  nn A  nn F  nn P  nn D  nn S  nn T  nn C  nn B  nn Z  xxxxx
789 nn X  nn a  nn f  nn p  nn d  nn s  nn t  nn c  nn b  nn z  xxxxx
790
791 There may be objects in cities and boats that aren't displayed.
792 The "xxxxx" field is the cumulative cost of building the hardware.
793 */
794
795 /* in declared order, with city first */
796 static char *pieces = "OAFPDSTCBZXafpdstcbz";
797
798 void stat_display(char *mbuf, int round)
799 {
800     count_t i;
801     int counts[2*NUM_OBJECTS+2];
802     int user_cost, comp_cost;
803     char *p;
804         
805     (void) bzero ((char *)counts, sizeof (counts));
806         
807     for (i = 0; i < MAP_SIZE; i++) {
808         p = strchr (pieces, mbuf[i]);
809         if (p) counts[p-pieces] += 1;
810     }
811     user_cost = 0;
812     for (i = 1; i <= NUM_OBJECTS; i++)
813         user_cost += counts[i] * piece_attr[i-1].build_time;
814                 
815     comp_cost = 0;
816     for (i = NUM_OBJECTS+2; i <= 2*NUM_OBJECTS+1; i++)
817         comp_cost += counts[i] * piece_attr[i-NUM_OBJECTS-2].build_time;
818                 
819     for (i = 0; i < NUM_OBJECTS+1; i++) {
820         pos_str (1, (int) i * 6, "%2d %c  ", counts[i], pieces[i]);
821         pos_str (2,(int) i * 6, "%2d %c  ", counts[i+NUM_OBJECTS+1], pieces[i+NUM_OBJECTS+1]);
822     }
823
824     pos_str (1, (int) i * 6, "%5d", user_cost);
825     pos_str (2, (int) i * 6, "%5d", comp_cost);
826     pos_str (0, 0, "Round %3d", (round + 1) / 2);
827 }
828
829 /* end */