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