Path: utzoo!hoptoad!amdahl!chuck From: chuck@amdahl.uts.amdahl.com (Charles Simmons) Newsgroups: alt.sources Subject: empire sources (part 1 of 6) Message-ID: <27519@amdahl.uts.amdahl.com> Date: 11 Apr 88 00:59:20 GMT Distribution: alt Organization: Amdahl Corporation, Sunnyvale CA Lines: 2020 # This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # map.c object.c main.c echo x - map.c sed -e 's/^X//' > "map.c" << '//E*O*F map.c//' X/* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */ X X/* X * Copyright (C) 1987, 1988 Chuck Simmons X * X * See the file COPYING, distributed with empire, for restriction X * and warranty information. X */ X X/* Xmap.c X XThis file contains routines for playing around with view_maps, Xreal_maps, path_maps, and cont_maps. X*/ X X#ifdef SYSV X#include X#else X#include X#endif X X#include "empire.h" X#include "extern.h" X X#ifndef PROFILE X#define STATIC static X#else X#define STATIC X/* can't get accurate profile when procedures are static */ X#endif X X#define SWAP(a,b) { \ X perimeter_t *x; \ X x = a; a = b; b = x; \ X} X XSTATIC void expand_perimeter(); XSTATIC void expand_prune(); XSTATIC void expand_dest_perimeter(); XSTATIC int objective_cost(); XSTATIC int terrain_type(); XSTATIC void start_perimeter(); XSTATIC void add_cell(); X Xstatic perimeter_t p1; /* perimeter list for use as needed */ Xstatic perimeter_t p2; Xstatic perimeter_t p3; Xstatic perimeter_t p4; X Xstatic int best_cost; /* cost and location of best objective */ Xstatic long best_loc; X X/* XMap out a continent. We are given a location on the continent. XWe mark each square that is part of the continent and unexplored Xterritory adjacent to the continent. By adjusting the value of X'bad_terrain', this routine can map either continents of land, Xor lakes. X*/ X Xvoid Xvmap_cont (cont_map, vmap, loc, bad_terrain) Xint *cont_map; Xview_map_t *vmap; Xlong loc; Xchar bad_terrain; X{ X (void) bzero ((char *)cont_map, MAP_SIZE * sizeof (int)); X vmap_mark_up_cont (cont_map, vmap, loc, bad_terrain); X} X X/* XMark all squares of a continent and the squares that are adjacent Xto the continent which are on the board. Our passed location is Xknown to be either on the continent or adjacent to the continent. X*/ X Xvoid Xvmap_mark_up_cont (cont_map, vmap, loc, bad_terrain) Xint *cont_map; Xview_map_t *vmap; Xlong loc; Xchar bad_terrain; X{ X int i, j; X long new_loc; X char this_terrain; X perimeter_t *from, *to; X X from = &p1; X to = &p2; X X from->len = 1; /* init perimeter */ X from->list[0] = loc; X cont_map[loc] = 1; /* loc is on continent */ X X while (from->len) { X to->len = 0; /* nothing in new perimeter yet */ X X for (i = 0; i < from->len; i++) /* expand perimeter */ X FOR_ADJ_ON(from->list[i], new_loc, j) X if (!cont_map[new_loc]) { X /* mark, but don't expand, unexplored territory */ X if (vmap[new_loc].contents == ' ') X cont_map[new_loc] = 1; X else { X if (vmap[new_loc].contents == '+') this_terrain = '+'; X else if (vmap[new_loc].contents == '.') this_terrain = '.'; X else this_terrain = map[new_loc].contents; X X if (this_terrain != bad_terrain) { /* on continent? */ X cont_map[new_loc] = 1; X to->list[to->len] = new_loc; X to->len += 1; X } X } X } X SWAP (from, to); X } X} X X/* XMap out a continent. We are given a location on the continent. XWe mark each square that is part of the continent. XBy adjusting the value of X'bad_terrain', this routine can map either continents of land, Xor lakes. X*/ X Xstatic void rmap_mark_up_cont(); X Xvoid Xrmap_cont (cont_map, loc, bad_terrain) Xint *cont_map; Xlong loc; Xchar bad_terrain; X{ X (void) bzero ((char *)cont_map, MAP_SIZE * sizeof (int)); X rmap_mark_up_cont (cont_map, loc, bad_terrain); X} X X/* XMark all squares of a continent and the squares that are adjacent Xto the continent which are on the board. Our passed location is Xknown to be either on the continent or adjacent to the continent. X XSomeday this should be tweaked to use perimeter lists. X*/ X Xstatic void Xrmap_mark_up_cont (cont_map, loc, bad_terrain) Xint *cont_map; Xlong loc; Xchar bad_terrain; X{ X int i; X long new_loc; X X if (!map[loc].on_board) return; /* off board */ X if (cont_map[loc]) return; /* already marked */ X if (map[loc].contents == bad_terrain) return; /* off continent */ X X cont_map[loc] = 1; /* on continent */ X X FOR_ADJ (loc, new_loc, i) X rmap_mark_up_cont (cont_map, new_loc, bad_terrain); X} X X/* XScan a continent recording items of interest on the continent. X XThis could be done as we mark up the continent. X*/ X X#define COUNT(c,item) case c: item += 1; break X Xscan_counts_t Xvmap_cont_scan (cont_map, vmap) Xint *cont_map; Xview_map_t *vmap; X{ X scan_counts_t counts; X long i; X X (void) bzero ((char *)&counts, sizeof (scan_counts_t)); X X for (i = 0; i < MAP_SIZE; i++) { X if (cont_map[i]) { /* cell on continent? */ X counts.size += 1; X X switch (vmap[i].contents) { X COUNT (' ', counts.unexplored); X COUNT ('O', counts.user_cities); X COUNT ('A', counts.user_objects[ARMY]); X COUNT ('F', counts.user_objects[FIGHTER]); X COUNT ('P', counts.user_objects[PATROL]); X COUNT ('D', counts.user_objects[DESTROYER]); X COUNT ('S', counts.user_objects[SUBMARINE]); X COUNT ('T', counts.user_objects[TRANSPORT]); X COUNT ('C', counts.user_objects[CARRIER]); X COUNT ('B', counts.user_objects[BATTLESHIP]); X COUNT ('X', counts.comp_cities); X COUNT ('a', counts.comp_objects[ARMY]); X COUNT ('f', counts.comp_objects[FIGHTER]); X COUNT ('p', counts.comp_objects[PATROL]); X COUNT ('d', counts.comp_objects[DESTROYER]); X COUNT ('s', counts.comp_objects[SUBMARINE]); X COUNT ('t', counts.comp_objects[TRANSPORT]); X COUNT ('c', counts.comp_objects[CARRIER]); X COUNT ('b', counts.comp_objects[BATTLESHIP]); X COUNT ('*', counts.unowned_cities); X case '+': break; X case '.': break; X default: /* check for city underneath */ X if (map[i].contents == '*') { X switch (map[i].cityp->owner) { X COUNT (USER, counts.user_cities); X COUNT (COMP, counts.comp_cities); X COUNT (UNOWNED, counts.unowned_cities); X } X } X } X } X } X return counts; X} X X/* XScan a real map as above. Only the 'size' and 'unowned_cities' Xfields are valid. X*/ X Xscan_counts_t Xrmap_cont_scan (cont_map) Xint *cont_map; X{ X scan_counts_t counts; X long i; X X (void) bzero ((char *)&counts, sizeof (scan_counts_t)); X X for (i = 0; i < MAP_SIZE; i++) { X if (cont_map[i]) { /* cell on continent? */ X counts.size += 1; X if (map[i].contents == '*') X counts.unowned_cities += 1; X } X } X return counts; X} X X/* XReturn TRUE if a location is on the edge of a continent. X*/ X Xint Xmap_cont_edge (cont_map, loc) Xint *cont_map; Xlong loc; X{ X long i, j; X X if (!cont_map[loc]) return FALSE; /* not on continent */ X X FOR_ADJ (loc, j, i) X if (!cont_map[j]) return TRUE; /* edge of continent */ X X return FALSE; X} X X/* XFind the nearest objective for a piece. This routine actually does Xsome real work. This code represents my fourth rewrite of the Xalgorithm. This algorithm is central to the strategy used by the Xcomputer. X XGiven a view_map, we create a path_map. On the path_map, we record Xthe distance from a location to the nearest objective. We are Xgiven information about what the interesting objectives are, and Xhow interesting each objective is. X XWe use a breadth first search to find the nearest objective. XWe maintain something called a "perimeter list". This list Xinitially contains a list of squares that we can reach in 'n' moves. XOn each pass through our loop, we add all squares that are adjacent Xto the perimeter list and which lie outside the perimeter to our Xlist. (The loop is only slightly more complicated for armies and Xtransports.) X XWhen our perimeter list becomes empty, or when the distance to Xthe current perimeter is at least as large as the weighted distance Xto the best objective, we return the location of the best objective Xfound. X XThe 'cost' field in a path_map must be INFINITY if the cell lies Xoutside of the current perimeter. The cost for cells that lie Xon or within the current perimeter doesn't matter, except that Xthe information must be consistent with the needs of 'vmap_mark_path'. X*/ X X/* Find an objective over a single type of terrain. */ X Xlong Xvmap_find_xobj (path_map, vmap, loc, move_info, start, expand) Xpath_map_t path_map[]; Xview_map_t *vmap; Xlong loc; Xmove_info_t *move_info; Xint start; Xint expand; X{ X perimeter_t *from; X perimeter_t *to; X int cur_cost; X X from = &p1; X to = &p2; X X start_perimeter (path_map, from, loc, start); X cur_cost = 0; /* cost to reach current perimeter */ X X for (;;) { X to->len = 0; /* nothing in perim yet */ X expand_perimeter (path_map, vmap, move_info, from, expand, X cur_cost, 1, 1, to, to); X X if (trace_pmap) X print_pzoom ("After xobj loop:", path_map, vmap); X X cur_cost += 1; X if (to->len == 0 || best_cost <= cur_cost) X return best_loc; X X SWAP (from, to); X } X} X X/* Find an objective for a piece that crosses land and water. */ X Xlong Xvmap_find_aobj (path_map, vmap, loc, move_info) Xpath_map_t path_map[]; Xview_map_t *vmap; Xlong loc; Xmove_info_t *move_info; X{ X return vmap_find_xobj (path_map, vmap, loc, move_info, T_LAND, T_AIR); X} X X/* Find an objective for a piece that crosses only water. */ X Xlong Xvmap_find_wobj (path_map, vmap, loc, move_info) Xpath_map_t path_map[]; Xview_map_t *vmap; Xlong loc; Xmove_info_t *move_info; X{ X return vmap_find_xobj (path_map, vmap, loc, move_info, T_WATER, T_WATER); X} X X/* Find an objective for a piece that crosses only land. */ X Xlong Xvmap_find_lobj (path_map, vmap, loc, move_info) Xpath_map_t path_map[]; Xview_map_t *vmap; Xlong loc; Xmove_info_t *move_info; X{ X return vmap_find_xobj (path_map, vmap, loc, move_info, T_LAND, T_LAND); X} X X/* XFind an objective moving from land to water. XThis is mildly complicated. It costs 2 to move on land Xand one to move on water. To handle this, we expand our current Xperimeter by one cell, where land can be expanded to either Xland or water, and water is only expanded to water. Then Xwe expand any water one more cell. X XWe have different objectives depending on whether the objective Xis being approached from the land or the water. X*/ X Xlong Xvmap_find_lwobj (path_map, vmap, loc, move_info, beat_cost) Xpath_map_t path_map[]; Xview_map_t *vmap; Xlong loc; Xmove_info_t *move_info; Xint beat_cost; X{ X perimeter_t *cur_land; X perimeter_t *cur_water; X perimeter_t *new_land; X perimeter_t *new_water; X int cur_cost; X X cur_land = &p1; X cur_water = &p2; X new_water = &p3; X new_land = &p4; X X start_perimeter (path_map, cur_land, loc, T_LAND); X cur_water->len = 0; X best_cost = beat_cost; /* we can do this well */ X cur_cost = 0; /* cost to reach current perimeter */ X X for (;;) { X /* expand current perimeter one cell */ X new_water->len = 0; X new_land->len = 0; X expand_perimeter (path_map, vmap, move_info, cur_water, X T_WATER, cur_cost, 1, 1, new_water, NULL); X X expand_perimeter (path_map, vmap, move_info, cur_land, X T_AIR, cur_cost, 1, 2, new_water, new_land); X X /* expand new water one cell */ X cur_water->len = 0; X expand_perimeter (path_map, vmap, move_info, new_water, X T_WATER, cur_cost+1, 1, 1, cur_water, NULL); X X if (trace_pmap) X print_pzoom ("After lwobj loop:", path_map, vmap); X X cur_cost += 2; X if (cur_water->len == 0 && new_land->len == 0 || best_cost <= cur_cost) { X return best_loc; X } X X SWAP (cur_land, new_land); X } X} X X/* XReturn the cost to reach the adjacent cell of the correct type Xwith the lowest cost. X*/ X XSTATIC int Xbest_adj (pmap, loc, type) Xpath_map_t *pmap; Xlong loc; Xint type; X{ X int i; X long new_loc; X int best; X X best = INFINITY; X X FOR_ADJ (loc, new_loc, i) X if (pmap[new_loc].terrain == type && pmap[new_loc].cost < best) X best = pmap[new_loc].cost; X X return best; X} X X/* XFind an objective moving from water to land. XHere, we expand water to either land or water. XWe expand land only to land. X XWe cheat ever so slightly, but this cheating accurately reflects Xthe mechanics o moving. The first time we expand water we can Xexpand to land or water (army moving off tt or tt moving on water), Xbut the second time, we only expand water (tt taking its second move). X*/ X Xlong Xvmap_find_wlobj (path_map, vmap, loc, move_info) Xpath_map_t path_map[]; Xview_map_t *vmap; Xlong loc; Xmove_info_t *move_info; X{ X perimeter_t *cur_land; X perimeter_t *cur_water; X perimeter_t *new_land; X perimeter_t *new_water; X int cur_cost; X X cur_land = &p1; X cur_water = &p2; X new_water = &p3; X new_land = &p4; X X start_perimeter (path_map, cur_water, loc, T_WATER); X cur_land->len = 0; X cur_cost = 0; /* cost to reach current perimeter */ X X for (;;) { X /* expand current perimeter one cell */ X new_water->len = 0; X new_land->len = 0; X expand_perimeter (path_map, vmap, move_info, cur_water, X T_AIR, cur_cost, 1, 2, new_water, new_land); X X expand_perimeter (path_map, vmap, move_info, cur_land, X T_LAND, cur_cost, 1, 2, NULL, new_land); X X /* expand new water one cell to water */ X cur_water->len = 0; X expand_perimeter (path_map, vmap, move_info, new_water, X T_WATER, cur_cost+1, 1, 1, cur_water, NULL); X X if (trace_pmap) X print_pzoom ("After wlobj loop:", path_map, vmap); X X cur_cost += 2; X if (cur_water->len == 0 && new_land->len == 0 || best_cost <= cur_cost) { X return best_loc; X } X SWAP (cur_land, new_land); X } X} X X/* XInitialize the perimeter searching. X XThis routine was taking a significant amount of the program time (10%) Xdoing the initialization of the path map. We now use an external Xconstant and 'memcpy'. X*/ X Xstatic path_map_t pmap_init[MAP_SIZE]; Xstatic int init_done = 0; X XSTATIC void Xstart_perimeter (pmap, perim, loc, terrain) Xpath_map_t *pmap; Xperimeter_t *perim; Xlong loc; Xint terrain; X{ X int i; X X /* zap the path map */ X if (!init_done) { X init_done = 1; X for (i = 0; i < MAP_SIZE; i++) { X pmap_init[i].cost = INFINITY; /* everything lies outside perim */ X pmap_init[i].terrain = T_UNKNOWN; X } X } X (void) memcpy ((char *)pmap, (char *)pmap_init, sizeof (pmap_init)); X X /* put first location in perimeter */ X pmap[loc].cost = 0; X pmap[loc].inc_cost = 0; X pmap[loc].terrain = terrain; X X perim->len = 1; X perim->list[0] = loc; X X best_cost = INFINITY; /* no best yet */ X best_loc = loc; /* if nothing found, result is current loc */ X} X X/* XExpand the perimeter. X XNote that 'waterp' and 'landp' may be the same. X XFor each cell of the current perimeter, we examine each Xcell adjacent to that cell which lies outside of the current Xperimeter. If the adjacent cell is an objective, we update Xbest_cost and best_loc. If the adjacent cell is of the correct Xtype, we turn place the adjacent cell in either the new water perimeter Xor the new land perimeter. X XWe set the cost to reach the current perimeter. X*/ X XSTATIC void Xexpand_perimeter (pmap, vmap, move_info, curp, type, cur_cost, inc_wcost, inc_lcost, waterp, landp) Xpath_map_t *pmap; /* path map to update */ Xview_map_t *vmap; Xmove_info_t *move_info; /* objectives and weights */ Xperimeter_t *curp; /* perimeter to expand */ Xint type; /* type of terrain to expand */ Xint cur_cost; /* cost to reach cells on perimeter */ Xint inc_wcost; /* cost to enter new water cells */ Xint inc_lcost; /* cost to enter new land cells */ Xperimeter_t *waterp; /* pointer to new water perimeter */ Xperimeter_t *landp; /* pointer to new land perimeter */ X{ X long i; X int j; X long new_loc; X int obj_cost; X int new_type; X X for (i = 0; i < curp->len; i++) /* for each perimeter cell... */ X FOR_ADJ_ON (curp->list[i], new_loc, j) /* for each adjacent cell... */ X if (pmap[new_loc].cost == INFINITY) { X new_type = terrain_type (pmap, vmap, move_info, curp->list[i], new_loc); X X if (new_type == T_LAND && (type & T_LAND)) X add_cell (pmap, new_loc, landp, new_type, cur_cost, inc_lcost); X else if (new_type == T_WATER && (type & T_WATER)) X add_cell (pmap, new_loc, waterp, new_type, cur_cost, inc_wcost); X else if (new_type == T_UNKNOWN) { /* unreachable cell? */ X pmap[new_loc].terrain = new_type; X pmap[new_loc].cost = cur_cost + INFINITY/2; X pmap[new_loc].inc_cost = INFINITY/2; X } X if (pmap[new_loc].cost != INFINITY) { /* did we expand? */ X obj_cost = objective_cost (vmap, move_info, new_loc, cur_cost); X if (obj_cost < best_cost) { X best_cost = obj_cost; X best_loc = new_loc; X if (new_type == T_UNKNOWN) { X pmap[new_loc].cost = cur_cost + 2; X pmap[new_loc].inc_cost = 2; X } X } X } X } X} X X/* Add a cell to a perimeter list. */ X XSTATIC void Xadd_cell (pmap, new_loc, perim, terrain, cur_cost, inc_cost) Xpath_map_t *pmap; Xlong new_loc; Xperimeter_t *perim; Xint terrain; Xint cur_cost; Xint inc_cost; X{ X pmap[new_loc].terrain = terrain; X pmap[new_loc].inc_cost = inc_cost; X pmap[new_loc].cost = cur_cost + inc_cost; X X perim->list[perim->len] = new_loc; X perim->len += 1; X} X X/* Compute the cost to move to an objective. */ X XSTATIC int Xobjective_cost (vmap, move_info, loc, base_cost) Xview_map_t *vmap; Xmove_info_t *move_info; Xlong loc; Xint base_cost; X{ X char *p; X int w; X city_info_t *cityp; X X p = strchr (move_info->objectives, vmap[loc].contents); X if (!p) return INFINITY; X X w = move_info->weights[p - move_info->objectives]; X if (w >= 0) return w + base_cost; X X switch (w) { X case W_TT_BUILD: X /* handle special case of moving to tt building city */ X cityp = find_city (loc); X if (!cityp) return base_cost + 2; /* tt is already here */ X if (cityp->prod != TRANSPORT) return base_cost + 2; /* just finished a tt */ X X /* compute time to wait for tt to be built */ X w = piece_attr[TRANSPORT].build_time - cityp->work; X w *= 2; /* had to cross land to get here */ X if (w < base_cost + 2) w = base_cost + 2; X return w; X X default: X ABORT; X /* NOTREACHED */ X } X} X X/* XReturn the type of terrain at a vmap location. X*/ X XSTATIC int Xterrain_type (pmap, vmap, move_info, from_loc, to_loc) Xpath_map_t *pmap; Xview_map_t *vmap; Xmove_info_t *move_info; Xlong from_loc; Xlong to_loc; X{ X if (vmap[to_loc].contents == '+') return T_LAND; X if (vmap[to_loc].contents == '.') return T_WATER; X if (vmap[to_loc].contents == '%') return T_UNKNOWN; /* magic objective */ X if (vmap[to_loc].contents == ' ') return pmap[from_loc].terrain; X X switch (map[to_loc].contents) { X case '.': return T_WATER; X case '+': return T_LAND; X case '*': X if (map[to_loc].cityp->owner == move_info->city_owner) X return T_WATER; X else return T_UNKNOWN; /* cannot cross */ X } X ABORT; X /*NOTREACHED*/ X} X X/* XPrune unexplored territory. We take a view map and we modify it Xso that unexplored territory that is adjacent to a lot of land Xor a lot of water is marked as being either that land or water. XSo basically, we are making a predicition about what we expect Xfor land and water. We iterate this algorithm until either Xthe next iteration would remove all unexplored territory, or Xthere is nothing more about which we can make an assumption. X XFirst, we use a pathmap to save the number of adjacent land Xand water cells for each unexplored cell. Cells which have Xadjacent explored territory are placed in a perimeter list. XWe also count the number of cells that are not unexplored. X XWe now take this perimeter list and make high-probability Xpredictions. X XThen we round things off by making one pass of medium Xprobability predictions. X XThen we make multiple passes extending our predictions. X XWe stop if at any point all remaining unexplored cells are Xin a perimeter list, or if no predictions were made during Xone of the final passes. X XUnlike other algorithms, here we deal with "off board" locations. XSo be careful. X*/ X Xvoid Xvmap_prune_explore_locs (vmap) Xview_map_t *vmap; X{ X path_map_t pmap[MAP_SIZE]; X perimeter_t *from, *to; X int explored; X long loc, new_loc; X long i; X long copied; X X (void) bzero (pmap, sizeof (pmap)); X from = &p1; X to = &p2; X from->len = 0; X explored = 0; X X /* build initial path map and perimeter list */ X for (loc = 0; loc < MAP_SIZE; loc++) { X if (vmap[loc].contents != ' ') explored += 1; X else { /* add unexplored cell to perim */ X FOR_ADJ (loc, new_loc, i) { X if (new_loc < 0 || new_loc >= MAP_SIZE); /* ignore off map */ X else if (vmap[new_loc].contents == ' '); /* ignore adjacent unexplored */ X else if (map[new_loc].contents != '.') X pmap[loc].cost += 1; /* count land */ X else pmap[loc].inc_cost += 1; /* count water */ X } X if (pmap[loc].cost || pmap[loc].inc_cost) { X from->list[from->len] = loc; X from->len += 1; X } X } X } X X if (print_vmap == 'I') print_xzoom (vmap); X X for (;;) { /* do high probability predictions */ X if (from->len + explored == MAP_SIZE) return; X to->len = 0; X copied = 0; X X for (i = 0; i < from->len; i++) { X loc = from->list[i]; X if (pmap[loc].cost >= 5) X expand_prune (vmap, pmap, loc, T_LAND, to, &explored); X else if (pmap[loc].inc_cost >= 5) X expand_prune (vmap, pmap, loc, T_WATER, to, &explored); X else if ((loc < MAP_WIDTH || loc >= MAP_SIZE-MAP_WIDTH) && pmap[loc].cost >= 3) X expand_prune (vmap, pmap, loc, T_LAND, to, &explored); X else if ((loc < MAP_WIDTH || loc >= MAP_SIZE-MAP_WIDTH) && pmap[loc].inc_cost >= 3) X expand_prune (vmap, pmap, loc, T_WATER, to, &explored); X else if ((loc == 0 || loc == MAP_SIZE-1) && pmap[loc].cost >= 2) X expand_prune (vmap, pmap, loc, T_LAND, to, &explored); X else if ((loc == 0 || loc == MAP_SIZE-1) && pmap[loc].inc_cost >= 2) X expand_prune (vmap, pmap, loc, T_WATER, to, &explored); X else { /* copy perimeter cell */ X to->list[to->len] = loc; X to->len += 1; X copied += 1; X } X } X if (copied == from->len) break; /* nothing expanded */ X SWAP (from, to); X } X X if (print_vmap == 'I') print_xzoom (vmap); X X /* one pass for medium probability predictions */ X if (from->len + explored == MAP_SIZE) return; X to->len = 0; X X for (i = 0; i < from->len; i++) { X loc = from->list[i]; X if (pmap[loc].cost > pmap[loc].inc_cost) X expand_prune (vmap, pmap, loc, T_LAND, to, &explored); X else if (pmap[loc].cost < pmap[loc].inc_cost) X expand_prune (vmap, pmap, loc, T_WATER, to, &explored); X else { /* copy perimeter cell */ X to->list[to->len] = loc; X to->len += 1; X } X } X SWAP (from, to); X X if (print_vmap == 'I') print_xzoom (vmap); X X /* multiple low probability passes */ X for (;;) { X /* return if very little left to explore */ X if (from->len + explored >= MAP_SIZE - MAP_HEIGHT) { X if (print_vmap == 'I') print_xzoom (vmap); X return; X } X to->len = 0; X copied = 0; X X for (i = 0; i < from->len; i++) { X loc = from->list[i]; X if (pmap[loc].cost >= 4 && pmap[loc].inc_cost < 4) X expand_prune (vmap, pmap, loc, T_LAND, to, &explored); X else if (pmap[loc].inc_cost >= 4 && pmap[loc].cost < 4) X expand_prune (vmap, pmap, loc, T_WATER, to, &explored); X else if ((loc < MAP_WIDTH || loc >= MAP_SIZE-MAP_WIDTH) && pmap[loc].cost > pmap[loc].inc_cost) X expand_prune (vmap, pmap, loc, T_LAND, to, &explored); X else if ((loc < MAP_WIDTH || loc >= MAP_SIZE-MAP_WIDTH) && pmap[loc].inc_cost > pmap[loc].cost) X expand_prune (vmap, pmap, loc, T_WATER, to, &explored); X else { /* copy perimeter cell */ X to->list[to->len] = loc; X to->len += 1; X copied += 1; X } X } X if (copied == from->len) break; /* nothing expanded */ X SWAP (from, to); X } X if (print_vmap == 'I') print_xzoom (vmap); X} X X/* XExpand an unexplored cell. We increment the land or water count Xof each neighbor. Any neighbor that acquires a non-zero count Xis added to the 'to' perimiter list. The count of explored Xterritory is incremented. X XCareful: 'loc' may be "off board". X*/ X XSTATIC void Xexpand_prune (vmap, pmap, loc, type, to, explored) Xview_map_t *vmap; Xpath_map_t *pmap; Xlong loc; Xint type; Xperimeter_t *to; Xint *explored; X{ X int i; X long new_loc; X X *explored += 1; X X if (type == T_LAND) vmap[loc].contents = '+'; X else vmap[loc].contents = '.'; X X FOR_ADJ (loc, new_loc, i) X if (new_loc >= 0 && new_loc < MAP_SIZE && vmap[new_loc].contents == ' ') { X if (!pmap[new_loc].cost && !pmap[new_loc].inc_cost) { X to->list[to->len] = new_loc; X to->len += 1; X } X if (type == T_LAND) X pmap[new_loc].cost += 1; X else pmap[new_loc].inc_cost += 1; X } X} X X/* XFind the shortest path from the current location to the Xdestination which passes over valid terrain. We return Xthe destination if a path exists. Otherwise we return the Xorigin. X XThis is similar to 'find_objective' except that we know our destination. X*/ X Xlong Xvmap_find_dest (path_map, vmap, cur_loc, dest_loc, owner, terrain) Xpath_map_t path_map[]; Xview_map_t vmap[]; Xlong cur_loc; /* current location of piece */ Xlong dest_loc; /* destination of piece */ Xint owner; /* owner of piece being moved */ Xint terrain; /* terrain we can cross */ X{ X perimeter_t *from; X perimeter_t *to; X int cur_cost; X int start_terrain; X move_info_t move_info; X char old_contents; X X old_contents = vmap[dest_loc].contents; X vmap[dest_loc].contents = '%'; /* mark objective */ X move_info.city_owner = owner; X move_info.objectives = "%"; X move_info.weights[0] = 1; X X from = &p1; X to = &p2; X X if (terrain == T_AIR) start_terrain = T_LAND; X else start_terrain = terrain; X X start_perimeter (path_map, from, cur_loc, start_terrain); X cur_cost = 0; /* cost to reach current perimeter */ X X for (;;) { X to->len = 0; /* nothing in perim yet */ X expand_perimeter (path_map, vmap, &move_info, from, X terrain, cur_cost, 1, 1, to, to); X cur_cost += 1; X if (to->len == 0 || best_cost <= cur_cost) { X vmap[dest_loc].contents = old_contents; X return best_loc; X } X SWAP (from, to); X } X} X X/* XStarting with the destination, we recursively back track toward the source Xmarking all cells which are on a shortest path between the start and the Xdestination. To do this, we know the distance from the destination to Xthe start. The destination is on a path. We then find the cells adjacent Xto the destination and nearest to the source and place them on the path. X XIf we know square P is on the path, then S is on the path if S is Xadjacent to P, the cost to reach S is less than the cost to reach P, Xand the cost to move from S to P is the difference in cost between XS and P. X XSomeday, this routine should probably use perimeter lists as well. X*/ X Xvoid Xvmap_mark_path (path_map, vmap, dest) Xpath_map_t *path_map; Xview_map_t *vmap; Xlong dest; X{ X int n; X long new_dest; X X if (path_map[dest].cost == 0) return; /* reached end of path */ X if (path_map[dest].terrain == T_PATH) return; /* already marked */ X X path_map[dest].terrain = T_PATH; /* this square is on path */ X X /* loop to mark adjacent squares on shortest path */ X FOR_ADJ (dest, new_dest, n) X if (path_map[new_dest].cost == path_map[dest].cost - path_map[dest].inc_cost) X vmap_mark_path (path_map, vmap, new_dest); X X} X X/* XCreate a marked path map. We mark those squares adjacent to the Xstarting location which are on the board. 'find_dir' must be Xinvoked to decide which squares are actually valid. X*/ X Xvoid Xvmap_mark_adjacent (path_map, loc) Xpath_map_t path_map[]; Xlong loc; X{ X int i; X long new_loc; X X FOR_ADJ_ON (loc, new_loc, i) X path_map[new_loc].terrain = T_PATH; X} X X/* XModify a marked path map. We mark those squares adjacent to the Xstarting location which are on the board and which are adjacent Xto a location on the existing shortest path. X*/ X Xvoid Xvmap_mark_near_path (path_map, loc) Xpath_map_t path_map[]; Xlong loc; X{ X int i, j; X long new_loc, xloc; X int hit_loc[8]; X X (void) bzero ((char *)hit_loc, sizeof(int)*8); X X FOR_ADJ_ON (loc, new_loc, i) { X FOR_ADJ_ON (new_loc, xloc, j) X if (xloc != loc && path_map[xloc].terrain == T_PATH) { X hit_loc[i] = 1; X break; X } X } X for (i = 0; i < 8; i++) X if (hit_loc[i]) X path_map[loc + dir_offset[i]].terrain = T_PATH; X} X X/* XLook at each neighbor of 'loc'. Select the first marked cell which Xis on a short path to the desired destination, and which holds a valid Xterrain. Note that while this terrain is matched against a 'vmap', Xit differs slightly from terrains used above. This terrain is the Xterrain to which we can move immediately, and does not include terrain Xfor which we would have to wait for another piece to move off of. X XWe prefer diagonal moves, and we try to have as many squares Xas possible containing something in 'adj_char'. X XFor tie-breaking, we prefer moving to cells that are adjacent to Xas many other squares on the path. This should have a few benefits: X X1) Fighters are less likely to be blocked from reaching a city Xbecause they stay in the center of the path and increase the number Xof options for subsequent moves. X X2) Transports will approach a city so that as many armies Xas possible can hop off the tt on one turn to take a valid Xpath toward the city. X X3) User pieces will move more intuitively by staying in the Xcenter of the best path. X*/ X Xstatic int order[] = {NORTHWEST, NORTHEAST, SOUTHWEST, SOUTHEAST, X WEST, EAST, NORTH, SOUTH}; X Xlong Xvmap_find_dir (path_map, vmap, loc, terrain, adj_char) Xpath_map_t path_map[]; Xview_map_t *vmap; Xlong loc; Xchar *terrain; Xchar *adj_char; X{ X int i, count, bestcount; X long bestloc, new_loc; X int path_count, bestpath; X char *p; X X if (trace_pmap) X print_pzoom ("Before vmap_find_dir:", path_map, vmap); X X bestcount = -INFINITY; /* no best yet */ X bestpath = -1; X bestloc = loc; X X for (i = 0; i < 8; i++) { /* for each adjacent square */ X new_loc = loc + dir_offset[order[i]]; X if (path_map[new_loc].terrain == T_PATH) { /* which is on path */ X p = strchr (terrain, vmap[new_loc].contents); X X if (p != NULL) { /* desirable square? */ X count = vmap_count_adjacent (vmap, new_loc, adj_char); X path_count = vmap_count_path (path_map, new_loc); X X /* remember best location */ X if (count > bestcount X || count == bestcount && path_count > bestpath) { X bestcount = count; X bestpath = path_count; X bestloc = new_loc; X } X } X } X } X return (bestloc); X} X X/* XCount the number of adjacent squares of interest. XSquares are weighted so that the first in the list Xis the most interesting. X*/ X Xint Xvmap_count_adjacent (vmap, loc, adj_char) Xview_map_t *vmap; Xlong loc; Xchar *adj_char; X{ X int i, count; X long new_loc; X char *p; X int len; X X len = strlen (adj_char); X X count = 0; X X FOR_ADJ_ON (loc, new_loc, i) { X p = strchr (adj_char, vmap[new_loc].contents); X if (p) count += 8 * (len - (p - adj_char)); X } X return (count); X} X X/* XCount the number of adjacent cells that are on the path. X*/ X Xstatic int Xvmap_count_path (pmap, loc) Xpath_map_t *pmap; Xlong loc; X{ X int i, count; X long new_loc; X X count = 0; X X FOR_ADJ_ON (loc, new_loc, i) X if (pmap[new_loc].terrain == T_PATH) X count += 1; X X return (count); X} X X/* XSee if a location is on the shore. We return true if a surrounding Xcell contains water and is on the board. X*/ X Xint Xrmap_shore (loc) Xlong loc; X{ X long i, j; X X FOR_ADJ_ON (loc, j, i) X if (map[j].contents == '.') return (TRUE); X X return (FALSE); X} X Xint Xvmap_shore (vmap, loc) Xview_map_t *vmap; Xlong loc; X{ X long i, j; X X FOR_ADJ_ON (loc, j, i) X if (vmap[j].contents != ' ' && vmap[j].contents != '+' && map[j].contents == '.') X return (TRUE); X X return (FALSE); X} X X/* XReturn true if a location is surrounded by ocean. Off board locations Xwhich cannot be moved to are treated as ocean. X*/ X Xint Xvmap_at_sea (vmap, loc) Xview_map_t *vmap; Xlong loc; X{ X long i, j; X X FOR_ADJ_ON (loc, j, i) X if (vmap[j].contents == ' ' || vmap[j].contents == '+' || map[j].contents != '.') X return (FALSE); X X return (TRUE); X} X Xint Xrmap_at_sea (loc) Xlong loc; X{ X long i, j; X X FOR_ADJ_ON (loc, j, i) { X if (map[j].contents != '.') return (FALSE); X } X return (TRUE); X} X //E*O*F map.c// echo x - object.c sed -e 's/^X//' > "object.c" << '//E*O*F object.c//' X/* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */ X X/* X * Copyright (C) 1987, 1988 Chuck Simmons X * X * See the file COPYING, distributed with empire, for restriction X * and warranty information. X */ X X/* Xobject.c -- routines for manipulating objects. X*/ X X#ifdef SYSV X#include X#else X#include X#endif X X#include X#include "empire.h" X#include "extern.h" X X/* XFind the nearest city to a location. Return the location Xof the city and the estimated cost to reach the city. XDistances are computed as straight-line distances. X*/ X Xint Xfind_nearest_city (loc, owner, city_loc) Xlong loc; Xint owner; Xlong *city_loc; X{ X long best_dist, best_loc; X long new_dist, i; X X best_dist = INFINITY; X best_loc = loc; X X for (i = 0; i < NUM_CITY; i++) X if (city[i].owner == owner) { X new_dist = dist (loc, city[i].loc); X if (new_dist < best_dist) { X best_dist = new_dist; X best_loc = city[i].loc; X } X } X *city_loc = best_loc; X return best_dist; X} X X/* XGiven the location of a city, return the index of that city. X*/ X Xcity_info_t *find_city (loc) Xlong loc; X{ X return (map[loc].cityp); X} X X/* XReturn the number of moves an object gets to make. This amount Xis based on the damage the object has suffered and the number of Xmoves it normally gets to make. The object always gets to make Xat least one move, assuming it is not dead. Damaged objects move Xat a fraction of their normal speed. An object which has lost Xhalf of its hits moves at half-speed, for example. X*/ X Xint obj_moves (obj) Xpiece_info_t *obj; X{ X return (piece_attr[obj->type].speed * obj->hits X + piece_attr[obj->type].max_hits - 1) /* round up */ X / piece_attr[obj->type].max_hits; X} X X/* XFigure out the capacity for an object. X*/ X Xint obj_capacity (obj) Xpiece_info_t *obj; X{ X return (piece_attr[obj->type].capacity * obj->hits X + piece_attr[obj->type].max_hits - 1) /* round up */ X / piece_attr[obj->type].max_hits; X} X X/* XSearch for an object of a given type at a location. We scan the Xlist of objects at the given location for one of the given type. X*/ X Xpiece_info_t *find_obj (type, loc) Xint type; Xlong loc; X{ X piece_info_t *p; X X for (p = map[loc].objp; p != NULL; p = p->loc_link.next) X if (p->type == type) return (p); X X return (NULL); X} X X/* XFind a non-full item of the appropriate type at the given location. X*/ X Xpiece_info_t *find_nfull (type, loc) Xint type; Xlong loc; X{ X piece_info_t *p; X X for (p = map[loc].objp; p != NULL; p = p->loc_link.next) X if (p->type == type) { X if (obj_capacity (p) > p->count) return (p); X } X return (NULL); X} X X/* XLook around a location for an unfull transport. Return the location Xof the transport if there is one. X*/ X Xlong Xfind_transport (owner, loc) Xint owner; Xlong loc; X{ X int i; X long new_loc; X piece_info_t *t; X X for (i = 0; i < 8; i++) { /* look around */ X new_loc = loc + dir_offset[i]; X t = find_nfull (TRANSPORT, new_loc); X if (t != NULL && t->owner == owner) return (new_loc); X } X return (loc); /* no tt found */ X} X X/* XSearch a list of objects at a location for any kind of object. XWe prefer transports and carriers to other objects. X*/ X Xpiece_info_t * Xfind_obj_at_loc (loc) Xlong loc; X{ X piece_info_t *p, *best; X X best = map[loc].objp; X if (best == NULL) return (NULL); /* nothing here */ X X for (p = best->loc_link.next; p != NULL; p = p->loc_link.next) X if (p->type > best->type && p->type != SATELLITE) X best = p; X X return (best); X} X X/* XIf an object is on a ship, remove it from that ship. X*/ X Xvoid disembark (obj) Xpiece_info_t *obj; X{ X if (obj->ship) { X UNLINK (obj->ship->cargo, obj, cargo_link); X obj->ship->count -= 1; X obj->ship = NULL; X } X} X X/* XMove an object onto a ship. X*/ X Xvoid embark (ship, obj) Xpiece_info_t *ship, *obj; X{ X obj->ship = ship; X LINK (ship->cargo, obj, cargo_link); X ship->count += 1; X} X X/* XKill an object. We scan around the piece and free it. If there is Xanything in the object, it is killed as well. X*/ X Xvoid kill_obj (obj, loc) Xpiece_info_t *obj; Xlong loc; X{ X void kill_one(); X X piece_info_t **list; X view_map_t *vmap; X X vmap = MAP(obj->owner); X list = LIST(obj->owner); X X while (obj->cargo != NULL) /* kill contents */ X kill_one (list, obj->cargo); X X kill_one (list, obj); X scan (vmap, loc); /* scan around new location */ X} X X/* kill an object without scanning */ X Xvoid kill_one (list, obj) Xpiece_info_t **list; Xpiece_info_t *obj; X{ X UNLINK (list[obj->type], obj, piece_link); /* unlink obj from all lists */ X UNLINK (map[obj->loc].objp, obj, loc_link); X disembark (obj); X X LINK (free_list, obj, piece_link); /* return object to free list */ X obj->hits = 0; /* let all know this object is dead */ X obj->moved = piece_attr[obj->type].speed; /* object has moved */ X} X X/* XKill a city. We kill off all objects in the city and set its type Xto unowned. We scan around the city's location. X*/ X Xvoid kill_city (cityp) Xcity_info_t *cityp; X{ X view_map_t *vmap; X piece_info_t *p; X piece_info_t *next_p; X piece_info_t **list; X int i; X X /* change ownership of hardware at this location; but not satellites */ X for (p = map[cityp->loc].objp; p; p = next_p) { X next_p = p->loc_link.next; X X if (p->type == ARMY) kill_obj (p, cityp->loc); X else if (p->type != SATELLITE) { X if (p->type == TRANSPORT) { X list = LIST(p->owner); X X while (p->cargo != NULL) /* kill contents */ X kill_one (list, p->cargo); X } X list = LIST (p->owner); X UNLINK (list[p->type], p, piece_link); X p->owner = (p->owner == USER ? COMP : USER); X list = LIST (p->owner); X LINK (list[p->type], p, piece_link); X X p->func = NOFUNC; X } X } X X if (cityp->owner != UNOWNED) { X vmap = MAP(cityp->owner); X cityp->owner = UNOWNED; X cityp->work = 0; X cityp->prod = NOPIECE; X X for (i = 0; i < NUM_OBJECTS; i++) X cityp->func[i] = NOFUNC; X X scan (vmap, cityp->loc); X } X} X X/* XProduce an item for a city. X*/ X Xstatic int sat_dir[4] = {MOVE_NW, MOVE_SW, MOVE_NE, MOVE_SE}; X Xvoid Xproduce (cityp) Xcity_info_t *cityp; X{ X piece_info_t **list; X piece_info_t *new; X X list = LIST (cityp->owner); X X cityp->work -= piece_attr[cityp->prod].build_time; X X ASSERT (free_list); /* can we allocate? */ X new = free_list; X UNLINK (free_list, new, piece_link); X LINK (list[cityp->prod], new, piece_link); X LINK (map[cityp->loc].objp, new, loc_link); X new->cargo_link.next = NULL; X new->cargo_link.prev = NULL; X X new->loc = cityp->loc; X new->func = NOFUNC; X new->hits = piece_attr[cityp->prod].max_hits; X new->owner = cityp->owner; X new->type = cityp->prod; X new->moved = 0; X new->cargo = NULL; X new->ship = NULL; X new->count = 0; X new->range = piece_attr[cityp->prod].range; X X if (new->type == SATELLITE) { /* set random move direction */ X new->func = sat_dir[irand (4)]; X } X} X X/* XMove an object to a location. We mark the object moved, we move Xthe object to the new square, and we scan around the object. XWe also do lots of little maintenance like updating the range Xof an object, keeping track of the number of pieces on a boat, Xetc. X*/ X Xvoid move_obj (obj, new_loc) Xpiece_info_t *obj; Xlong new_loc; X{ X view_map_t *vmap; X long old_loc; X piece_info_t *p; X X ASSERT (obj->hits); X vmap = MAP(obj->owner); X X old_loc = obj->loc; /* save original location */ X obj->moved += 1; X obj->loc = new_loc; X obj->range--; X X disembark (obj); /* remove object from any ship */ X X UNLINK (map[old_loc].objp, obj, loc_link); X LINK (map[new_loc].objp, obj, loc_link); X X /* move any objects contained in object */ X for (p = obj->cargo; p != NULL; p = p->cargo_link.next) { X p->loc = new_loc; X UNLINK (map[old_loc].objp, p, loc_link); X LINK (map[new_loc].objp, p, loc_link); X } X X switch (obj->type) { /* board new ship */ X case FIGHTER: X if (map[obj->loc].cityp == NULL) { /* not in a city? */ X p = find_nfull (CARRIER, obj->loc); X if (p != NULL) embark (p, obj); X } X break; X X case ARMY: X p = find_nfull (TRANSPORT, obj->loc); X if (p != NULL) embark (p, obj); X break; X } X X if (obj->type == SATELLITE) X scan_sat (vmap, obj->loc); X scan (vmap, obj->loc); X} X X/* XMove a satellite. It moves according to the preset direction. XSatellites bounce off the edge of the board. X XWe start off with some preliminary routines. X*/ X X/* Return next direction for a sattellite to travel. */ X Xstatic long Xbounce (loc, dir1, dir2, dir3) Xlong loc, dir1, dir2, dir3; X{ X int new_loc; X X new_loc = loc + dir_offset[MOVE_DIR (dir1)]; X if (map[new_loc].on_board) return dir1; X X new_loc = loc + dir_offset[MOVE_DIR (dir2)]; X if (map[new_loc].on_board) return dir2; X X return dir3; X} X X/* Move a satellite one square. */ X Xstatic void Xmove_sat1 (obj) Xpiece_info_t *obj; X{ X int dir; X long new_loc; X X dir = MOVE_DIR(obj->func); X new_loc = obj->loc + dir_offset[dir]; X X if (!map[new_loc].on_board) { X switch (obj->func) { X case MOVE_NE: X obj->func = bounce (obj->loc, MOVE_NW, MOVE_SE, MOVE_SW); X break; X case MOVE_NW: X obj->func = bounce (obj->loc, MOVE_NE, MOVE_SW, MOVE_SE); X break; X case MOVE_SE: X obj->func = bounce (obj->loc, MOVE_SW, MOVE_NE, MOVE_NW); X break; X case MOVE_SW: X obj->func = bounce (obj->loc, MOVE_SE, MOVE_NW, MOVE_NE); X break; X default: ABORT; X } X dir = MOVE_DIR(obj->func); X new_loc = obj->loc + dir_offset[dir]; X } X move_obj (obj, new_loc); X} X X/* XNow move the satellite all of its squares. XSatellite burns iff it's range reaches zero. X*/ X Xvoid Xmove_sat (obj) Xpiece_info_t *obj; X{ X obj->moved = 0; X X while (obj->moved < obj_moves (obj)) { X move_sat1 (obj); X if (obj->range == 0) { X if (obj->owner == USER) X comment ("Satellite at %d crashed and burned.", X obj->loc); X kill_obj (obj, obj->loc); X } X } X} X X/* XReturn true if a piece can move to a specified location. XWe are passed the object and the location. The location Xmust be on the board, and the player's view map must have an appropriate Xterrain type for the location. Boats may move into port, armies may Xmove onto transports, and fighters may move onto cities or carriers. X*/ X Xint good_loc (obj, loc) Xpiece_info_t *obj; Xlong loc; X{ X view_map_t *vmap; X piece_info_t *p; X X if (!map[loc].on_board) return (FALSE); X X vmap = MAP (obj->owner); X X if (strchr (piece_attr[obj->type].terrain, vmap[loc].contents) != NULL) X return (TRUE); X X /* armies can move into unfull transports */ X if (obj->type == ARMY) { X p = find_nfull (TRANSPORT, loc); X return (p != NULL && p->owner == obj->owner); X } X X /* ships and fighters can move into cities */ X if (map[loc].cityp && map[loc].cityp->owner == obj->owner) X return (TRUE); X X /* fighters can move onto unfull carriers */ X if (obj->type == FIGHTER) { X p = find_nfull (CARRIER, loc); X return (p != NULL && p->owner == obj->owner); X } X X return (FALSE); X} X Xvoid describe_obj (obj) Xpiece_info_t *obj; X{ X char func[STRSIZE]; X char other[STRSIZE]; X X if (obj->func >= 0) (void) sprintf (func, "%d", obj->func); X else (void) sprintf (func, func_name[FUNCI(obj->func)]); X X other[0] = 0; X X switch (obj->type) { /* set other information */ X case FIGHTER: X (void) sprintf (other,"; range = %d",obj->range); X break; X X case TRANSPORT: X (void) sprintf (other,"; armies = %d",obj->count); X break; X X case CARRIER: X (void) sprintf (other,"; fighters = %d",obj->count); X break; X } X X prompt ("%s at %d: moves = %d; hits = %d; func = %s%s", X piece_attr[obj->type].name, X obj->loc, X obj_moves (obj) - obj->moved, X obj->hits, X func, X other); X} X X/* XScan around a location to update a player's view of the world. For each Xsurrounding cell, we remember the date the cell was examined, and the Xcontents of the cell. Notice how we carefully update the cell to first Xreflect land, water, or city, then army or fighter, then boat, and finally Xcity owner. This guarantees that the object we want to display will appear Xon top. X*/ X Xvoid Xscan (vmap, loc) Xview_map_t vmap[]; Xlong loc; X{ X void update(), check(); X X int i; X long xloc; X X#ifdef DEBUG X check (); /* perform a consistency check */ X#endif X ASSERT (map[loc].on_board); /* passed loc must be on board */ X X for (i = 0; i < 8; i++) { /* for each surrounding cell */ X xloc = loc + dir_offset[i]; X update (vmap, xloc); X } X update (vmap, loc); /* update current location as well */ X} X X/* XScan a portion of the board for a satellite. X*/ X Xvoid Xscan_sat (vmap, loc) Xview_map_t *vmap; Xlong loc; X{ X int i; X long xloc; X X ASSERT (map[loc].on_board); X X for (i = 0; i < 8; i++) { /* for each surrounding cell */ X xloc = loc + 2 * dir_offset[i]; X if (xloc >= 0 && xloc < MAP_SIZE && map[xloc].on_board) X scan (vmap, xloc); X } X scan (vmap, loc); X} X X/* XUpdate a location. We set the date seen, the land type, object Xcontents starting with armies, then fighters, then boats, and the Xcity type. X*/ X Xchar city_char[] = {'*', 'O', 'X'}; X Xvoid Xupdate (vmap, loc) Xview_map_t vmap[]; Xlong loc; X{ X piece_info_t *p; X X vmap[loc].seen = date; X X if (map[loc].cityp) /* is there a city here? */ X vmap[loc].contents = city_char[map[loc].cityp->owner]; X X else { X p = find_obj_at_loc (loc); X X if (p == NULL) /* nothing here? */ X vmap[loc].contents = map[loc].contents; X else if (p->owner == USER) X vmap[loc].contents = piece_attr[p->type].sname; X else vmap[loc].contents = tolower (piece_attr[p->type].sname); X } X if (vmap == comp_map) X display_locx (COMP, comp_map, loc); X else if (vmap == user_map) X display_locx (USER, user_map, loc); X} X X/* XSet the production for a city. We make sure the city is displayed Xon the screen, and we ask the user for the new production. We keep Xasking until we get a valid answer. X*/ X Xvoid Xset_prod (cityp) Xcity_info_t *cityp; X{ X int i; X X scan (user_map, cityp->loc); X display_loc_u (cityp->loc); X X for (;;) { X prompt ("What do you want the city at %d to produce? ", X cityp->loc); X X i = get_piece_name (); X X if (i == NOPIECE) X error ("I don't know how to build those."); X X else { X cityp->prod = i; X city->work = -(piece_attr[i].build_time / 5); X return; X } X } X} X X/* Get the name of a type of object. */ X Xint Xget_piece_name () X{ X char c; X int i; X X c = get_chx (); /* get the answer */ X X for (i = 0; i < NUM_OBJECTS; i++) X if (piece_attr[i].sname == c) { X return i; X } X return NOPIECE; X} //E*O*F object.c// echo x - main.c sed -e 's/^X//' > "main.c" << '//E*O*F main.c//' X/* %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */ X X/* X * Copyright (C) 1987, 1988 Chuck Simmons X * X * See the file COPYING, distributed with empire, for restriction X * and warranty information. X */ X X/* Xmain.c -- parse command line for empire X Xoptions: X X -w water: percentage of map that is water. Must be in the range X 10..90. Default is 70. X X -s smooth: amount of smoothing performed to generate map. Must X be a nonnegative integer. Default is 5. X X -d delay: number of milliseconds to delay between output. X default is 2000 (2 seconds). X*/ X X#include X#include "empire.h" X#include "extern.h" X X#define OPTFLAGS "w:s:d:" X Xmain (argc, argv) Xint argc; Xchar *argv[]; X{ X int c; X extern char *optarg; X extern int optind; X extern int opterr; /* set to 1 to suppress error msg */ X int errflg = 0; X int wflg, sflg, dflg; X int land; X X wflg = 70; /* set defaults */ X sflg = 5; X dflg = 2000; X X /* X * extract command line options X */ X X while ((c = getopt (argc, argv, OPTFLAGS)) != EOF) { X switch (c) { X case 'w': X wflg = atoi (optarg); X break; X case 's': X sflg = atoi (optarg); X break; X case 'd': X dflg = atoi (optarg); X break; X case '?': /* illegal option? */ X errflg++; X break; X } X } X if (errflg || (argc-optind) != 0) { X (void) printf ("empire: usage: empire [-w water] [-s smooth] [-d delay]\n"); X exit (1); X } X X if (wflg < 10 || wflg > 90) { X (void) printf ("empire: -w argument must be in the range 0..90.\n"); X exit (1); X } X if (sflg < 0) { X (void) printf ("empire: -s argument must be greater or equal to zero.\n"); X exit (1); X } X X if (dflg < 0 || dflg > 30000) { X (void) printf ("empire: -d argument must be in the range 0..30000.\n"); X exit (1); X } X X SMOOTH = sflg; X WATER_RATIO = wflg; X delay_time = dflg; X X /* compute min distance between cities */ X land = MAP_SIZE * (100 - WATER_RATIO) / 100; /* available land */ X land /= NUM_CITY; /* land per city */ X MIN_CITY_DIST = sqrt (land); /* distance between cities */ X X empire (); /* call main routine */ X return (0); X} //E*O*F main.c// echo shar: End of archive 1 \(of 6\). cp /dev/null ark1isdone MISSING="" for I in 1 2 3 4 5 6 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 6 archives. rm -f ark[1-9]*isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0