Give it a logo.
[vms-empire.git] / game.c
1 /* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */
2
3 /*
4  *    Copyright (C) 1987, 1988 Chuck Simmons
5  * 
6  * See the file COPYING, distributed with empire, for restriction
7  * and warranty information.
8  */
9
10 /*
11 game.c -- Routines to initialize, save, and restore a game.
12 */
13
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <ctype.h>
18 #include "empire.h"
19 #include "extern.h"
20
21 long remove_land(long loc, long num_land);
22 int select_cities(void);
23 int find_next(long *mapi);
24 int good_cont(long mapi);
25 int xread(FILE *f, char *buf, int size);
26 int xwrite(FILE *f, char *buf, int size);
27 void stat_display( char *mbuf, int round);
28
29 /*
30 Initialize a new game.  Here we generate a new random map, put cities
31 on the map, select cities for each opponent, and zero out the lists of
32 pieces on the board.
33 */
34
35 void init_game (void) {
36         void make_map(void), place_cities(void);
37
38         long i;
39
40         kill_display (); /* nothing on screen */
41         automove = FALSE;
42         resigned = FALSE;
43         debug = FALSE;
44         print_debug = FALSE;
45         print_vmap = FALSE;
46         trace_pmap = FALSE;
47         save_movie = FALSE;
48         win = 0;
49         date = 0; /* no date yet */
50         user_score = 0;
51         comp_score = 0;
52         
53         for (i = 0; i < MAP_SIZE; i++) {
54                 user_map[i].contents = ' '; /* nothing seen yet */
55                 user_map[i].seen = 0;
56                 comp_map[i].contents = ' ';
57                 comp_map[i].seen = 0;
58         }
59         for (i = 0; i < NUM_OBJECTS; i++) {
60                 user_obj[i] = NULL;
61                 comp_obj[i] = NULL;
62         }
63         free_list = NULL; /* nothing free yet */
64         for (i = 0; i < LIST_SIZE; i++) { /* for each object */
65                 piece_info_t *obj = &(object[i]);
66                 obj->hits = 0; /* mark object as dead */
67                 obj->owner = UNOWNED;
68                 LINK (free_list, obj, piece_link); 
69         }
70
71         make_map (); /* make land and water */
72
73         do {
74                 for (i = 0; i < MAP_SIZE; i ++) { /* remove cities */
75                         if (map[i].contents == '*')
76                                 map[i].contents = '+'; /* land */
77                 }
78                 place_cities (); /* place cities on map */
79         } while (!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         int from, to, k;
102         long i, j, sum, 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 long land[MAP_SIZE];
170
171 void place_cities (void) {
172         long regen_land();
173
174         long placed, i, loc;
175         long 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 long regen_land (placed)
208 long placed;
209 {
210         long num_land;
211         long 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 long remove_land (loc, num_land)
235 long loc, num_land;
236 {
237         long new, i;
238
239         new = 0; /* nothing kept yet */
240         for (i = 0; i < num_land; i++) {
241                 if (dist (loc, land[i]) >= MIN_CITY_DIST) {
242                         land[new] = land[i];
243                         new++;
244                 }
245         }
246         return (new);
247 }
248
249 /*
250 Here we select the cities for the user and the computer.  Our choice of
251 cities will be heavily dependent on the difficulty level the user desires.
252
253 Our algorithm will not guarantee that either player will eventually be
254 able to move armies to any continent on the map.  There may be continents
255 which are unreachable by sea.  Consider the case of an island in a lake.
256 If the lake has no shore cities, then there is no way for a boat to reach
257 the island.  Our hope is that there will be enough water on the map, or enough
258 land, and that there will be enough cities, to make this case extremely rare.
259
260 First we make a list of continents which contain at least two cities, one
261 or more of which is on the coast.  If there are no such continents, we return
262 FALSE, and our caller should decide again where cities should be placed
263 on the map.  While making this list, we will rank the continents.  Our ranking
264 is based on the thought that shore cities are better than inland cities,
265 that any city is very important, and that the land area of a continent
266 is mildly important.  Usually, we expect every continent to have a different
267 ranking.  It will be unusual to have two continents with the same land area,
268 the same number of shore cities, and the same number of inland cities.  When
269 this is not the case, the first city encountered will be given the highest
270 rank.
271
272 We then rank pairs of continents.  We tell the user the number of different
273 ranks, and ask the user what rank they want to use.  This is how the
274 user specifies the difficulty level.  Using that pair, we have now decided
275 on a continent for each player.  We now choose a random city on each continent,
276 making sure the cities are not the same.
277 */
278
279 #define MAX_CONT 10 /* most continents we will allow */
280
281 typedef struct cont { /* a continent */
282         long value; /* value of continent */
283         int ncity; /* number of cities */
284         city_info_t * cityp[NUM_CITY]; /* pointer to city */
285 } cont_t;
286
287 typedef struct pair {
288         long value; /* value of pair for user */
289         int user_cont; /* index to user continent */
290         int comp_cont; /* index to computer continent */
291 } pair_t;
292
293 static int marked[MAP_SIZE]; /* list of examine cells */
294 static int ncont; /* number of continents */
295 static cont_t cont_tab[MAX_CONT]; /* list of good continenets */
296 static int rank_tab[MAX_CONT]; /* indices to cont_tab in order of rank */
297 static pair_t pair_tab[MAX_CONT*MAX_CONT]; /* ranked pairs of continents */
298
299 int select_cities (void) {
300         void find_cont(void), make_pair(void);
301
302         long compi, useri;
303         city_info_t *compp, *userp;
304         int comp_cont, user_cont;
305         int pair;
306
307         find_cont (); /* find and rank the continents */
308         if (ncont == 0) return (FALSE); /* there are no good continents */
309
310         make_pair (); /* create list of ranked pairs */
311
312         (void) sprintf (jnkbuf,
313                 "Choose a difficulty level where 0 is easy and %d is hard: ",
314                 ncont*ncont-1);
315
316         pair = get_range (jnkbuf, 0, ncont*ncont-1);
317         comp_cont = pair_tab[pair].comp_cont;
318         user_cont = pair_tab[pair].user_cont;
319
320         compi = irand ((long)cont_tab[comp_cont].ncity);
321         compp = cont_tab[comp_cont].cityp[compi];
322
323         do { /* select different user city */
324                 useri = irand ((long)cont_tab[user_cont].ncity);
325                 userp = cont_tab[user_cont].cityp[useri];
326         } while (userp == compp);
327
328         addprintf ("Your city is at %d.", loc_disp(userp->loc),0,0,0,0,0,0,0);
329         delay (); /* let user see output before we set_prod */
330
331         /* update city and map */
332         compp->owner = COMP;
333         compp->prod = ARMY;
334         compp->work = 0;
335         scan (comp_map, compp->loc);
336
337         userp->owner = USER;
338         userp->work = 0;
339         scan (user_map, userp->loc);
340         set_prod (userp);
341         return (TRUE);
342 }
343
344 /*
345 Find all continents with 2 cities or more, one of which must be a shore
346 city.  We rank the continents.
347 */
348
349 void find_cont (void) {
350         long i;
351         long mapi;
352
353         for (i = 0; i < MAP_SIZE; i++) marked[i] = 0; /* nothing marked yet */
354
355         ncont = 0; /* no continents found yet */
356         mapi = 0;
357
358         while (ncont < MAX_CONT)
359                 if (!find_next (&mapi)) return; /* all found */
360 }
361
362 /*
363 Find the next continent and insert it in the rank table.
364 If there are no more continents, we return false.
365 */
366
367 int find_next (mapi)
368 long *mapi;
369 {
370         long i, val;
371
372         for (;;) {
373                 if (*mapi >= MAP_SIZE) return (FALSE);
374
375                 if (!map[*mapi].on_board || marked[*mapi]
376                         || map[*mapi].contents == '.') *mapi += 1;
377                 else if (good_cont (*mapi)) {
378                         rank_tab[ncont] = ncont; /* insert cont in rank tab */
379                         val = cont_tab[ncont].value;
380
381                         for (i = ncont; i > 0; i--) { /* bubble up new rank */
382                                 if (val > cont_tab[rank_tab[i-1]].value) {
383                                         rank_tab[i] = rank_tab[i-1];
384                                         rank_tab[i-1] = ncont;
385                                 }
386                                 else break;
387                         }
388                         ncont++; /* count continents */
389                         return (TRUE);
390                 }
391         }
392 }
393
394 /*
395 Map out the current continent.  We mark every piece of land on the continent,
396 count the cities, shore cities, and land area of the continent.  If the
397 continent contains 2 cities and a shore city, we set the value of the
398 continent and return true.  Otherwise we return false.
399 */
400
401 static long ncity, nland, nshore;
402 static void mark_cont(long);
403
404 int good_cont (mapi)
405 long mapi;
406 {
407         long val;
408
409         ncity = 0; /* nothing seen yet */
410         nland = 0;
411         nshore = 0;
412
413         mark_cont (mapi);
414
415         if (nshore < 1 || ncity < 2) return (FALSE);
416
417         /* The first two cities, one of which must be a shore city,
418         don't contribute to the value.  Otherwise shore cities are
419         worth 3/2 an inland city.  A city is worth 1000 times as much
420         as land area. */
421
422         if (ncity == nshore) val = (nshore - 2) * 3;
423         else val = (nshore-1) * 3 + (ncity - nshore - 1) * 2;
424
425         val *= 1000; /* cities are worth a lot */
426         val += nland;
427         cont_tab[ncont].value = val;
428         cont_tab[ncont].ncity = ncity;
429         return (TRUE);
430 }
431
432 /*
433 Mark a continent.  This recursive algorithm marks the current square
434 and counts it if it is land or city.  If it is city, we also check
435 to see if it is a shore city, and we install it in the list of
436 cities for the continent.  We then examine each surrounding cell.
437 */
438
439 static void
440 mark_cont (mapi)
441 long mapi;
442 {
443         int i;
444
445         if (marked[mapi] || map[mapi].contents == '.'
446                 || !map[mapi].on_board) return;
447
448         marked[mapi] = 1; /* mark this cell seen */
449         nland++; /* count land on continent */
450
451         if (map[mapi].contents == '*') { /* a city? */
452                 cont_tab[ncont].cityp[ncity] = map[mapi].cityp;
453                 ncity++;
454                 if (rmap_shore (mapi)) nshore++;
455         }
456
457         for (i = 0; i < 8; i++) /* look at surrounding squares */
458                 mark_cont (mapi + dir_offset[i]);
459 }
460
461 /*
462 Create a list of pairs of continents in a ranked order.  The first
463 element in the list is the pair which is easiest for the user to
464 win with.  Our ranking is simply based on the difference in value
465 between the user's continent and the computer's continent.
466 */
467
468 void make_pair (void) {
469         int i, j, k, npair;
470         long val;
471
472         npair = 0; /* none yet */
473
474         for (i = 0; i < ncont; i++)
475         for (j = 0; j < ncont; j++) { /* loop through all continents */
476                 val = cont_tab[i].value - cont_tab[j].value;
477                 pair_tab[npair].value = val;
478                 pair_tab[npair].user_cont = i;
479                 pair_tab[npair].comp_cont = j;
480
481                 for (k = npair; k > 0; k--) { /* bubble up new rank */
482                         if (val > pair_tab[k-1].value) {
483                                 pair_tab[k] = pair_tab[k-1];
484                                 pair_tab[k-1].user_cont = i;
485                                 pair_tab[k-1].comp_cont = j;
486                         }
487                         else break;
488                 }
489                 npair++; /* count pairs */
490         }
491 }
492
493 /*
494 Save a game.  We save the game in emp_save.dat.  Someday we may want
495 to ask the user for a file name.  If we cannot save the game, we will
496 tell the user why.
497 */
498
499 /* macro to save typing; write an array, return if it fails */
500 #define wbuf(buf) if (!xwrite (f, (char *)buf, sizeof (buf))) return
501 #define wval(val) if (!xwrite (f, (char *)&val, sizeof (val))) return
502
503 void save_game (void) {
504         FILE *f; /* file to save game in */
505
506         f = fopen (savefile, "w"); /* open for output */
507         if (f == NULL) {
508                 perror ("Cannot save saved game");
509                 return;
510         }
511         wbuf (map);
512         wbuf (comp_map);
513         wbuf (user_map);
514         wbuf (city);
515         wbuf (object);
516         wbuf (user_obj);
517         wbuf (comp_obj);
518         wval (free_list);
519         wval (date);
520         wval (automove);
521         wval (resigned);
522         wval (debug);
523         wval (win);
524         wval (save_movie);
525         wval (user_score);
526         wval (comp_score);
527
528         (void) fclose (f);
529         topmsg (3, "Game saved.",0,0,0,0,0,0,0,0);
530 }
531
532 /*
533 Recover a saved game from emp_save.dat.
534 We return TRUE if we succeed, otherwise FALSE.
535 */
536
537 #define rbuf(buf) if (!xread (f, (char *)buf, sizeof(buf))) return (FALSE);
538 #define rval(val) if (!xread (f, (char *)&val, sizeof(val))) return (FALSE);
539
540 int restore_game (void) {
541         void read_embark();
542         
543         FILE *f; /* file to save game in */
544         long i;
545         piece_info_t **list;
546         piece_info_t *obj;
547
548         f = fopen (savefile, "r"); /* open for input */
549         if (f == NULL) {
550                 perror ("Cannot open saved game");
551                 return (FALSE);
552         }
553         rbuf (map);
554         rbuf (comp_map);
555         rbuf (user_map);
556         rbuf (city);
557         rbuf (object);
558         rbuf (user_obj);
559         rbuf (comp_obj);
560         rval (free_list);
561         rval (date);
562         rval (automove);
563         rval (resigned);
564         rval (debug);
565         rval (win);
566         rval (save_movie);
567         rval (user_score);
568         rval (comp_score);
569
570         /* Our pointers may not be valid because of source
571         changes or other things.  We recreate them. */
572         
573         free_list = NULL; /* zero all ptrs */
574         for (i = 0; i < MAP_SIZE; i++) {
575                 map[i].cityp = NULL;
576                 map[i].objp = NULL;
577         }
578         for (i = 0; i < LIST_SIZE; i++) {
579                 object[i].loc_link.next = NULL;
580                 object[i].loc_link.prev = NULL;
581                 object[i].cargo_link.next = NULL;
582                 object[i].cargo_link.prev = NULL;
583                 object[i].piece_link.next = NULL;
584                 object[i].piece_link.prev = NULL;
585                 object[i].ship = NULL;
586                 object[i].cargo = NULL;
587         }
588         for (i = 0; i < NUM_OBJECTS; i++) {
589                 comp_obj[i] = NULL;
590                 user_obj[i] = NULL;
591         }
592         /* put cities on map */
593         for (i = 0; i < NUM_CITY; i++)
594                 map[city[i].loc].cityp = &(city[i]);
595         
596         /* put pieces in free list or on map and in object lists */
597         for (i = 0; i < LIST_SIZE; i++) {
598                 obj = &(object[i]);
599                 if (object[i].owner == UNOWNED || object[i].hits == 0) {
600                         LINK (free_list, obj, piece_link);
601                 }
602                 else {
603                         list = LIST (object[i].owner);
604                         LINK (list[object[i].type], obj, piece_link);
605                         LINK (map[object[i].loc].objp, obj, loc_link);
606                 }
607         }
608         
609         /* Embark armies and fighters. */
610         read_embark (user_obj[TRANSPORT], ARMY);
611         read_embark (user_obj[CARRIER], FIGHTER);
612         read_embark (comp_obj[TRANSPORT], ARMY);
613         read_embark (comp_obj[CARRIER], FIGHTER);
614         
615         (void) fclose (f);
616         kill_display (); /* what we had is no longer good */
617         topmsg (3, "Game restored from save file.",0,0,0,0,0,0,0,0);
618         return (TRUE);
619 }
620         
621 /*
622 Embark cargo on a ship.  We loop through the list of ships.
623 We then loop through the pieces at the ship's location until
624 the ship has the same amount of cargo it previously had.
625 */
626
627 void read_embark (list, piece_type)
628 piece_info_t *list;
629 int piece_type;
630 {
631         void inconsistent(void);
632
633         piece_info_t *ship;
634         piece_info_t *obj;
635         int count;
636
637         for (ship = list; ship != NULL; ship = ship->piece_link.next) {
638                 count = ship->count; /* get # of pieces we need */
639                 if (count < 0) inconsistent ();
640                 ship->count = 0; /* nothing on board yet */
641                 for (obj = map[ship->loc].objp; obj && count;
642                     obj = obj->loc_link.next) {
643                         if (obj->ship == NULL && obj->type == piece_type) {
644                                 embark (ship, obj);
645                                 count -= 1;
646                         }
647                 }
648                 if (count) inconsistent ();
649         }
650 }
651
652 void inconsistent (void) {
653         (void) printf ("saved game is inconsistent.  Please remove it.\n");
654         exit (1);
655 }
656
657 /*
658 Write a buffer to a file.  If we cannot write everything, return FALSE.
659 Also, tell the user why the write did not work if it didn't.
660 */
661
662 int xwrite (f, buf, size)
663 FILE *f;
664 char *buf;
665 int size;
666 {
667         int bytes;
668  
669         bytes = fwrite (buf, 1, size, f);
670         if (bytes == -1) {
671                 perror ("Write to save file failed");
672                 return (FALSE);
673         }
674         if (bytes != size) {
675                 perror ("Cannot complete write to save file.\n");
676                 return (FALSE);
677         }
678         return (TRUE);
679 }
680
681 /*
682 Read a buffer from a file.  If the read fails, we tell the user why
683 and return FALSE.
684 */
685
686 int xread (f, buf, size)
687 FILE *f;
688 char *buf;
689 int size;
690 {
691         int bytes;
692
693         bytes = fread (buf, 1, size, f);
694         if (bytes == -1) {
695                 perror ("Read from save file failed");
696                 return (FALSE);
697         }
698         if (bytes != size) {
699                 perror ("Saved file is too short.\n");
700                 return (FALSE);
701         }
702         return (TRUE);
703 }
704
705 /*
706 Save a movie screen.  For each cell on the board, we write out
707 the character that would appear on either the user's or the
708 computer's screen.  This information is appended to 'empmovie.dat'.
709 */
710
711 extern char city_char[];
712 static char mapbuf[MAP_SIZE];
713
714 void
715 save_movie_screen (void)
716 {
717         FILE *f; /* file to save game in */
718         long i;
719         piece_info_t *p;
720
721         f = fopen ("empmovie.dat", "a"); /* open for append */
722         if (f == NULL) {
723                 perror ("Cannot open empmovie.dat");
724                 return;
725         }
726
727         for (i = 0; i < MAP_SIZE; i++) {
728                 if (map[i].cityp) mapbuf[i] = city_char[map[i].cityp->owner];
729                 else {
730                         p = find_obj_at_loc (i);
731                         
732                         if (!p) mapbuf[i] = map[i].contents;
733                         else if (p->owner == USER)
734                                 mapbuf[i] = piece_attr[p->type].sname;
735                         else mapbuf[i] = tolower (piece_attr[p->type].sname);
736                 }
737         }
738         wbuf (mapbuf);
739         (void) fclose (f);
740 }
741
742 /*
743 Replay a movie.  We read each buffer from the file and
744 print it using a zoomed display.
745 */
746
747 void
748 replay_movie (void)
749 {
750         void print_movie_cell();
751
752         FILE *f; /* file to save game in */
753         int row_inc, col_inc;
754         int r, c;
755         int round;
756
757         
758         f = fopen ("empmovie.dat", "r"); /* open for input */
759         if (f == NULL) {
760                 perror ("Cannot open empmovie.dat");
761                 return;
762         }
763         round = 0;
764         clear_screen ();
765         for (;;) {
766                 if (fread ((char *)mapbuf, 1, sizeof (mapbuf), f) != sizeof (mapbuf)) 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) refresh ();
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 (mbuf, round)
799 char *mbuf;
800 int round;
801 {
802         long i;
803         int counts[2*NUM_OBJECTS+2];
804         int user_cost, comp_cost;
805         char *p;
806         
807         (void) bzero ((char *)counts, sizeof (counts));
808         
809         for (i = 0; i < MAP_SIZE; i++) {
810                 p = strchr (pieces, mbuf[i]);
811                 if (p) counts[p-pieces] += 1;
812         }
813         user_cost = 0;
814         for (i = 1; i <= NUM_OBJECTS; i++)
815                 user_cost += counts[i] * piece_attr[i-1].build_time;
816                 
817         comp_cost = 0;
818         for (i = NUM_OBJECTS+2; i <= 2*NUM_OBJECTS+1; i++)
819                 comp_cost += counts[i] * piece_attr[i-NUM_OBJECTS-2].build_time;
820                 
821         for (i = 0; i < NUM_OBJECTS+1; i++) {
822                 pos_str (1, (int) i * 6, "%2d %c  ", counts[i], pieces[i],0,0,0,0,0,0);
823                 pos_str (2,(int) i * 6, "%2d %c  ", counts[i+NUM_OBJECTS+1], pieces[i+NUM_OBJECTS+1],0,0,0,0,0,0);
824         }
825
826         pos_str (1, (int) i * 6, "%5d", user_cost,0,0,0,0,0,0,0);
827         pos_str (2, (int) i * 6, "%5d", comp_cost,0,0,0,0,0,0,0);
828         pos_str (0, 0, "Round %3d", (round + 1) / 2,0,0,0,0,0,0,0);
829 }
830