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