2 * Copyright (C) 1987, 1988 Chuck Simmons
4 * See the file COPYING, distributed with empire, for restriction
5 * and warranty information.
9 game.c -- Routines to initialize, save, and restore a game.
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);
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
35 void make_map(void), place_cities(void);
39 kill_display (); /* nothing on screen */
48 date = 0; /* no date yet */
52 for (i = 0; i < MAP_SIZE; i++) {
53 user_map[i].contents = ' '; /* nothing seen yet */
55 comp_map[i].contents = ' ';
58 for (i = 0; i < NUM_OBJECTS; i++) {
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 */
67 LINK (free_list, obj, piece_link);
70 make_map (); /* make land and water */
73 for (i = 0; i < MAP_SIZE; i ++) { /* remove cities */
74 if (map[i].contents == MAP_CITY)
75 map[i].contents = MAP_LAND; /* land */
77 place_cities (); /* place cities on map */
79 (!select_cities ()); /* choose a city for each player */
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.
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
94 #define MAX_HEIGHT 999 /* highest height */
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];
106 for (i = 0; i < MAP_SIZE; i++) /* fill map with random sand */
107 height[0][i] = irand (MAX_HEIGHT);
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];
120 height[to][j] = sum / 9;
122 k = to; /* swap to and from */
127 /* count the number of cells at each height */
128 for (i = 0; i <= MAX_HEIGHT; i++)
131 for (i = 0; i <= MAP_SIZE; i++)
132 height_count[height[from][i]]++;
134 /* find the water line */
135 loc = MAX_HEIGHT; /* default to all water */
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 */
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;
151 map[i].objp = NULL; /* nothing in cell yet */
157 map[i].on_board = !(j == 0 || j == MAP_WIDTH-1
158 || k == 0 || k == MAP_HEIGHT-1);
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.
170 /* avoid compiler problems with large automatic arrays */
171 static loc_t land[MAP_SIZE];
173 void place_cities(void)
175 count_t regen_land();
180 num_land = 0; /* nothing in land array yet */
181 placed = 0; /* nothing placed yet */
182 while (placed < NUM_CITY) {
186 while (num_land == 0) num_land = regen_land (placed);
187 i = irand (num_land-1); /* select random piece of land */
190 city[placed].loc = loc;
191 city[placed].owner = UNOWNED;
192 city[placed].work = 0;
193 city[placed].prod = NOPIECE;
195 for (i = 0; i < NUM_OBJECTS; i++)
196 city[placed].func[i] = NOFUNC; /* no function */
198 map[loc].contents = MAP_CITY;
199 map[loc].cityp = &(city[placed]);
202 /* Now remove any land too close to selected land. */
203 num_land = remove_land (loc, num_land);
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.
213 count_t regen_land(count_t placed)
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 */
225 if (placed > 0) { /* don't decrement 1st time */
227 ASSERT (MIN_CITY_DIST >= 0);
229 for (i = 0; i < placed; i++) { /* for each placed city */
230 num_land = remove_land (city[i].loc, num_land);
236 Remove land that is too close to a city.
239 count_t remove_land(loc_t loc, count_t num_land)
243 new = 0; /* nothing kept yet */
244 for (i = 0; i < num_land; i++) {
245 if (dist (loc, land[i]) >= MIN_CITY_DIST) {
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.
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.
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
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.
283 #define MAX_CONT 10 /* most continents we will allow */
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 */
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 */
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 */
303 bool select_cities(void)
305 void find_cont(void), make_pair(void);
308 city_info_t *compp, *userp;
309 int comp_cont, user_cont;
312 find_cont (); /* find and rank the continents */
313 if (ncont == 0) return (false); /* there are no good continents */
315 make_pair (); /* create list of ranked pairs */
317 (void) sprintf (jnkbuf,
318 "難易度を0(簡単)から%d(難しい)の間で選択せよ: ",
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;
325 compi = irand ((long)cont_tab[comp_cont].ncity);
326 compp = cont_tab[comp_cont].cityp[compi];
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);
333 topmsg(1, "あなたの都市は%dだ。", loc_disp(userp->loc));
334 delay (); /* let user see output before we set_prod */
336 /* update city and map */
340 scan (comp_map, compp->loc);
344 scan (user_map, userp->loc);
350 Find all continents with 2 cities or more, one of which must be a shore
351 city. We rank the continents.
359 for (i = 0; i < MAP_SIZE; i++) marked[i] = 0; /* nothing marked yet */
361 ncont = 0; /* no continents found yet */
364 while (ncont < MAX_CONT)
365 if (!find_next (&mapi))
366 return; /* all found */
370 Find the next continent and insert it in the rank table.
371 If there are no more continents, we return false.
374 bool find_next(loc_t *mapi)
380 if (*mapi >= MAP_SIZE) return (false);
382 if (!map[*mapi].on_board || marked[*mapi]
383 || map[*mapi].contents == MAP_SEA)
385 else if (good_cont (*mapi)) {
386 rank_tab[ncont] = ncont; /* insert cont in rank tab */
387 val = cont_tab[ncont].value;
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;
396 ncont++; /* count continents */
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.
409 static count_t ncity, nland, nshore;
410 static void mark_cont(loc_t);
412 bool good_cont(loc_t mapi)
416 ncity = 0; /* nothing seen yet */
422 if (nshore < 1 || ncity < 2) return (false);
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
429 if (ncity == nshore) val = (nshore - 2) * 3;
430 else val = (nshore-1) * 3 + (ncity - nshore - 1) * 2;
432 val *= 1000; /* cities are worth a lot */
434 cont_tab[ncont].value = val;
435 cont_tab[ncont].ncity = ncity;
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.
447 mark_cont(loc_t mapi)
452 || map[mapi].contents == MAP_SEA
453 || !map[mapi].on_board)
456 marked[mapi] = 1; /* mark this cell seen */
457 nland++; /* count land on continent */
459 if (map[mapi].contents == MAP_CITY) { /* a city? */
460 cont_tab[ncont].cityp[ncity] = map[mapi].cityp;
462 if (rmap_shore (mapi)) nshore++;
465 for (i = 0; i < 8; i++) /* look at surrounding squares */
466 mark_cont (mapi + dir_offset[i]);
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.
481 npair = 0; /* none yet */
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;
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;
498 npair++; /* count pairs */
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
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
512 #define SAVECOOKIE "EMPSAVE 1\n" /* increase digit when format changes */
518 FILE *f; /* file to save game in */
520 f = fopen (savefile, "w"); /* open for output */
522 perror ("ゲームを保存できなかった");
544 topmsg (3, "ゲームを保存した。");
548 Recover a saved game from emp_save.dat.
549 We return true if we succeed, otherwise false.
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);
555 int restore_game(void)
557 void read_embark(piece_info_t *, int);
559 FILE *f; /* file to save game in */
564 f = fopen (savefile, "r"); /* open for input */
566 perror ("ゲームを復帰できなかった");
569 if (fgets(buf, sizeof(buf), f) == NULL)
571 else if (strcmp(buf, SAVECOOKIE) != 0)
573 i = fread(buf, 1, sizeof(char), f); /* skip trailing nul after cookie */
591 /* Our pointers may not be valid because of source
592 changes or other things. We recreate them. */
594 free_list = NULL; /* zero all ptrs */
595 for (i = 0; i < MAP_SIZE; i++) {
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;
609 for (i = 0; i < NUM_OBJECTS; i++) {
613 /* put cities on map */
614 for (i = 0; i < NUM_CITY; i++)
615 map[city[i].loc].cityp = &(city[i]);
617 /* put pieces in free list or on map and in object lists */
618 for (i = 0; i < LIST_SIZE; i++) {
620 if (object[i].owner == UNOWNED || object[i].hits == 0) {
621 LINK (free_list, obj, piece_link);
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);
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);
637 kill_display (); /* what we had is no longer good */
638 topmsg (3, "保存されたゲームを復帰した。");
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.
648 void read_embark(piece_info_t *list, int piece_type)
650 void inconsistent(void);
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) {
666 if (count) inconsistent ();
670 void inconsistent(void)
672 (void) printf ("保存されたゲームが不正だ。削除すること。\n");
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.
681 bool xwrite(FILE *f, char *buf, int size)
685 bytes = fwrite (buf, 1, size, f);
691 perror ("完全に書ききれなかった。\n");
698 Read a buffer from a file. If the read fails, we tell the user why
702 bool xread(FILE *f, char *buf, int size)
706 bytes = fread (buf, 1, size, f);
708 perror ("保存されたファイルを読み込めなかった");
712 perror ("保存されたファイルが短すぎる。\n");
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'.
724 extern char city_char[];
725 static char mapbuf[MAP_SIZE];
728 save_movie_screen(void)
730 FILE *f; /* file to save game in */
734 f = fopen ("empmovie.dat", "a"); /* open for append */
736 perror ("empmovie.datに書き込めなかった");
740 for (i = 0; i < MAP_SIZE; i++) {
741 if (map[i].cityp) mapbuf[i] = city_char[map[i].cityp->owner];
743 p = find_obj_at_loc (i);
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);
756 Replay a movie. We read each buffer from the file and
757 print it using a zoomed display.
763 void print_movie_cell(char *, int, int, int, int);
765 FILE *f; /* file to save game in */
770 f = fopen ("empmovie.dat", "r"); /* open for input */
772 perror ("empmovie.datを読み込めなかった");
778 int row_inc, col_inc;
779 if (fread ((char *)mapbuf, 1, sizeof (mapbuf), f) != sizeof (mapbuf))
783 stat_display (mapbuf, round);
785 row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
786 col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
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);
799 Display statistics about the game. At the top of the screen we
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
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.
809 /* in declared order, with city first */
810 static char *pieces = "OAFPDSTCBZXafpdstcbz";
812 void stat_display(char *mbuf, int round)
815 int counts[2*NUM_OBJECTS+2];
816 int user_cost, comp_cost;
818 (void) memset ((char *)counts, '\0', sizeof (counts));
820 for (i = 0; i < MAP_SIZE; i++) {
821 char *p = strchr (pieces, mbuf[i]);
822 if (p) counts[p-pieces] += 1;
825 for (i = 1; i <= NUM_OBJECTS; i++)
826 user_cost += counts[i] * piece_attr[i-1].build_time;
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;
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]);
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);