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