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