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