OSDN Git Service

日本語訳を修正
[vms-empire-jp/vms-empire-jp.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 count_t remove_land(loc_t loc, count_t num_land);
20 bool select_cities(void);
21 bool find_next(loc_t *mapi);
22 bool good_cont(loc_t mapi);
23 bool xread(FILE *f, char *buf, int size);
24 bool 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 {
35     void make_map(void), place_cities(void);
36
37     count_t i;
38
39     kill_display (); /* nothing on screen */
40     automove = false;
41     resigned = false;
42     debug = false;
43     print_debug = false;
44     print_vmap = false;
45     trace_pmap = false;
46     save_movie = false;
47     win = no_win;
48     date = 0; /* no date yet */
49     user_score = 0;
50     comp_score = 0;
51         
52     for (i = 0; i < MAP_SIZE; i++) {
53         user_map[i].contents = ' '; /* nothing seen yet */
54         user_map[i].seen = 0;
55         comp_map[i].contents = ' ';
56         comp_map[i].seen = 0;
57     }
58     for (i = 0; i < NUM_OBJECTS; i++) {
59         user_obj[i] = NULL;
60         comp_obj[i] = NULL;
61     }
62     free_list = NULL; /* nothing free yet */
63     for (i = 0; i < LIST_SIZE; i++) { /* for each object */
64         piece_info_t *obj = &(object[i]);
65         obj->hits = 0; /* mark object as dead */
66         obj->owner = UNOWNED;
67         LINK (free_list, obj, piece_link); 
68     }
69
70     make_map (); /* make land and water */
71
72     do {
73         for (i = 0; i < MAP_SIZE; i ++) { /* remove cities */
74             if (map[i].contents == MAP_CITY)
75                 map[i].contents = MAP_LAND; /* land */
76         }
77         place_cities (); /* place cities on map */
78     } while
79         (!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 {
102     int from, to, k;
103     count_t i, j, sum;
104     loc_t loc;
105
106     for (i = 0; i < MAP_SIZE; i++) /* fill map with random sand */
107         height[0][i] = irand (MAX_HEIGHT);
108
109     from = 0;
110     to = 1;
111     for (i = 0; i < SMOOTH; i++) { /* smooth the map */
112         for (j = 0; j < MAP_SIZE; j++) {
113             sum = height[from][j];
114             for (k = 0; k < 8; k++) {
115                 loc = j + dir_offset[k];
116                 /* edges get smoothed in a wierd fashion */
117                 if (loc < 0 || loc >= MAP_SIZE) loc = j;
118                 sum += height[from][loc];
119             }
120             height[to][j] = sum / 9;
121         }
122         k = to; /* swap to and from */
123         to = from;
124         from = k;
125     }
126
127     /* count the number of cells at each height */
128     for (i = 0; i <= MAX_HEIGHT; i++)
129         height_count[i] = 0;
130
131     for (i = 0; i <= MAP_SIZE; i++)
132         height_count[height[from][i]]++;
133
134     /* find the water line */
135     loc = MAX_HEIGHT; /* default to all water */
136     sum = 0;
137     for (i = 0; i <= MAX_HEIGHT; i++) {
138         sum += height_count[i];
139         if (sum * 100 / MAP_SIZE > WATER_RATIO && sum >= NUM_CITY) {
140             loc = i; /* this is last height that is water */
141             break;
142         }
143     }
144
145     /* mark the land and water */
146     for (i = 0; i < MAP_SIZE; i ++) {
147         if (height[from][i] > loc)
148             map[i].contents = MAP_LAND;
149         else map[i].contents = MAP_SEA;
150
151         map[i].objp = NULL; /* nothing in cell yet */
152         map[i].cityp = NULL;
153
154         j = loc_col (i);
155         k = loc_row (i);
156
157         map[i].on_board = !(j == 0 || j == MAP_WIDTH-1 
158                             || k == 0 || k == MAP_HEIGHT-1);
159     }
160 }
161
162 /*
163 Randomly place cities on the land.  There is a minimum distance that
164 should exist between cities.  We maintain a list of acceptable land cells
165 on which a city may be placed.  We randomly choose elements from this
166 list until all the cities are placed.  After each choice of a land cell
167 for a city, we remove land cells which are too close to the city.
168 */
169
170 /* avoid compiler problems with large automatic arrays */
171 static loc_t land[MAP_SIZE];
172
173 void place_cities(void)
174 {
175     count_t regen_land();
176
177     count_t placed;
178     count_t num_land;
179
180     num_land = 0; /* nothing in land array yet */
181     placed = 0; /* nothing placed yet */
182     while (placed < NUM_CITY) {
183         count_t i;
184         loc_t loc;
185
186         while (num_land == 0) num_land = regen_land (placed);
187         i = irand (num_land-1); /* select random piece of land */
188         loc = land[i];
189                 
190         city[placed].loc = loc;
191         city[placed].owner = UNOWNED;
192         city[placed].work = 0;
193         city[placed].prod = NOPIECE;
194                 
195         for (i = 0; i < NUM_OBJECTS; i++)
196             city[placed].func[i] = NOFUNC; /* no function */
197                         
198         map[loc].contents = MAP_CITY;
199         map[loc].cityp = &(city[placed]);
200         placed++;
201
202         /* Now remove any land too close to selected land. */
203         num_land = remove_land (loc, num_land);
204     }
205 }
206
207 /*
208 When we run out of available land, we recreate our land list.  We
209 put all land in the list, decrement the min_city_dist, and then
210 remove any land which is too close to a city.
211 */
212
213 count_t regen_land(count_t placed)
214 {
215     count_t num_land;
216     count_t i;
217
218     num_land = 0;
219     for (i = 0; i < MAP_SIZE; i++) {
220         if (map[i].on_board && map[i].contents == MAP_LAND) {
221             land[num_land] = i; /* remember piece of land */
222             num_land++; /* remember number of pieces */
223         }
224     }
225     if (placed > 0) { /* don't decrement 1st time */
226         MIN_CITY_DIST -= 1;
227         ASSERT (MIN_CITY_DIST >= 0);
228     }
229     for (i = 0; i < placed; i++) { /* for each placed city */
230         num_land = remove_land (city[i].loc, num_land);
231     }
232     return (num_land);
233 }
234
235 /*
236 Remove land that is too close to a city.
237 */
238
239 count_t remove_land(loc_t loc, count_t num_land)
240 {
241     count_t 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 bool select_cities(void)
304 {
305     void find_cont(void), make_pair(void);
306
307     loc_t compi;
308     city_info_t *compp, *userp;
309     int comp_cont, user_cont;
310     int pair;
311
312     find_cont (); /* find and rank the continents */
313     if (ncont == 0) return (false); /* there are no good continents */
314
315     make_pair (); /* create list of ranked pairs */
316
317     (void) sprintf (jnkbuf,
318                     "難易度を0(簡単)から%d(難しい)の間で選択せよ: ",
319                     ncont*ncont-1);
320
321     pair = get_range (jnkbuf, 0, ncont*ncont-1);
322     comp_cont = pair_tab[pair].comp_cont;
323     user_cont = pair_tab[pair].user_cont;
324
325     compi = irand ((long)cont_tab[comp_cont].ncity);
326     compp = cont_tab[comp_cont].cityp[compi];
327
328     do { /* select different user city */
329         loc_t useri = irand ((long)cont_tab[user_cont].ncity);
330         userp = cont_tab[user_cont].cityp[useri];
331     } while (userp == compp);
332
333     topmsg(1, "あなたの都市は%dだ。", loc_disp(userp->loc));
334     delay (); /* let user see output before we set_prod */
335
336     /* update city and map */
337     compp->owner = COMP;
338     compp->prod = ARMY;
339     compp->work = 0;
340     scan (comp_map, compp->loc);
341
342     userp->owner = USER;
343     userp->work = 0;
344     scan (user_map, userp->loc);
345     set_prod (userp);
346     return (true);
347 }
348
349 /*
350 Find all continents with 2 cities or more, one of which must be a shore
351 city.  We rank the continents.
352 */
353
354 void find_cont(void)
355 {
356     loc_t i;
357     loc_t mapi;
358
359     for (i = 0; i < MAP_SIZE; i++) marked[i] = 0; /* nothing marked yet */
360
361     ncont = 0; /* no continents found yet */
362     mapi = 0;
363
364     while (ncont < MAX_CONT)
365         if (!find_next (&mapi))
366             return; /* all found */
367 }
368
369 /*
370 Find the next continent and insert it in the rank table.
371 If there are no more continents, we return false.
372 */
373
374 bool find_next(loc_t *mapi)
375 {
376     count_t i;
377     long val;
378
379     for (;;) {
380         if (*mapi >= MAP_SIZE) return (false);
381
382         if (!map[*mapi].on_board || marked[*mapi]
383             || map[*mapi].contents == MAP_SEA)
384             *mapi += 1;
385         else if (good_cont (*mapi)) {
386             rank_tab[ncont] = ncont; /* insert cont in rank tab */
387             val = cont_tab[ncont].value;
388
389             for (i = ncont; i > 0; i--) { /* bubble up new rank */
390                 if (val > cont_tab[rank_tab[i-1]].value) {
391                     rank_tab[i] = rank_tab[i-1];
392                     rank_tab[i-1] = ncont;
393                 }
394                 else break;
395             }
396             ncont++; /* count continents */
397             return (true);
398         }
399     }
400 }
401
402 /*
403 Map out the current continent.  We mark every piece of land on the continent,
404 count the cities, shore cities, and land area of the continent.  If the
405 continent contains 2 cities and a shore city, we set the value of the
406 continent and return true.  Otherwise we return false.
407 */
408
409 static count_t ncity, nland, nshore;
410 static void mark_cont(loc_t);
411
412 bool good_cont(loc_t mapi)
413 {
414     long val;
415
416     ncity = 0; /* nothing seen yet */
417     nland = 0;
418     nshore = 0;
419
420     mark_cont (mapi);
421
422     if (nshore < 1 || ncity < 2) return (false);
423
424     /* The first two cities, one of which must be a shore city,
425        don't contribute to the value.  Otherwise shore cities are
426        worth 3/2 an inland city.  A city is worth 1000 times as much
427        as land area. */
428
429     if (ncity == nshore) val = (nshore - 2) * 3;
430     else val = (nshore-1) * 3 + (ncity - nshore - 1) * 2;
431
432     val *= 1000; /* cities are worth a lot */
433     val += nland;
434     cont_tab[ncont].value = val;
435     cont_tab[ncont].ncity = ncity;
436     return (true);
437 }
438
439 /*
440 Mark a continent.  This recursive algorithm marks the current square
441 and counts it if it is land or city.  If it is city, we also check
442 to see if it is a shore city, and we install it in the list of
443 cities for the continent.  We then examine each surrounding cell.
444 */
445
446 static void
447 mark_cont(loc_t mapi)
448 {
449     int i;
450
451     if (marked[mapi] 
452         || map[mapi].contents == MAP_SEA
453         || !map[mapi].on_board)
454         return;
455
456     marked[mapi] = 1; /* mark this cell seen */
457     nland++; /* count land on continent */
458
459     if (map[mapi].contents == MAP_CITY) { /* a city? */
460         cont_tab[ncont].cityp[ncity] = map[mapi].cityp;
461         ncity++;
462         if (rmap_shore (mapi)) nshore++;
463     }
464
465     for (i = 0; i < 8; i++) /* look at surrounding squares */
466         mark_cont (mapi + dir_offset[i]);
467 }
468
469 /*
470 Create a list of pairs of continents in a ranked order.  The first
471 element in the list is the pair which is easiest for the user to
472 win with.  Our ranking is simply based on the difference in value
473 between the user's continent and the computer's continent.
474 */
475
476 void make_pair(void)
477 {
478     int i, j, k, npair;
479     long val;
480
481     npair = 0; /* none yet */
482
483     for (i = 0; i < ncont; i++)
484         for (j = 0; j < ncont; j++) { /* loop through all continents */
485             val = cont_tab[i].value - cont_tab[j].value;
486             pair_tab[npair].value = val;
487             pair_tab[npair].user_cont = i;
488             pair_tab[npair].comp_cont = j;
489
490             for (k = npair; k > 0; k--) { /* bubble up new rank */
491                 if (val > pair_tab[k-1].value) {
492                     pair_tab[k] = pair_tab[k-1];
493                     pair_tab[k-1].user_cont = i;
494                     pair_tab[k-1].comp_cont = j;
495                 }
496                 else break;
497             }
498             npair++; /* count pairs */
499         }
500 }
501
502 /*
503 Save a game.  We save the game in emp_save.dat.  Someday we may want
504 to ask the user for a file name.  If we cannot save the game, we will
505 tell the user why.
506 */
507
508 /* macro to save typing; write an array, return if it fails */
509 #define wbuf(buf) if (!xwrite (f, (char *)buf, sizeof (buf))) return
510 #define wval(val) if (!xwrite (f, (char *)&val, sizeof (val))) return
511
512 #define SAVECOOKIE      "EMPSAVE 1\n"   /* increase digit when format changes */
513
514 static char buf[32];
515
516 void save_game(void)
517 {
518     FILE *f; /* file to save game in */
519
520     f = fopen (savefile, "w"); /* open for output */
521     if (f == NULL) {
522         perror ("ゲームを保存できなかった");
523         return;
524     }
525     wbuf(SAVECOOKIE);
526     wbuf (map);
527     wbuf (comp_map);
528     wbuf (user_map);
529     wbuf (city);
530     wbuf (object);
531     wbuf (user_obj);
532     wbuf (comp_obj);
533     wval (free_list);
534     wval (date);
535     wval (automove);
536     wval (resigned);
537     wval (debug);
538     wval (win);
539     wval (save_movie);
540     wval (user_score);
541     wval (comp_score);
542
543     (void) fclose (f);
544     topmsg (3, "ゲームを保存した。");
545 }
546
547 /*
548 Recover a saved game from emp_save.dat.
549 We return true if we succeed, otherwise false.
550 */
551
552 #define rbuf(buf) if (!xread (f, (char *)buf, sizeof(buf))) return (false);
553 #define rval(val) if (!xread (f, (char *)&val, sizeof(val))) return (false);
554
555 int restore_game(void)
556 {
557     void read_embark(piece_info_t *, int);
558         
559     FILE *f; /* file to save game in */
560     long i;
561     piece_info_t **list;
562     piece_info_t *obj;
563
564     f = fopen (savefile, "r"); /* open for input */
565     if (f == NULL) {
566         perror ("ゲームを復帰できなかった");
567         return (false);
568     }
569     if (fgets(buf, sizeof(buf), f) == NULL)
570         return false;
571     else if (strcmp(buf, SAVECOOKIE) != 0)
572         return false;
573     i = fread(buf, 1, sizeof(char), f); /* skip trailing nul after cookie */ 
574     rbuf (map);
575     rbuf (comp_map);
576     rbuf (user_map);
577     rbuf (city);
578     rbuf (object);
579     rbuf (user_obj);
580     rbuf (comp_obj);
581     rval (free_list);
582     rval (date);
583     rval (automove);
584     rval (resigned);
585     rval (debug);
586     rval (win);
587     rval (save_movie);
588     rval (user_score);
589     rval (comp_score);
590
591     /* Our pointers may not be valid because of source
592        changes or other things.  We recreate them. */
593         
594     free_list = NULL; /* zero all ptrs */
595     for (i = 0; i < MAP_SIZE; i++) {
596         map[i].cityp = NULL;
597         map[i].objp = NULL;
598     }
599     for (i = 0; i < LIST_SIZE; i++) {
600         object[i].loc_link.next = NULL;
601         object[i].loc_link.prev = NULL;
602         object[i].cargo_link.next = NULL;
603         object[i].cargo_link.prev = NULL;
604         object[i].piece_link.next = NULL;
605         object[i].piece_link.prev = NULL;
606         object[i].ship = NULL;
607         object[i].cargo = NULL;
608     }
609     for (i = 0; i < NUM_OBJECTS; i++) {
610         comp_obj[i] = NULL;
611         user_obj[i] = NULL;
612     }
613     /* put cities on map */
614     for (i = 0; i < NUM_CITY; i++)
615         map[city[i].loc].cityp = &(city[i]);
616         
617     /* put pieces in free list or on map and in object lists */
618     for (i = 0; i < LIST_SIZE; i++) {
619         obj = &(object[i]);
620         if (object[i].owner == UNOWNED || object[i].hits == 0) {
621             LINK (free_list, obj, piece_link);
622         }
623         else {
624             list = LIST (object[i].owner);
625             LINK (list[object[i].type], obj, piece_link);
626             LINK (map[object[i].loc].objp, obj, loc_link);
627         }
628     }
629         
630     /* Embark armies and fighters. */
631     read_embark (user_obj[TRANSPORT], ARMY);
632     read_embark (user_obj[CARRIER], FIGHTER);
633     read_embark (comp_obj[TRANSPORT], ARMY);
634     read_embark (comp_obj[CARRIER], FIGHTER);
635         
636     (void) fclose (f);
637     kill_display (); /* what we had is no longer good */
638     topmsg (3, "保存されたゲームを復帰した。");
639     return (true);
640 }
641         
642 /*
643 Embark cargo on a ship.  We loop through the list of ships.
644 We then loop through the pieces at the ship's location until
645 the ship has the same amount of cargo it previously had.
646 */
647
648 void read_embark(piece_info_t *list, int piece_type)
649 {
650     void inconsistent(void);
651
652     piece_info_t *ship;
653     piece_info_t *obj;
654
655     for (ship = list; ship != NULL; ship = ship->piece_link.next) {
656         int count = ship->count; /* get # of pieces we need */
657         if (count < 0) inconsistent ();
658         ship->count = 0; /* nothing on board yet */
659         for (obj = map[ship->loc].objp; obj && count;
660              obj = obj->loc_link.next) {
661             if (obj->ship == NULL && obj->type == piece_type) {
662                 embark (ship, obj);
663                 count -= 1;
664             }
665         }
666         if (count) inconsistent ();
667     }
668 }
669
670 void inconsistent(void)
671 {
672     (void) printf ("保存されたゲームが不正だ。削除すること。\n");
673     exit (1);
674 }
675
676 /*
677 Write a buffer to a file.  If we cannot write everything, return false.
678 Also, tell the user why the write did not work if it didn't.
679 */
680
681 bool xwrite(FILE *f, char *buf, int size)
682 {
683     int bytes;
684  
685     bytes = fwrite (buf, 1, size, f);
686     if (bytes == -1) {
687         perror ("保存に失敗した");
688         return (false);
689     }
690     if (bytes != size) {
691         perror ("完全に書ききれなかった。\n");
692         return (false);
693     }
694     return (true);
695 }
696
697 /*
698 Read a buffer from a file.  If the read fails, we tell the user why
699 and return false.
700 */
701
702 bool xread(FILE *f, char *buf, int size)
703 {
704     int bytes;
705
706     bytes = fread (buf, 1, size, f);
707     if (bytes == -1) {
708         perror ("保存されたファイルを読み込めなかった");
709         return (false);
710     }
711     if (bytes != size) {
712         perror ("保存されたファイルが短すぎる。\n");
713         return (false);
714     }
715     return (true);
716 }
717
718 /*
719 Save a movie screen.  For each cell on the board, we write out
720 the character that would appear on either the user's or the
721 computer's screen.  This information is appended to 'empmovie.dat'.
722 */
723
724 extern char city_char[];
725 static char mapbuf[MAP_SIZE];
726
727 void
728 save_movie_screen(void)
729 {
730     FILE *f; /* file to save game in */
731     count_t i;
732     piece_info_t *p;
733
734     f = fopen ("empmovie.dat", "a"); /* open for append */
735     if (f == NULL) {
736         perror ("empmovie.datに書き込めなかった");
737         return;
738     }
739
740     for (i = 0; i < MAP_SIZE; i++) {
741         if (map[i].cityp) mapbuf[i] = city_char[map[i].cityp->owner];
742         else {
743             p = find_obj_at_loc (i);
744                         
745             if (!p) mapbuf[i] = map[i].contents;
746             else if (p->owner == USER)
747                 mapbuf[i] = piece_attr[p->type].sname;
748             else mapbuf[i] = tolower (piece_attr[p->type].sname);
749         }
750     }
751     wbuf (mapbuf);
752     (void) fclose (f);
753 }
754
755 /*
756 Replay a movie.  We read each buffer from the file and
757 print it using a zoomed display.
758 */
759
760 void
761 replay_movie(void)
762 {
763     void print_movie_cell(char *, int, int, int, int);
764
765     FILE *f; /* file to save game in */
766     int r, c;
767     int round;
768
769         
770     f = fopen ("empmovie.dat", "r"); /* open for input */
771     if (f == NULL) {
772         perror ("empmovie.datを読み込めなかった");
773         return;
774     }
775     round = 0;
776     clear_screen ();
777     for (;;) {
778         int row_inc, col_inc;
779         if (fread ((char *)mapbuf, 1, sizeof (mapbuf), f) != sizeof (mapbuf))
780             break;
781         round += 1;
782                 
783         stat_display (mapbuf, round);
784                 
785         row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
786         col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
787         
788         for (r = 0; r < MAP_HEIGHT; r += row_inc)
789             for (c = 0; c < MAP_WIDTH; c += col_inc)
790                 print_movie_cell (mapbuf, r, c, row_inc, col_inc);
791                 
792         (void) redisplay ();
793         delay ();
794     }
795     (void) fclose (f);
796 }
797
798 /*
799 Display statistics about the game.  At the top of the screen we
800 print:
801
802 nn O  nn A  nn F  nn P  nn D  nn S  nn T  nn C  nn B  nn Z  xxxxx
803 nn X  nn a  nn f  nn p  nn d  nn s  nn t  nn c  nn b  nn z  xxxxx
804
805 There may be objects in cities and boats that aren't displayed.
806 The "xxxxx" field is the cumulative cost of building the hardware.
807 */
808
809 /* in declared order, with city first */
810 static char *pieces = "OAFPDSTCBZXafpdstcbz";
811
812 void stat_display(char *mbuf, int round)
813 {
814     count_t i;
815     int counts[2*NUM_OBJECTS+2];
816     int user_cost, comp_cost;
817         
818     (void) memset ((char *)counts, '\0', sizeof (counts));
819         
820     for (i = 0; i < MAP_SIZE; i++) {
821         char *p = strchr (pieces, mbuf[i]);
822         if (p) counts[p-pieces] += 1;
823     }
824     user_cost = 0;
825     for (i = 1; i <= NUM_OBJECTS; i++)
826         user_cost += counts[i] * piece_attr[i-1].build_time;
827                 
828     comp_cost = 0;
829     for (i = NUM_OBJECTS+2; i <= 2*NUM_OBJECTS+1; i++)
830         comp_cost += counts[i] * piece_attr[i-NUM_OBJECTS-2].build_time;
831                 
832     for (i = 0; i < NUM_OBJECTS+1; i++) {
833         pos_str (1, (int) i * 6, "%2d %c  ", counts[i], pieces[i]);
834         pos_str (2,(int) i * 6, "%2d %c  ", counts[i+NUM_OBJECTS+1], pieces[i+NUM_OBJECTS+1]);
835     }
836
837     pos_str (1, (int) i * 6, "%5d", user_cost);
838     pos_str (2, (int) i * 6, "%5d", comp_cost);
839     pos_str (0, 0, "Round %3d", (round + 1) / 2);
840 }
841
842 /* end */