empire sources (part 1 of 6)

Charles Simmons chuck at amdahl.uts.amdahl.com
Mon Apr 11 10:59:20 AEST 1988



# 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 <string.h>
X#else
X#include <strings.h>
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 <string.h>
X#else
X#include <strings.h>
X#endif
X
X#include <ctype.h>
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 <stdio.h>
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



More information about the Alt.sources mailing list