[alt.sources] empire sources

chuck@amdahl.uts.amdahl.com (Charles Simmons) (04/11/88)

Following this cover letter there are six shar archives containing
C/Unix sources for a game whose concept is based on the version of
Empire written in Fortran that runs on VMS operating systems.
(Do not confuse this with the entirely different game called "empire"
that already runs on Unix systems.)

The sources have been compiled on Sys V systems and
Sun systems.  Apparently Ultrix systems do not have a function
named "usleep" that exists on Sun systems, so there are minor
porting hacks that need to be made for Ultrix systems.

Be forewarned that the game is slow if you have anything less
than a 20 VAX-MIPS processor at your disposal.  The game is
even slower if you compile it with the -DDEBUG flag or if you
do not have a local hard disk.  Hopefully, someone out there
will fix these problems and repost the sources.

It is my belief that this game is useable and playable as is.  However,
I have a list of enhancements (enclosed) that I would like to see someone
make to the game.  Please let me know of any problems you encounter
(including porting difficulties that you had to overcome).  I will
also be interested in diffs of changes that you make to improve the
program.

			Happy Hacking,
				Chuck

amdahl!chuck

chuck@amdahl.uts.amdahl.com (Charles Simmons) (04/11/88)

# 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

chuck@amdahl.uts.amdahl.com (Charles Simmons) (04/11/88)

# 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:
# compmove.c edit.c empire.c

echo x - compmove.c
sed -e 's/^X//' > "compmove.c" << '//E*O*F compmove.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/*
Xcompmove.c -- Make a move for the computer. 
X
XFor each move the user wants us to make, we do the following:
X
X    1)  Handle city production;
X    2)  Move computer's pieces;
X    3)  Check to see if the game is over.
X*/
X
X#ifdef SYSV
X#include <string.h>
X#else
X#include <strings.h>
X#endif
X
X#include <curses.h>
X#include "empire.h"
X#include "extern.h"
X
Xstatic view_map_t emap[MAP_SIZE]; /* pruned explore map */
X
Xvoid
Xcomp_move (nmoves) 
Xint nmoves;
X{
X	void do_cities(), do_pieces(), check_endgame();
X
X	int i;
X	piece_info_t *obj;
X
X	/* Update our view of the world. */
X	
X	for (i = 0; i < NUM_OBJECTS; i++)
X	for (obj = comp_obj[i]; obj != NULL; obj = obj->piece_link.next)
X		scan (comp_map, obj->loc); /* refresh comp's view of world */
X
X	for (i = 1; i <= nmoves; i++) { /* for each move we get... */
X		comment ("Thinking...");
X
X		(void) memcpy (emap, comp_map, MAP_SIZE * sizeof (view_map_t));
X		vmap_prune_explore_locs (emap);
X	
X		do_cities (); /* handle city production */
X		do_pieces (); /* move pieces */
X		
X		if (save_movie) save_movie_screen ();
X		check_endgame (); /* see if game is over */
X
X		topini ();
X		(void) refresh ();
X	}
X}
X
X/*
XHandle city production.  First, we set production for new cities.
XThen we produce new pieces.  After producing a piece, we will see
Xif we should change our production.
X
XOur goals for city production are first, not to change production
Xwhile something is in the process of being built.  Second, we attempt
Xto have enough city producing armies on a continent to counter any
Xthreat on the continent, and to adequately explore and control the
Xcontinent.  Third, we attempt to always have at least one transport
Xproducer.  Fourth, we attempt to maintain a good ratio between the
Xnumber of producers we have of each type of piece.  Fifth, we never
Xbuild carriers, as we don't have a good strategy for moving these.
X*/
X
Xvoid
Xdo_cities () {
X	void comp_prod();
X	
X	int i;
X	int is_lake;
X
X	for (i = 0; i < NUM_CITY; i++) /* new production */
X	if (city[i].owner == COMP) {
X		scan (comp_map, city[i].loc);
X
X		if (city[i].prod == NOPIECE)
X			comp_prod (&city[i], lake (city[i].loc));
X	}
X	for (i = 0; i < NUM_CITY; i++) /* produce and change */
X	if (city[i].owner == COMP) {
X		is_lake = lake (city[i].loc);
X		if (city[i].work++ >= (long)piece_attr[city[i].prod].build_time) {
X			produce (&city[i]);
X			comp_prod (&city[i], is_lake);
X		}
X		/* don't produce ships in lakes */
X		else if (city[i].prod > FIGHTER && city[i].prod != SATELLITE && is_lake)
X			comp_prod (&city[i], is_lake);
X	}
X}
X			
X/*
XDefine ratios for numbers of cities we want producing each object.
X
XEarly in the game, we want to produce armies and transports for
Xrapid expansion.  After a while, we add fighters and pt boats
Xfor exploration.  Later, we add ships of all kinds for control of
Xthe sea.
X*/
X                                 /* A    F    P    S    D    T    C    B   Z*/
Xstatic int ratio1[NUM_OBJECTS] = { 60,   0,  10,   0,   0,  20,   0,   0,  0};
Xstatic int ratio2[NUM_OBJECTS] = { 90,  10,  10,  10,  10,  40,   0,   0,  0};
Xstatic int ratio3[NUM_OBJECTS] = {120,  20,  20,  10,  10,  60,  10,  10,  0};
Xstatic int ratio4[NUM_OBJECTS] = {150,  30,  30,  20,  20,  70,  10,  10,  0};
Xstatic int *ratio;
X
X/*
XSet city production if necessary.
X
XThe algorithm below contains three parts:
X
X1)  Defend continents we own.
X
X2)  Produce a TT and a Satellite.
X
X3)  Meet the ratio requirements given above.
X*/
X
Xvoid
Xcomp_prod (cityp, is_lake)
Xcity_info_t *cityp;
Xint is_lake;
X{
X	void comp_set_prod(), comp_set_needed();
X	
X	int city_count[NUM_OBJECTS]; /* # of cities producing each piece */
X	int cont_map[MAP_SIZE];
X	int total_cities;
X	long i;
X	int comp_ac;
X	city_info_t *p;
X	int need_count, interest;
X	scan_counts_t counts;
X
X	/* Make sure we have army producers for current continent. */
X	
X	/* map out city's continent */
X	vmap_cont (cont_map, comp_map, cityp->loc, '.');
X
X	/* count items of interest on the continent */
X	counts = vmap_cont_scan (cont_map, comp_map);
X	comp_ac = 0; /* no army producing computer cities */
X	
X	for (i = 0; i < MAP_SIZE; i++)
X	if (cont_map[i]) { /* for each cell of continent */
X		if (comp_map[i].contents == 'X') {
X			p = find_city (i);
X			ASSERT (p != NULL && p->owner == COMP);
X			if (p->prod == ARMY) comp_ac += 1;
X		}
X	}
X	/* see if anything of interest is on continent */
X	interest = (counts.unexplored || counts.user_cities
X		 || counts.user_objects[ARMY]
X		 || counts.unowned_cities);
X	
X	/* we want one more army producer than enemy has cities */
X	/* and one more if anything of interest on cont */
X	need_count = counts.user_cities - comp_ac + interest;
X	if (counts.user_cities) need_count += 1;
X	
X	if (need_count > 0) { /* need an army producer? */
X		comp_set_prod (cityp, ARMY);
X		return;
X	}
X	
X	/* Produce armies in new cities if there is a city to attack. */
X	if (counts.user_cities && cityp->prod == NOPIECE) {
X		comp_set_prod (cityp, ARMY);
X		return;
X	}
X
X	/* Produce a TT and SAT if we don't have one. */
X	
X	/* count # of cities producing each piece */
X	for (i = 0; i < NUM_OBJECTS; i++)
X		city_count[i] = 0;
X		
X	total_cities = 0;
X		
X	for (i = 0; i < NUM_CITY; i++)
X	if (city[i].owner == COMP && city[i].prod != NOPIECE) {
X		city_count[city[i].prod] += 1;
X		total_cities += 1;
X	}
X	if (total_cities <= 10) ratio = ratio1;
X	else if (total_cities <= 20) ratio = ratio2;
X	else if (total_cities <= 30) ratio = ratio3;
X	else ratio = ratio4;
X		
X	/* if we have one army producer, and this is it, return */
X	if (city_count[ARMY] == 1 && cityp->prod == ARMY) return;
X	
X	/* first available non-lake becomes a tt producer */
X	if (city_count[TRANSPORT] == 0) {
X		if (!is_lake) {
X			comp_set_prod (cityp, TRANSPORT);
X			return;
X		}
X		/* if we have one army producer that is not on a lake, */
X		/* produce armies here instead */
X		if (city_count[ARMY] == 1) {
X			for (i = 0; i < NUM_CITY; i++)
X			if (city[i].owner == COMP && city[i].prod == ARMY) break;
X		
X			if (!lake (city[i].loc)) {
X				comp_set_prod (cityp, ARMY);
X				return;
X			}
X		}
X	}
X#if 0
X	/* Now we need a SATELLITE. */
X	if (cityp->prod == NOPIECE && city_count[SATELLITE] == 0) {
X		comp_set_prod (cityp, SATELLITE);
X		return;
X	}
X	if (cityp->prod == SATELLITE) return;
X	/* "The satellites are out tonight." -- Lori Anderson */
X#endif
X	/* don't change prod from armies if something on continent */
X	if (cityp->prod == ARMY && interest) return;
X			
X	/* Produce armies in new cities if there is a city to attack. */
X	if (counts.unowned_cities && cityp->prod == NOPIECE) {
X		comp_set_prod (cityp, ARMY);
X		return;
X	}
X
X	/* Set production to item most needed.  Continents with one
X	city and nothing interesting may not produce armies.  We
X	set production for unset cities, and change production for
X	cities that produce objects for which we have many city producers.
X	Ship producers on lakes also get there production changed. */
X	
X	interest = (counts.comp_cities != 1 || interest);
X	
X	if (cityp->prod == NOPIECE
X	    || cityp->prod == ARMY && counts.comp_cities == 1
X	    || overproduced (cityp, city_count)
X	    || cityp->prod > FIGHTER && is_lake)
X		comp_set_needed (cityp, city_count, interest, is_lake);
X}
X	
X/*
XSet production for a computer city to a given type.  Don't
Xreset production if it is already correct.
X*/
X
Xvoid
Xcomp_set_prod (cityp, type)
Xcity_info_t *cityp;
Xint type;
X{
X	if (cityp->prod == type) return;
X	
X	pdebug ("Changing city prod at %d from %d to %d\n",
X		cityp->loc, cityp->prod, type);
X	
X	cityp->prod = type;
X	cityp->work = -(piece_attr[type].build_time / 5);
X}
X
X/*
XSee if a city is producing an object which is being overproduced.
X*/
X
Xint
Xoverproduced (cityp, city_count)
Xcity_info_t *cityp;
Xint *city_count;
X{
X	int i;
X
X	for (i = 0; i < NUM_OBJECTS; i++) {
X		/* return true if changing production would improve balance */
X		if (i != cityp->prod
X		 && ((city_count[cityp->prod] - 1) * ratio[i]
X		   > (city_count[i] + 1) * ratio[cityp->prod]))
X  		return (TRUE);
X	}
X	return (FALSE);
X}
X
X/*
XSee if one type of production is needed more than another type.
XReturn the most-needed type of production.
X*/
X
Xint
Xneed_more (city_count, prod1, prod2)
Xint *city_count;
Xint prod1, prod2;
X{
X	if (city_count[prod1] * ratio[prod2]
X		 <= city_count[prod2] * ratio[prod1])
X	return (prod1);
X
X	else return (prod2);
X}
X
X/*
XFigure out the most needed type of production.  We are passed
Xa flag telling us if armies are ok to produce.
X*/
X
Xvoid
Xcomp_set_needed (cityp, city_count, army_ok, is_lake)
Xcity_info_t *cityp;
Xint *city_count;
Xint army_ok;
Xint is_lake;
X{
X	int best_prod;
X	int prod;
X
X	if (!army_ok) city_count[ARMY] = INFINITY;
X
X	if (is_lake) { /* choose fighter or army */
X		comp_set_prod (cityp, need_more (city_count, ARMY, FIGHTER));
X		return;
X	}
X	/* don't choose fighter */
X	city_count[FIGHTER] = INFINITY;
X	
X	best_prod = ARMY; /* default */
X	for (prod = 0; prod < NUM_OBJECTS; prod++) {
X		best_prod = need_more (city_count, best_prod, prod);
X	}
X	comp_set_prod (cityp, best_prod);
X}
X
X/*
XSee if a city is on a lake.  We define a lake to be a body of
Xwater (where cities are considered to be water) that does not
Xtouch either an attackable city or unexplored territory.
X
XBe careful, because we use the 'emap'.  This predicts whether
Xunexplored territory will be land or water.  The prediction should
Xbe helpful, because small bodies of water that enclose unexplored
Xterritory will appear as solid water.  Big bodies of water should
Xhave unexplored territory on the edges.
X*/
X
Xint
Xlake (loc)
Xlong loc;
X{
X	int cont_map[MAP_SIZE];
X	scan_counts_t counts;
X
X	vmap_cont (cont_map, emap, loc, '+'); /* map lake */
X	counts = vmap_cont_scan (cont_map, emap);
X
X	return !(counts.unowned_cities || counts.user_cities || counts.unexplored);
X}
X
X/*
XMove all computer pieces.
X*/
X
Xstatic view_map_t amap[MAP_SIZE]; /* temp view map */
Xstatic path_map_t path_map[MAP_SIZE];
X
Xvoid
Xdo_pieces () { /* move pieces */
X	void cpiece_move();
X
X	int i;
X	piece_info_t *obj, *next_obj;
X
X	for (i = 0; i < NUM_OBJECTS; i++) { /* loop through obj lists */
X		for (obj = comp_obj[move_order[i]]; obj != NULL;
X		    obj = next_obj) { /* loop through objs in list */
X			next_obj = obj->piece_link.next;
X			cpiece_move (obj); /* yup; move the object */
X		}
X	}
X}
X
X/*
XMove a piece.  We loop until all the moves of a piece are made.  Within
Xthe loop, we find a direction to move that will take us closer to an
Xobjective.
X*/
X
Xvoid
Xcpiece_move (obj)
Xpiece_info_t *obj;
X{
X	void move1();
X
X	int changed_loc;
X	int max_hits;
X	long saved_loc;
X	city_info_t *cityp;
X
X	if (obj->type == SATELLITE) {
X		move_sat (obj);
X		return;
X	}
X	
X	obj->moved = 0; /* not moved yet */
X	changed_loc = FALSE; /* not changed yet */
X	max_hits = piece_attr[obj->type].max_hits;
X
X	if (obj->type == FIGHTER) { /* init fighter range */
X		cityp = find_city (obj->loc);
X		if (cityp != NULL) obj->range = piece_attr[FIGHTER].range;
X	}
X	
X	while (obj->moved < obj_moves (obj)) {
X		saved_loc = obj->loc; /* remember starting location */
X		move1 (obj);
X		if (saved_loc != obj->loc) changed_loc = TRUE;
X		
X		if (obj->type == FIGHTER && obj->hits > 0) {
X			if (comp_map[obj->loc].contents == 'X')
X				obj->moved = piece_attr[FIGHTER].speed;
X			else if (obj->range == 0) {
X				pdebug ("Fighter at %d crashed and burned\n", obj->loc);
X				kill_obj (obj, obj->loc); /* crash & burn */
X			}
X		}
X	}
X	/* if a boat is in port, damaged, and never moved, fix some damage */
X	if (obj->hits > 0 /* live piece? */
X		&& !changed_loc /* object never changed location? */
X		&& obj->type != ARMY && obj->type != FIGHTER /* it is a boat? */
X		&& obj->hits != max_hits /* it is damaged? */
X		&& comp_map[obj->loc].contents == 'X') /* it is in port? */
X	obj->hits++; /* fix some damage */
X}
X
X/*
XMove a piece one square.
X*/
X
Xvoid
Xmove1 (obj)
Xpiece_info_t *obj;
X{
X	void army_move(), transport_move(), fighter_move(), ship_move();
X
X	switch (obj->type) {
X	case ARMY: army_move (obj); break;
X	case TRANSPORT: transport_move (obj); break;
X	case FIGHTER: fighter_move (obj); break;
X	default: ship_move (obj); break;
X	}
X}
X
X/*
XMove an army.
X
XThis is a multi-step algorithm:
X
X1)  See if there is an object we can attack immediately.
XIf so, attack it.
X
X2)  Look for the nearest land objective.
X
X3)  If we find an objective reachable by land, figure out
Xhow far away that objective is.  Based on the found objective,
Xalso figure out how close a loadable tt must be to be of
Xinterest.  If the objective is closer than the tt must be,
Xhead towards the objective.
X
X4)  Otherwise, look for the nearest loading tt (or tt producing
Xcity).  If the nearest loading tt is farther than our land objective,
Xhead towards the land objective.
X
X5)  Otherwise, head for the tt.
X
X6)  If we still have no destination and we are in a city,
Xattempt to leave the city.
X
X7)  Once we have a destination, find the best move toward that
Xdestination.  (If there is no destination, sit around and wait.)
X*/
X
Xvoid
Xarmy_move (obj)
Xpiece_info_t *obj;
X{
X	long move_away();
X	void move_objective();
X	long find_attack();
X	void make_army_load_map(), make_unload_map(), make_tt_load_map();
X	void board_ship();
X	
X	long new_loc;
X	path_map_t path_map2[MAP_SIZE];
X	long new_loc2;
X	int cross_cost; /* cost to enter water */
X	
X	obj->func = 0; /* army doesn't want a tt */
X	if (vmap_at_sea (comp_map, obj->loc)) { /* army can't move? */
X		(void) load_army (obj);
X		obj->moved = piece_attr[ARMY].speed;
X		if (!obj->ship) obj->func = 1; /* load army on ship */
X		return;
X	}
X	if (obj->ship) /* is army on a transport? */
X		new_loc = find_attack (obj->loc, army_attack, "+*");
X	else new_loc = find_attack (obj->loc, army_attack, ".+*");
X		
X	if (new_loc != obj->loc) { /* something to attack? */
X		attack (obj, new_loc); /* attack it */
X		if (map[new_loc].contents == '.' /* moved to ocean? */
X		  && obj->hits > 0) { /* object still alive? */
X			kill_obj (obj, new_loc);
X			scan (user_map, new_loc); /* rescan for user */
X		}
X		return;
X	}
X	if (obj->ship) {
X		if (obj->ship->func == 0) {
X			if (!load_army (obj)) ABORT; /* load army on best ship */
X			return; /* armies stay on a loading ship */
X		}
X		make_unload_map (amap, comp_map);
X		new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_unload);
X		move_objective (obj, path_map, new_loc, " ");
X		return;
X	}
X
X	new_loc = vmap_find_lobj (path_map, comp_map, obj->loc, &army_fight);
X	
X	if (new_loc != obj->loc) { /* something interesting on land? */
X		switch (comp_map[new_loc].contents) {
X		case 'A':
X		case 'O':
X			cross_cost = 60; /* high cost if enemy present */
X			break;
X		case '*':
X			cross_cost = 30; /* medium cost for attackable city */
X			break;
X		case ' ':
X			cross_cost = 14; /* low cost for exploring */
X			break;
X		default:
X			ABORT;
X		}
X		cross_cost = path_map[new_loc].cost * 2 - cross_cost;
X	}
X	else cross_cost = INFINITY;
X	
X	if (new_loc == obj->loc || cross_cost > 0) {
X		/* see if there is something interesting to load */
X		make_army_load_map (obj, amap, comp_map);
X		new_loc2 = vmap_find_lwobj (path_map2, amap, obj->loc, &army_load, cross_cost);
X		
X		if (new_loc2 != obj->loc) { /* found something? */
X			board_ship (obj, path_map2, new_loc2);
X			return;
X		}
X	}
X
X	move_objective (obj, path_map, new_loc, " ");
X}
X
X/*
XRemove pruned explore locs from a view map.
X*/
X
Xvoid
Xunmark_explore_locs (xmap)
Xview_map_t *xmap;
X{
X	long i;
X
X	for (i = 0; i < MAP_SIZE; i++)
X	if (map[i].on_board && xmap[i].contents == ' ')
X		xmap[i].contents = emap[i].contents;
X}
X
X/*
XMake a load map.  We copy the view map and mark each loading
Xtransport and tt producing city with a '$'.
X*/
X
Xvoid
Xmake_army_load_map (obj, xmap, vmap)
Xpiece_info_t *obj;
Xview_map_t *xmap;
Xview_map_t *vmap;
X{
X	piece_info_t *p;
X	int i;
X	
X	(void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
X
X	/* mark loading transports or cities building transports */
X	for (p = comp_obj[TRANSPORT]; p; p = p->piece_link.next)
X	if (p->func == 0) /* loading tt? */
X	xmap[p->loc].contents = '$';
X	
X	for (i = 0; i < NUM_CITY; i++)
X	if (city[i].owner == COMP && city[i].prod == TRANSPORT) {
X		if (nearby_load (obj, city[i].loc))
X			xmap[city[i].loc].contents = 'x'; /* army is nearby so it can load */
X		else if (nearby_count (city[i].loc) < piece_attr[TRANSPORT].capacity)
X			xmap[city[i].loc].contents = 'x'; /* city needs armies */
X	}
X	
X	if (print_vmap == 'A') print_xzoom (xmap);
X}
X
X/* Return true if an army is considered near a location for loading. */
X
Xint
Xnearby_load (obj, loc)
Xpiece_info_t *obj;
Xlong loc;
X{
X	return obj->func == 1 && dist (obj->loc, loc) <= 2;
X}
X	
X/* Return number of nearby armies. */
X
Xint
Xnearby_count (loc)
Xlong loc;
X{
X	piece_info_t *obj;
X	int count;
X
X	count = 0;
X	for (obj = comp_obj[ARMY]; obj; obj = obj->piece_link.next) {
X		if (nearby_load (obj, loc)) count += 1;
X	}
X	return count;
X}
X
X/* Make load map for a ship. */
X
Xvoid
Xmake_tt_load_map (xmap, vmap)
Xview_map_t *xmap;
Xview_map_t *vmap;
X{
X	piece_info_t *p;
X	
X	(void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
X
X	/* mark loading armies */
X	for (p = comp_obj[ARMY]; p; p = p->piece_link.next)
X	if (p->func == 1) /* loading army? */
X	xmap[p->loc].contents = '$';
X	
X	if (print_vmap == 'L') print_xzoom (xmap);
X}
X	
X/*
XMake an unload map.  We copy the view map.  We then create
Xa continent map.  For each of our cities, we mark out the continent
Xthat city is on.  Then, for each city that we don't own and which
Xdoesn't appear on our continent map, we set that square to a digit.
X
XWe want to assign weights to each attackable city.
XCities are more valuable if they are on a continent which
Xhas lots of cities.  Cities are also valuable if either it
Xwill be easy for us to take over the continent, or if we
Xneed to defend that continent from an enemy.
X
XTo implement the above, we assign numbers to each city as follows:
X
Xa)  if unowned_cities > user_cities && comp_cities == 0
X       set number to min (total_cities, 9)
X
Xb)  if comp_cities != 0 && user_cities != 0
X       set number to min (total_cities, 9)
X       
Xb)  if enemy_cities == 1 && total_cities == 1, set number to 2.
X    (( taking the sole enemy city on a continent is as good as
X       getting a two city continent ))
X       
Xc)  Any other attackable city is marked with a '0'.
X*/
X
Xstatic int owncont_map[MAP_SIZE];
Xstatic int tcont_map[MAP_SIZE];
X
Xvoid
Xmake_unload_map (xmap, vmap)
Xview_map_t *xmap;
Xview_map_t *vmap;
X{
X	long i;
X	scan_counts_t counts;
X
X	(void) memcpy (xmap, vmap, sizeof (view_map_t) * MAP_SIZE);
X	unmark_explore_locs (xmap);
X	
X	for (i = 0; i < MAP_SIZE; i++)
X		owncont_map[i] = 0; /* nothing marked */
X
X	for (i = 0; i < NUM_CITY; i++)
X	if (city[i].owner == COMP)
X	vmap_mark_up_cont (owncont_map, xmap, city[i].loc, '.');
X
X	for (i = 0; i < MAP_SIZE; i++)
X	if (strchr ("O*", vmap[i].contents)) {
X		int total_cities;
X		
X		vmap_cont (tcont_map, xmap, i, '.'); /* map continent */
X		counts = vmap_cont_scan (tcont_map, xmap);
X		
X		total_cities = counts.unowned_cities
X			     + counts.user_cities
X			     + counts.comp_cities;
X			     
X		if (total_cities > 9) total_cities = 0;
X		
X		if (counts.user_cities && counts.comp_cities)
X			xmap[i].contents = '0' + total_cities;
X
X		else if (counts.unowned_cities > counts.user_cities
X			 && counts.comp_cities == 0)
X			xmap[i].contents = '0' + total_cities;
X
X		else if (counts.user_cities == 1 && counts.comp_cities == 0)
X			xmap[i].contents = '2';
X			
X		else xmap[i].contents = '0';
X	}
X	if (print_vmap == 'U') print_xzoom (xmap);
X}
X
X/*
XLoad an army onto a ship.  First look for an adjacent ship.
XIf that doesn't work, move to the objective, trying to be
Xclose to the ocean.
X*/
X
Xvoid
Xboard_ship (obj, pmap, dest)
Xpiece_info_t *obj;
Xpath_map_t *pmap;
Xlong dest;
X{
X	if (!load_army (obj)) {
X		obj->func = 1; /* loading */
X		move_objective (obj, pmap, dest, "t.");
X	}
X}
X
X/*
XLook for the most full, non-full transport at a location.
XPrefer switching to staying.  If we switch, we force
Xone of the ships to become more full.
X*/
X
Xpiece_info_t *
Xfind_best_tt (best, loc)
Xpiece_info_t *best;
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 == TRANSPORT && obj_capacity (p) > p->count) {
X		if (!best) best = p;
X		else if (p->count >= best->count) best = p;
X	}
X	return best;
X}
X
X/*
XLoad an army onto the most full non-full ship.
X*/
X
Xint
Xload_army (obj)
Xpiece_info_t *obj;
X{
X	piece_info_t *p;
X	int i;
X	long x_loc;
X
X	p = find_best_tt (obj->ship, obj->loc); /* look here first */
X
X	for (i = 0; i < 8; i++) { /* try surrounding squares */
X		x_loc = obj->loc + dir_offset[i];
X		if (map[x_loc].on_board)
X			p = find_best_tt (p, x_loc);
X
X	}
X	if (!p) return FALSE; /* no tt to be found */
X
X	if (p->loc == obj->loc) { /* stay in same place */
X		obj->moved = piece_attr[ARMY].speed;
X	}
X	else move_obj (obj, p->loc); /* move to square with ship */
X
X	if (p->ship != obj->ship) { /* reload army to new ship */
X		disembark (obj);
X		embark (p, obj);
X	}
X	return TRUE;
X}
X
X/*
XReturn the first location we find adjacent to the current location of
Xthe correct terrain.
X*/
X
Xlong
Xmove_away (vmap, loc, terrain)
Xview_map_t *vmap;
Xlong loc;
Xchar *terrain;
X{
X	long new_loc;
X	int i;
X
X	for (i = 0; i < 8; i++) {
X		new_loc = loc + dir_offset[i];
X		if (map[new_loc].on_board
X		 && strchr (terrain, vmap[new_loc].contents))
X			return (new_loc);
X	}
X	return (loc);
X}
X
X/*
XLook to see if there is an adjacent object to attack.  We are passed
Xa location and a list of items we attack sorted in order of most
Xvaluable first.  We look at each surrounding on board location.
XIf there is an object we can attack, we return the location of the
Xbest of these.
X*/
X
Xlong
Xfind_attack (loc, obj_list, terrain)
Xlong loc;
Xchar *obj_list;
Xchar *terrain;
X{
X	long new_loc, best_loc;
X	int i, best_val;
X	char *p;
X
X	best_loc = loc; /* nothing found yet */
X	best_val = INFINITY;
X	for (i = 0; i < 8; i++) {
X		new_loc = loc + dir_offset[i];
X
X		if (map[new_loc].on_board /* can we move here? */
X		    && strchr (terrain, map[new_loc].contents)) {
X			p = strchr (obj_list, comp_map[new_loc].contents);
X			if (p != NULL && p - obj_list < best_val) {
X				best_val = p - obj_list;
X				best_loc = new_loc;
X			}
X		}
X	}
X	return (best_loc);
X}
X
X/*
XMove a transport.
X
XThere are two kinds of transports:  loading and unloading.
X
XLoading transports move toward loading armies.  Unloading
Xtransports move toward attackable cities on unowned continents.
X
XAn empty transport is willing to attack adjacent enemy transports.
XTransports become 'loading' when empty, and 'unloading' when full.
X*/
X
Xvoid
Xtransport_move (obj)
Xpiece_info_t *obj;
X{
X	void tt_do_move();
X
X	long new_loc;
X
X	/* empty transports can attack */
X	if (obj->count == 0) { /* empty? */
X		obj->func = 0; /* transport is loading */
X		new_loc = find_attack (obj->loc, tt_attack, ".");
X		if (new_loc != obj->loc) { /* something to attack? */
X			attack (obj, new_loc); /* attack it */
X			return;
X		}
X	}
X
X	if (obj->count == obj_capacity (obj)) /* full? */
X		obj->func = 1; /* unloading */
X
X	if (obj->func == 0) { /* loading? */
X		make_tt_load_map (amap, comp_map);
X		new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_load);
X		
X		if (new_loc == obj->loc) { /* nothing to load? */
X			(void) memcpy (amap, comp_map, MAP_SIZE * sizeof (view_map_t));
X			unmark_explore_locs (amap);
X			if (print_vmap == 'S') print_xzoom (amap);
X			new_loc = vmap_find_wobj (path_map, amap, obj->loc, &tt_explore);
X		}
X		
X		move_objective (obj, path_map, new_loc, "a ");
X	}
X	else {
X		make_unload_map (amap, comp_map);
X		new_loc = vmap_find_wlobj (path_map, amap, obj->loc, &tt_unload);
X		move_objective (obj, path_map, new_loc, " ");
X	}
X}
X
X/*
XMove a fighter.
X
X1)  See if there is an object we can attack immediately.
XIf so, attack it.
X
X2)  Otherwise, if fighter is low on fuel, move toward nearest city
Xif there is one in range.
X
X3)  Otherwise, look for an objective.
X*/
X
Xvoid
Xfighter_move (obj)
Xpiece_info_t *obj;
X{
X	long new_loc;
X
X	new_loc = find_attack (obj->loc, fighter_attack, ".+");
X	if (new_loc != obj->loc) { /* something to attack? */
X		attack (obj, new_loc); /* attack it */
X		return;
X	}
X	/* return to base if low on fuel */
X	if (obj->range <= find_nearest_city (obj->loc, COMP, &new_loc) + 2) {
X		if (new_loc != obj->loc)
X			new_loc = vmap_find_dest (path_map, comp_map, obj->loc,
X						  new_loc, COMP, T_AIR);
X	}
X	else new_loc = obj->loc;
X	
X	if (new_loc == obj->loc) { /* no nearby city? */
X		new_loc = vmap_find_aobj (path_map, comp_map, obj->loc,
X					       &fighter_fight);
X	}
X	move_objective (obj, path_map, new_loc, " ");
X}
X
X/*
XMove a ship.
X
XAttack anything adjacent.  If nothing adjacent, explore or look for
Xsomething to attack.
X*/
X
Xvoid
Xship_move (obj)
Xpiece_info_t *obj;
X{
X	long new_loc;
X	char *adj_list;
X
X	if (obj->hits < piece_attr[obj->type].max_hits) { /* head to port */
X		if (comp_map[obj->loc].contents == 'X') { /* stay in port */
X			obj->moved = piece_attr[obj->type].speed;
X			return;
X		}
X		new_loc = vmap_find_wobj (path_map, comp_map, obj->loc,
X					       &ship_repair);
X		adj_list = ".";
X
X	}
X	else {
X		new_loc = find_attack (obj->loc, ship_attack, ".");
X		if (new_loc != obj->loc) { /* something to attack? */
X			attack (obj, new_loc); /* attack it */
X			return;
X		}
X		/* look for an objective */
X		(void) memcpy (amap, comp_map, MAP_SIZE * sizeof (view_map_t));
X		unmark_explore_locs (amap);
X		if (print_vmap == 'S') print_xzoom (amap);
X		
X		new_loc = vmap_find_wobj (path_map, amap, obj->loc,
X					       &ship_fight);
X		adj_list = ship_fight.objectives;
X	}
X
X	move_objective (obj, path_map, new_loc, adj_list);
X}
X
X/*
XMove to an objective.
X*/
X
Xvoid
Xmove_objective (obj, pathmap, new_loc, adj_list)
Xpiece_info_t *obj;
Xpath_map_t pathmap[];
Xlong new_loc;
Xchar *adj_list;
X{
X	char *terrain;
X	char *attack_list;
X	int d;
X	int reuse; /* true iff we should reuse old map */
X	long old_loc;
X	long old_dest;
X	
X	if (new_loc == obj->loc) {
X		obj->moved = piece_attr[obj->type].speed;
X		obj->range -= 1;
X		pdebug ("No destination found for %d at %d; func=%d\n",
X			obj->type, obj->loc, obj->func);
X		return;
X	}
X	old_loc = obj->loc; /* remember where we are */
X	old_dest = new_loc; /* and where we're going */
X	
X	d = dist (new_loc, obj->loc);
X	reuse = 1; /* try to reuse unless we learn otherwise */
X	
X	if (comp_map[new_loc].contents == ' ' && d == 2) { /* are we exploring? */
X		vmap_mark_adjacent (pathmap, obj->loc);
X		reuse = 0;
X	}
X	else vmap_mark_path (pathmap, comp_map, new_loc); /* find routes to destination */
X	
X	/* path terrain and move terrain may differ */
X	switch (obj->type) {
X	case ARMY: terrain = "+"; break;
X	case FIGHTER: terrain = "+.X"; break;
X	default: terrain = ".X"; break;
X	}
X	
X	new_loc = vmap_find_dir (pathmap, comp_map, obj->loc,
X				 terrain, adj_list);
X	
X	if (new_loc == obj->loc /* path is blocked? */
X	    && (obj->type != ARMY || !obj->ship)) { /* don't unblock armies on a ship */
X		vmap_mark_near_path (pathmap, obj->loc);
X		reuse = 0;
X		new_loc = vmap_find_dir (pathmap, comp_map, obj->loc,
X					 terrain, adj_list);
X	}
X	
X	/* encourage army to leave city */
X	if (new_loc == obj->loc && map[obj->loc].cityp != NULL
X				&& obj->type == ARMY) {
X		new_loc = move_away (comp_map, obj->loc, "+");
X		reuse = 0;
X	}
X	if (new_loc == obj->loc) {
X		obj->moved = piece_attr[obj->type].speed;
X		
X		if (obj->type == ARMY && obj->ship) ;
X		else pdebug ("Cannot move %d at %d toward objective; func=%d\n",
X			     obj->type, obj->loc, obj->func);
X	}
X	else move_obj (obj, new_loc);
X	
X	/* Try to make more moves using same path map. */
X	if (reuse && obj->moved < obj_moves (obj) && obj->loc != old_dest) {
X		/* check for immediate attack */
X		switch (obj->type) {
X		case FIGHTER:
X			if (comp_map[old_dest].contents != 'X' /* watch fuel */
X				&& obj->range <= piece_attr[FIGHTER].range / 2)
X					return;
X			attack_list = fighter_attack;
X			terrain = "+.";
X			break;
X		case ARMY:
X			attack_list = army_attack;
X			if (obj->ship) terrain = "+*";
X			else terrain = "+.*";
X			break;
X		case TRANSPORT:
X			terrain = ".*";
X			if (obj->cargo) attack_list = tt_attack;
X			else attack_list = "*O"; /* causes tt to wake up */
X			break;
X		default:
X			attack_list = ship_attack;
X			terrain = ".";
X			break;
X		}
X		if (find_attack (obj->loc, attack_list, terrain) != obj->loc)
X			return;
X		
X		/* clear old path */
X		pathmap[old_loc].terrain = T_UNKNOWN;
X		for (d = 0; d < 8; d++) {
X			new_loc = old_loc + dir_offset[d];
X			pathmap[new_loc].terrain = T_UNKNOWN;
X		}
X		/* pathmap is already marked, but this should work */
X		move_objective (obj, pathmap, old_dest, adj_list);
X	}
X}
X
X/*
XCheck to see if the game is over.  We count the number of cities
Xowned by each side.  If either side has no cities and no armies, then
Xthe game is over.  If the computer has less than half as many cities
Xand armies as the user, then the computer will give up.
X*/
X
Xvoid
Xcheck_endgame () { /* see if game is over */
X
X	int nuser_city, ncomp_city;
X	int nuser_army, ncomp_army;
X	piece_info_t *p;
X	int i;
X	
X	date += 1; /* one more turn has passed */
X	if (win != 0) return; /* we already know game is over */
X
X	nuser_city = 0; /* nothing counted yet */
X	ncomp_city = 0;
X	nuser_army = 0;
X	ncomp_army = 0;
X	
X	for (i = 0; i < NUM_CITY; i++) {
X		if (city[i].owner == USER) nuser_city++;
X		else if (city[i].owner == COMP) ncomp_city++;
X	}
X	
X	for (p = user_obj[ARMY]; p != NULL; p = p->piece_link.next)
X		nuser_army++;
X	
X	for (p = comp_obj[ARMY]; p != NULL; p = p->piece_link.next)
X		ncomp_army++;
X		
X	if (ncomp_city < nuser_city/3 && ncomp_army < nuser_army/3) {
X		clear_screen ();
X		prompt ("The computer acknowledges defeat. Do");
X		error ("you wish to smash the rest of the enemy? ");
X
X		if (get_chx() !=  'Y') empend ();
X		(void) addstr ("\nThe enemy inadvertantly revealed its code used for");
X		(void) addstr ("\nreceiving battle information. You can display what");
X		(void) addstr ("\nthey've learned with the ''E'' command.");
X		resigned = TRUE;
X		win = 2;
X		automove = FALSE;
X	}
X	else if (ncomp_city == 0 && ncomp_army == 0) {
X		clear_screen ();
X		(void) addstr ("The enemy is incapable of defeating you.\n");
X		(void) addstr ("You are free to rape the empire as you wish.\n");
X	    	(void) addstr ("There may be, however, remnants of the enemy fleet\n");
X	    	(void) addstr ("to be routed out and destroyed.\n");
X		win = 1;
X		automove = FALSE;
X	}
X	else if (nuser_city == 0 && nuser_army == 0) {
X	    	clear_screen ();
X	    	(void) addstr ("You have been rendered incapable of\n");
X	    	(void) addstr ("defeating the rampaging enemy fascists! The\n");
X	    	(void) addstr ("empire is lost. If you have any ships left, you\n");
X	    	(void) addstr ("may attempt to harass enemy shipping.");
X		win = 1;
X		automove = FALSE;
X	}
X}
//E*O*F compmove.c//

echo x - edit.c
sed -e 's/^X//' > "edit.c" << '//E*O*F edit.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/*
Xedit.c -- Routines to handle edit mode commands.
X*/
X
X#ifdef SYSV
X#include <string.h>
X#else
X#include <strings.h>
X#endif
X
X#include <curses.h>
X#include <ctype.h>
X#include "empire.h"
X#include "extern.h"
X
Xvoid
Xedit(edit_cursor)
Xlong edit_cursor;
X{
X	char e_cursor();
X	void e_leave(), e_print(), e_random();
X	void e_stasis(), e_move(), e_end(), e_wake(), e_sleep();
X	void e_info(), e_prod(), e_help(), e_explore();
X	void e_fill(), e_land(), e_city_func(), e_transport();
X	void e_attack(), e_repair();
X
X	long path_start;
X	int path_type;
X	char e;
X	
X	path_start = -1; /* not building a path yet */
X	
X	for (;;) { /* until user gives command to leave */
X		display_loc_u (edit_cursor); /* position cursor */
X		e = e_cursor (&edit_cursor); /* handle cursor movement */
X
X		switch (e) {
X		case 'B': /* change city production */
X			e_prod (edit_cursor);
X			break;
X		case 'F': /* fill */
X			e_fill (edit_cursor);
X			break;
X		case 'G': /* explore */
X			e_explore (edit_cursor);
X			break;
X		case 'H': /* help */
X			e_help ();
X			break;
X		case 'I': /* directional stasis */
X			e_stasis (edit_cursor);
X			break;
X		case 'K': /* wake up anything and everything */
X			e_wake (edit_cursor);
X			break;
X		case 'L': /* land plane */
X			e_land (edit_cursor);
X			break;
X		case 'M': /* start move to location */
X			path_type = NOPIECE;
X			e_move (&path_start, edit_cursor);
X			break;
X		case 'N': /* end move to location */
X			e_end (&path_start, edit_cursor, path_type);
X			break;
X		case 'O': /* leave display mode */
X			e_leave ();
X			return;
X		case 'P': /* print new sector */
X			e_print (&edit_cursor);
X			break;
X		case 'R': /* make piece move randomly */
X			e_random (edit_cursor);
X			break;
X		case 'S': /* sleep */
X			e_sleep (edit_cursor);
X			break;
X		case 'T': /* transport army */
X			e_transport (edit_cursor);
X			break;
X		case 'U': /* repair ship */
X			e_repair (edit_cursor);
X			break;
X		case 'V': /* set city function */
X			e_city_func (&path_start, edit_cursor, &path_type);
X			break;
X		case 'Y': /* set army func to attack */
X			e_attack (edit_cursor);
X			break;
X		case '?': /* request info */
X			e_info (edit_cursor);
X			break;
X		case '\014': /* control-L */
X			redraw ();
X			break;
X		default: /* bad command? */
X			huh ();
X			break;
X		}
X	}
X}
X
X/*
XGet the next command.  We handle cursor movement here.
XThis routine is an attempt to make cursor movement reasonably
Xfast.
X*/
X
Xstatic char dirchars[] = "WwEeDdCcXxZzAaQq";
X
Xchar
Xe_cursor (edit_cursor)
Xlong *edit_cursor;
X{
X	char e;
X	char *p;
X	
X	/* set up terminal */
X	(void) crmode ();
X	(void) refresh ();
X	e = getch ();
X	topini (); /* clear any error messages */
X
X	for (;;) {
X		p = strchr (dirchars, e);
X		if (!p) break;
X
X		if (!move_cursor (edit_cursor, dir_offset[(p-dirchars) / 2]))
X			(void) beep ();
X		
X		(void) refresh ();
X		e = getch ();
X	}
X	(void) nocrmode (); /* reset terminal */
X	if (islower (e)) e = upper (e);
X	return e;
X}
X
X/*
XLeave edit mode.
X*/
X
Xvoid
Xe_leave () {
X}
X
X/*
XPrint new sector.
X*/
X
Xvoid
Xe_print (edit_cursor)
Xlong *edit_cursor;
X{
X        int sector;
X	
X	sector = get_range ("New Sector? ", 0, NUM_SECTORS-1);
X
X	/* position cursor at center of sector */
X	*edit_cursor = sector_loc (sector);
X	sector_change (); /* allow change of sector */
X}
X
X/*
XSet the function of a piece.
X*/
X
Xvoid
Xe_set_func (loc, func)
Xlong loc;
Xlong func;
X{
X	piece_info_t *obj;
X	obj = find_obj_at_loc (loc);
X	if (obj != NULL && obj->owner == USER) {
X		obj->func = func;
X		return;
X	}
X	huh (); /* no object here */
X}
X	
X/* Set the function of a city for some piece. */
X
Xvoid
Xe_set_city_func (cityp, type, func)
Xcity_info_t *cityp;
Xint type;
Xlong func;
X{
X	cityp->func[type] = func;
X}
X
X/*
XSet a piece to move randomly.
X*/
X
Xvoid
Xe_random (loc)
Xlong loc;
X{
X	e_set_func (loc, RANDOM);
X}
X
Xvoid
Xe_city_random (cityp, type)
Xcity_info_t *cityp;
Xint type;
X{
X	e_set_city_func (cityp, type, RANDOM);
X}
X
X/*
XPut a ship in fill mode.
X*/
X
Xvoid
Xe_fill (loc)
Xlong loc;
X{
X	if (user_map[loc].contents == 'T' || user_map[loc].contents == 'C')
X		e_set_func (loc, FILL);
X	else huh ();
X}
X
Xvoid
Xe_city_fill (cityp, type)
Xcity_info_t *cityp;
Xint type;
X{
X	if (type == TRANSPORT || type == CARRIER)
X		e_set_city_func (cityp, type, FILL);
X	else huh ();
X}
X
X/*
XSet a piece to explore.
X*/
X
Xvoid
Xe_explore (loc)
Xlong loc;
X{
X	e_set_func (loc, EXPLORE);
X}
X
Xvoid
Xe_city_explore (cityp, type)
Xcity_info_t *cityp;
Xint type;
X{
X	e_set_city_func (cityp, type, EXPLORE);
X}
X
X/*
XSet a fighter to land.
X*/
X
Xvoid
Xe_land (loc)
Xlong loc;
X{
X	if (user_map[loc].contents == 'F')
X		e_set_func (loc, LAND);
X	else huh ();
X}
X/*
XSet an army's function to TRANSPORT.
X*/
X
Xvoid
Xe_transport (loc)
Xlong loc;
X{
X	if (user_map[loc].contents == 'A')
X		e_set_func (loc, WFTRANSPORT);
X	else huh ();
X}
X
X/*
XSet an army's function to ATTACK.
X*/
X
Xvoid
Xe_attack (loc)
Xlong loc;
X{
X	if (user_map[loc].contents == 'A')
X		e_set_func (loc, ARMYATTACK);
X	else huh ();
X}
X
Xvoid
Xe_city_attack (cityp, type)
Xcity_info_t *cityp;
Xint type;
X{
X	if (type == ARMY)
X		e_set_city_func (cityp, type, ARMYATTACK);
X	else huh ();
X}
X
X/*
XSet a ship's function to REPAIR.
X*/
X
Xvoid
Xe_repair (loc)
Xlong loc;
X{
X	if (strchr ("PDSTBC", user_map[loc].contents))
X		e_set_func (loc, REPAIR);
X	else huh ();
X}
X
Xvoid
Xe_city_repair (cityp, type)
Xcity_info_t *cityp;
Xint type;
X{
X	if (type == ARMY || type == FIGHTER || type == SATELLITE)
X		huh ();
X	else e_set_city_func (cityp, type, REPAIR);
X}
X
X/*
XSet object to move in a direction.
X*/
X
Xstatic char dirs[] = "WEDCXZAQ";
X 
Xvoid
Xe_stasis (loc)
Xlong loc;
X{
X	char e;
X	char *p;
X	
X	if (!isupper (user_map[loc].contents)) huh (); /* no object here */
X	else if (user_map[loc].contents == 'X') huh ();
X	else {
X		e = get_chx(); /* get a direction */
X		p = strchr (dirs, e);
X
X		if (p == NULL) huh ();
X		else e_set_func (loc, (long)(MOVE_N - (p - dirs)));
X	}
X}
X
Xvoid
Xe_city_stasis (cityp, type)
Xcity_info_t *cityp;
Xint type;
X{
X	char e;
X	char *p;
X	
X	e = get_chx(); /* get a direction */
X	p = strchr (dirs, e);
X
X	if (p == NULL) huh ();
X	else e_set_city_func (cityp, type, (long)(MOVE_N - (p - dirs)));
X}
X
X/*
XWake up anything and everything.
X*/
X
Xvoid
Xe_wake (loc)
Xlong loc;
X{
X	city_info_t *cityp;
X	piece_info_t *obj;
X	int i;
X
X	cityp = find_city (loc);
X        if (cityp != NULL) {
X		for (i = 0; i < NUM_OBJECTS; i++)
X			cityp->func[i] = NOFUNC;
X	}
X	for (obj = map[loc].objp; obj != NULL; obj = obj->loc_link.next)
X		obj->func = NOFUNC;
X}
X
Xvoid
Xe_city_wake (cityp, type)
Xcity_info_t *cityp;
Xint type;
X{
X	e_set_city_func (cityp, type, NOFUNC);
X}
X
X/*
XSet a city's function.  We get the piece type to set, then
Xthe function itself.
X*/
X
Xvoid
Xe_city_func (path_start, loc, path_type)
Xlong *path_start;
Xlong loc;
Xint *path_type;
X{
X	int type;
X	char e;
X	city_info_t *cityp;
X
X	cityp = find_city (loc);
X	if (!cityp || cityp->owner != USER) {
X		huh ();
X		return;
X	}
X
X	type = get_piece_name();
X	if (type == NOPIECE) {
X		huh ();
X		return;
X	}
X	
X	e = get_chx ();
X	
X	switch (e) {
X	case 'F': /* fill */
X		e_city_fill (cityp, type);
X		break;
X	case 'G': /* explore */
X		e_city_explore (cityp, type);
X		break;
X	case 'I': /* directional stasis */
X		e_city_stasis (cityp, type);
X		break;
X	case 'K': /* turn off function */
X		e_city_wake (cityp, type);
X		break;
X	case 'M': /* start move to location */
X		*path_type = type;
X		e_move (path_start, loc);
X		break;
X	case 'R': /* make piece move randomly */
X		e_city_random (cityp, type);
X		break;
X	case 'U': /* repair ship */
X		e_city_repair (cityp, type);
X		break;
X	case 'Y': /* set army func to attack */
X		e_city_attack (cityp, type);
X		break;
X	default: /* bad command? */
X		huh ();
X		break;
X	}
X}
X
X/*
XBeginning of move to location.
X*/
X
Xvoid
Xe_move (path_start, loc)
Xlong *path_start;
Xlong loc;
X{
X	if (!isupper(user_map[loc].contents)) huh (); /* nothing there? */
X	else if (user_map[loc].contents == 'X') huh (); /* enemy city? */
X        else *path_start = loc;
X}
X
X/*
XEnd of move to location.
X*/
X
Xvoid
Xe_end (path_start, loc, path_type)
Xlong *path_start;
Xlong loc;
Xint path_type;
X{
X	city_info_t *cityp;
X	
X	if (*path_start == -1) huh (); /* no path started? */
X	else if (path_type == NOPIECE) e_set_func (*path_start, loc);
X	else {
X		cityp = find_city (*path_start);
X		ASSERT (cityp);
X		e_set_city_func (cityp, path_type, loc);
X	}
X
X	*path_start = -1; /* remember no path in progress */
X}
X
X/*
XPut a piece to sleep.
X*/
X
Xvoid
Xe_sleep (loc)
Xlong loc;
X{
X	if (user_map[loc].contents == 'O') huh (); /* can't sleep a city */
X	else e_set_func (loc, SENTRY);
X}
X
X/*
XPrint out information about a piece.
X*/
X
Xvoid
Xe_info (edit_cursor)
Xlong edit_cursor;
X{
X	void e_city_info(), e_piece_info();
X
X	char ab;
X
X	ab = user_map[edit_cursor].contents;
X
X	if (ab == 'O') e_city_info (edit_cursor);
X	else if (ab == 'X' && debug) e_city_info (edit_cursor);
X	else if ((ab >= 'A') && (ab <= 'T'))
X		e_piece_info (edit_cursor, ab);
X	else if ((ab >= 'a') && (ab <= 't') && (debug))
X		e_piece_info (edit_cursor, ab);
X	else huh ();
X}
X
X/*
XPrint info about a piece.
X*/
X
Xvoid
Xe_piece_info (edit_cursor, ab)
Xlong edit_cursor;
Xchar ab;
X{
X	piece_info_t *obj;
X	int type;
X	char *p;
X
X	ab = upper (ab);
X	p = strchr (type_chars, ab);
X	type = p - type_chars;
X
X	obj = find_obj (type, edit_cursor);
X	ASSERT (obj != NULL);
X	describe_obj (obj);
X}
X
X/*
XDisplay info on a city.
X*/
X
Xvoid
Xe_city_info (edit_cursor)
Xlong edit_cursor;
X{
X	piece_info_t *obj;
X	city_info_t *cityp;
X	int f, s;
X	char func_buf[STRSIZE];
X	char temp_buf[STRSIZE];
X	char junk_buf2[STRSIZE];
X
X	error (0); /* clear line */
X
X	f = 0; /* no fighters counted yet */
X	for (obj = map[edit_cursor].objp; obj != NULL;
X		obj = obj->loc_link.next)
X			if (obj->type == FIGHTER) f++;
X
X	s = 0; /* no ships counted yet */
X	for (obj = map[edit_cursor].objp; obj != NULL;
X		obj = obj->loc_link.next)
X			if (obj->type >= DESTROYER) s++;
X
X	if (f == 1 && s == 1) 
X		(void) sprintf (jnkbuf, "1 fighter landed, 1 ship docked");
X	else if (f == 1)
X		(void) sprintf (jnkbuf, "1 fighter landed, %d ships docked", s);
X	else if (s == 1)
X		(void) sprintf (jnkbuf, "%d fighters landed, 1 ship docked", f);
X	else (void) sprintf (jnkbuf, "%d fighters landed, %d ships docked", f, s);
X
X	cityp = find_city (edit_cursor);
X	ASSERT (cityp != NULL);
X
X	*func_buf = 0; /* nothing in buffer */
X	for (s = 0; s < NUM_OBJECTS; s++) { /* for each piece */
X		if (cityp->func[s] < 0)
X			(void) sprintf (temp_buf, "%c:%s; ",
X				piece_attr[s].sname,
X				func_name[FUNCI(cityp->func[s])]);
X		else (void) sprintf (temp_buf, "%c: %d;",
X				piece_attr[s].sname,
X				cityp->func[s]);
X		
X		(void) strcat (func_buf, temp_buf);
X	}
X
X	(void) sprintf (junk_buf2,
X		"City at location %d will complete %s on round %d",
X		cityp->loc,
X		piece_attr[cityp->prod].article,
X		date + piece_attr[cityp->prod].build_time - cityp->work);
X
X	info (junk_buf2, jnkbuf, func_buf);
X}
X
X/*
XChange city production.
X*/
X
Xvoid
Xe_prod (loc)
Xlong loc;
X{
X	city_info_t *cityp;
X	
X	cityp = find_city (loc);
X
X	if (cityp == NULL) huh (); /* no city? */
X	else set_prod (cityp);
X}
X
X/*
Xget help
X*/
X
Xvoid
Xe_help () {
X	help (help_edit, edit_lines);
X	prompt ("Press any key to continue: ");
X	(void) get_chx ();
X}
//E*O*F edit.c//

echo x - empire.c
sed -e 's/^X//' > "empire.c" << '//E*O*F empire.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/*
Xempire.c -- this file contains initialization code, the main command
Xparser, and the simple commands.
X*/
X
X#include <stdio.h>
X#include <curses.h>
X#include "empire.h"
X#include "extern.h"
X
Xvoid
Xempire () {
X	void do_command();
X	void print_zoom();
X
X	char order;
X
X	ttinit (); /* init tty */
X	rndini (); /* init random number generator */
X
X	clear_screen (); /* nothing on screen */
X	(void) move (7, 0);
X	ver ();
X	pos_str (8, 0, "Detailed directions are in EMPIRE.DOC\n");
X	(void) refresh ();
X
X	if (!restore_game ()) /* try to restore previous game */
X		init_game (); /* otherwise init a new game */
X
X	/* Command loop starts here. */
X
X	for (;;) { /* until user quits */
X	    if (automove) { /* don't ask for cmd in auto mode */
X		user_move ();
X		comp_move (1);
X		save_game ();
X	    }
X	    else {
X		prompt (0); /* blank top line */
X		(void) refresh ();
X	        prompt ("Your orders? ");
X	        order = get_chx (); /* get a command */
X		do_command (order);
X	    }
X	}
X}
X
X/*
XExecute a command.
X*/
X
Xvoid
Xdo_command (orders)
Xchar orders;
X{
X	void c_debug(), c_quit(), c_sector(), c_map(), c_examine();
X	void c_give(), c_movie();
X
X	char e;
X	int ncycle;
X
X	switch (orders) {
X	case 'A': /* turn on auto move mode */
X		automove = TRUE;
X		error ("Now in Auto-Mode");
X		user_move ();
X		comp_move (1);
X		save_game ();
X		break;
X
X	case 'C': /* give a city to the computer */
X		c_give ();
X		break;
X	
X	case 'D': /* display round number */
X		error ("Round #%d", date);
X		break;
X
X	case 'E': /* examine enemy map */
X		if (resigned) c_examine ();
X		else huh (); /* illegal command */
X		break;
X
X	case 'F': /* print map to file */
X		c_map ();
X		break;
X
X	case 'G': /* give one free enemy move */
X		comp_move (1);
X		break;
X
X	case 'H': /* help */
X		help (help_cmd, cmd_lines);
X		break;
X
X	case 'J': /* edit mode */
X		ncycle = cur_sector ();
X		if (ncycle == -1) ncycle = 0;
X		edit (sector_loc (ncycle));
X		break;
X
X	case 'M': /* move */
X		user_move ();
X		comp_move (1);
X		save_game ();
X		break;
X
X	case 'N': /* give enemy free moves */
X		ncycle = getint ("Number of free enemy moves: ");
X		comp_move (ncycle);
X		save_game ();
X		break;
X
X	case 'P': /* print a sector */
X		c_sector ();
X		break;
X
X	case '\026': /* some interrupt */
X	case 'Q': /* quit */
X		c_quit ();
X		break;
X
X	case 'R': /* restore game */
X		clear_screen ();
X		e = restore_game ();
X		break;
X
X	case 'S': /* save game */
X		save_game ();
X		break;
X	
X	case 'T': /* trace: toggle save_movie flag */
X		save_movie = !save_movie;
X		if (save_movie) comment ("Saving movie screens to 'empmovie.dat'.");
X		else comment ("No longer saving movie screens.");
X		break;
X
X	case 'W': /* watch movie */
X		if (resigned || debug) replay_movie ();
X		else error ("You cannot watch movie until computer resigns.");
X		break;
X	
X	case 'Z': /* print compressed map */
X		(void) clear ();
X		print_zoom (user_map);
X		(void) refresh ();
X		break;
X
X	case '\014': /* redraw the screen */
X		redraw ();
X		break;
X
X	case '+': /* change debug state */
X		e = get_chx();
X		if ( e  ==  '+' ) debug = TRUE;
X		else if ( e  ==  '-' ) debug = FALSE;
X		else huh ();
X		break;
X
X	default:
X		if (debug) c_debug (orders); /* debug */
X		else huh (); /* illegal command */
X		break;
X	}
X	e = e; /* keep lint quiet */
X}
X
X/*
XGive an unowned city (if any) to the computer.  We make
Xa list of unowned cities, choose one at random, and mark
Xit as the computers.
X*/
X
Xvoid
Xc_give () {
X	int unowned[NUM_CITY];
X	long i, count;
X
X	count = 0; /* nothing in list yet */
X	for (i = 0; i < NUM_CITY; i++) {
X		if (city[i].owner == UNOWNED) {
X			unowned[count] = i; /* remember this city */
X			count += 1;
X		}
X	}
X	if (count == 0) {
X		error ("There are no unowned cities.");
X		return;
X	}
X	i = irand (count);
X	i = unowned[i]; /* get city index */
X	city[i].owner = COMP;
X	city[i].prod = NOPIECE;
X	city[i].work = 0;
X	scan (comp_map, city[i].loc);
X}
X
X/*
XDebugging commands should be implemented here.  
XThe order cannot be any legal command.
X*/
X
Xvoid
Xc_debug (order)
Xchar order;
X{
X	char e;
X
X	switch (order) {
X	case '#' : c_examine (); break;
X	case '%' : c_movie (); break;
X	
X	case '@': /* change trace state */
X		e = get_chx();
X		if ( e  ==  '+' ) trace_pmap = TRUE;
X		else if ( e  ==  '-' ) trace_pmap = FALSE;
X		else huh ();
X		break;
X
X	case '$': /* change print_debug state */
X		e = get_chx();
X		if ( e  ==  '+' ) print_debug = TRUE;
X		else if ( e  ==  '-' ) print_debug = FALSE;
X		else huh ();
X		break;
X
X	case '&': /* change print_vmap state */
X		print_vmap = get_chx();
X		break;
X
X	default: huh (); break;
X	}
X}
X
X/*
XThe quit command.  Make sure the user really wants to quit.
X*/
X
Xvoid
Xc_quit () {
X	if (getyn ("QUIT - Are you sure? ")) {
X	    empend ();
X	}
X}
X
X/*
XPrint a sector.  Read the sector number from the user
Xand print it.
X*/
X
Xvoid
Xc_sector () {
X	int num;
X
X	num = get_range ("Sector number? ", 0, NUM_SECTORS-1);
X	print_sector_u (num);
X}
X
X/*
XPrint the map to a file.  We ask for a filename, attempt to open the
Xfile, and if successful, print out the user's information to the file.
XWe print the map sideways to make it easier for the user to print
Xout the map.
X*/
X
Xvoid
Xc_map () {
X	FILE *f;
X	int i, j;
X	char line[MAP_HEIGHT+2];
X
X	prompt ("Filename? ");
X	get_str (jnkbuf, STRSIZE);
X
X	f = fopen (jnkbuf, "w");
X	if (f == NULL) {
X		error ("I can't open that file.");
X		return;
X	}
X	for (i = 0; i < MAP_WIDTH; i++) { /* for each column */
X		for (j = MAP_HEIGHT-1; j >= 0; j--) { /* for each row */
X                        line[MAP_HEIGHT-1-j] = user_map[row_col_loc(j,i)].contents;
X		}
X		j = MAP_HEIGHT-1;
X		while (j >= 0 && line[j] == ' ') /* scan off trailing blanks */
X			j -= 1;
X			
X		line[++j] = '\n';
X		line[++j] = 0; /* trailing null */
X		(void) fputs (line, f);
X	}
X	(void) fclose (f);
X}
X
X/*
XAllow user to examine the computer's map.
X*/
X
Xvoid
Xc_examine () {
X	int num;
X
X	num = get_range ("Sector number? ", 0, NUM_SECTORS-1);
X	print_sector_c (num);
X}
X
X/*
XWe give the computer lots of free moves and
XPrint a "zoomed" version of the computer's map.
X*/
X
Xvoid
Xc_movie () {
X	(void) clear ();
X	for (;;) {
X		comp_move (1);
X		print_zoom (comp_map);
X		save_game ();
X#ifdef PROFILE
X		if (date == 125) empend();
X#endif
X	}
X}
//E*O*F empire.c//

echo shar: End of archive 2 \(of 6\).
cp /dev/null ark2isdone
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

chuck@amdahl.uts.amdahl.com (Charles Simmons) (04/11/88)

# 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:
# bugs display.c empire.h

echo x - bugs
sed -e 's/^X//' > "bugs" << '//E*O*F bugs//'
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
XBugs
X----
X
X1)  The computer is allowed to leave armies in cities.  Fixing this
Xfeature might be difficult.  This feature also gives the computer
Xa small advantage which slightly helps overcome the natural superiority
Xof humans.
X
X2)  Once upon a time, if a fighter landed on a carrier, the user
Xwas never asked if she wanted to move it again.  I don't know if
Xthis bug still exists.
X
X3)  Satellites are not completely implemented.  When the user
Xmoves to a satellite, it should be allowed.  The user should
Xnot be asked if she "really wants to attack her own piece".
XEnemy satelites should not cause the user's pieces to be
Xawoken, since there is nothing the user can do.
X
X4)  If the computer has a damaged ship and is returning it to
Xport, the user can block the ship with another piece.  The computer
Xwill not attempt to move the damaged ship.  The user can then
Xsail up a transport to the city the damaged ship is heading toward,
Xunblock the damaged ship, and as soon as the damaged ship enters
Xport, take the city.  Since ships in port are capturable, the user
Xshould now own one slightly damaged ship.
X
X5)  An assertion failed in 'attack_obj'.  Somehow this routine
Xwas passed a location where there was no defending object.  This
Ximplies that a view map was messed up.  I've no idea why.
X
X6)  Currently, a fighter's range is not decremented if it is not
Xmoved during a turn.  This could be called a feature, but I think
XI would really prefer that a fighter's range was decremented by at
Xleast one each turn.  The computer does not take advantage of this
Xbug.
X
X7)  While playing a game, one of my cities was producing armies.
XAt one point in the game, the city sudddenly decided that never
Xagain would it increment the build count.  Actually, at one point
Xthe build count seemed to be negative.  The count eventually reached
Xzero and stayed there.  My hypothesis is that somewhere I'm trashing
Xmy data structures, and somehow the "owner" of the city got set to
Xsome incorrect value.
X
X(Note that bugs (5) and (7) occured while there was an inconsitency
Xin the 'kill_city' routine.  Hardware had its ownership modified,
Xbut it remained in the object list of the wrong user.  This
Xcould have caused some problems.  So potentially bugs 5 and 7 no
Xlonger exist.)
X
X8)  Movement of armies in "attack" mode seems a little strange.
X
X9)  Maybe in "sentry" mode, an army should wake up and go into "attack"
Xmode if an invader appears on the continent.  In any event, there
Xshould be some mechanism to allow a user to specify that an army
Xshould sit on the shore and wait for either a transport to pass by,
Xor for an invader to appear on the continent.
X
X10)  When setting a city function, the computer should prompt
Xfor the various pieces of input.  Even I have a hard time setting
Xfunctions for cities.
X
X
XCode Cleanup (Enhancements to improve performance or generality)
X----------------------------------------------------------------
X
X1)  How can we use minicurses?  minicurses is smaller and possibly
Xfaster than full blown curses.  The curses routines that I use
Xwhich are not part of minicurses are 'clrtoeol', 'getstr', and 'getch'.
XThese seem pretty fundamental, but I know very little about curses.
X
X2)  'vmap_cont' and 'vmap_mark_up_cont' would almost certainly be
Xfaster if we didn't recurse, but instead used something like the
Xperimeter lists used in all the other map scanning algorithms.
XSince 'vmap_mark_up_cont' takes about 10% of the processing time,
Xthe performance improvements could be significant.
X
X3)  The real speed killer is 'expand_perimeter'.  This routine
Xis very generalized, and not all the generality is always needed.
XIt might be better to write multiple non-general routines.  I believe
Xthis routine accounts for roughly 20% to 30% of the processing time.
XIf we take subroutines like 'strchr' and 'terrain_type' into account
Xas well, this routine accounts for more like 50% of the processing.
XHowever, on a mainframe, the game plays sufficiently fast.
X
X4)  Allow multiple computer players and/or multiple human players.
XAllow the players to come in over a network of workstations.
X
X5)  The width and height of the map should be parameters to the
Xprogram.
X
X6)  Interrupts should be caught.  When an interrupt is received,
Xthe user should be asked if she really wants to quit, and she
Xshould be warned if the game will not be saved.  This is particularly
Xuseful when playing over a noisy phone line that generates spurious
Xinterrupts.  (When will digitial telephone transmissions arrive?)
X
X7)  Storage for the various data structures should be allocated
Xdynamically instead of being stored in static tables.
X
X
XDebugging Aids
X--------------
X
X1)  My current debugging algorithm is to put the program into
Xdebug mode with the "++" command, and then put the program into
Xmovie mode with the "%" command.  When I see something wrong, I
Xhit the interrupt key and restart the program, so that I can
Xuse other debugging commands.  It would be nice if this interface
Xwas better.  (See comments above about catching interrupts.)
X
X2)  It would be nice if there were debugging commands which allowed
Xme to examine the computer map and request information such as
X"What is this city producing?  What objects are in this city? etc."
XThis would be very much like the edit mode "?" command.
X
X
XComputer Algorithm Enhancements
X-------------------------------
X
X1)  What improvements can be made to speed up the acquiring of
Xterritory by the computer?
X
XNote:  As a person, I can acquire about 1/2 the board (and control
X3/4 of the board) in about 150 turns.
XThe current algorithm seems to be a little bit slower, but within
Xthe same order of magnitude.
X
XObservations:
X
XI've implemented an algorithm which keeps the computer from exploring
Xevery little square of the world it comes across.  Building satellites
Xseems to slow down the computer.  The computer has an algorithm
Xfor unloading armies on interesting continents.
X
XA careful balance seems to be needed between the number of army
Xproducing cities and the number of tt producing cities.  Currently,
XI build a tt as soon as poosible.  On large
Xcontinents, this is a drawback, as the tt is built before armies
Xare ready to leave.  To counter this effect, I attempted to build
Xmore armies in other cities.  This causes an overproduction in
Xarmies after the first tt's fill up and head off to find something
Xto dump on.
X
XSuggestions:
X
XVarious minor improvements might be made in causing tt's to load
Xone or two turns faster, and to unload one or two turns faster.
XOther improvements would prevent two tt's from heading to the
Xsame destination.
X
XThis fix would probably go in 'transport_move' in 'compmove.c'.
XIn this routine, for a loading transport, we would count the
Xnumber of adjacent loading armies for the current cell, for each reachable
Xcell that is one away, and for each reachable cell that is two away.
XIf there were reachable loading armies, we would move the transport
Xto that square.  Otherwise we would use the current algorithm to
Xfind a destination for the transport and move the transport to that
Xdestination.
X
XFor unloading transports, things are perhaps slightly more difficult.
XI think what needs to be done here is, if the transport cannot move
Xalong a shortest path towards the destination, then the transport
Xshould attempt to move to an adjacent square which minimizes either
Xthe row distance between the tt and the objective, or the column
Xdistance between the tt and the objective.  For tie-breakers, the
Xtt would move to the cell which touched the most land.
X
XPossibly I should describe the problems that the above to tweaks
Xwould fix.  For loading armies, loading transports prefer moving to
Xstaying in place.  Thus, they will sometimes move from a square
Xthat touches lots of loading armies, to a square that touches very
Xfew loading armies.  This probably increases the load time by one
Xor two turns.
X
XFor unloading tt's, a tt will often be on a diagonal from a city
Xthat is an objective.  Unloading armies will hop off one at a time
Xbecause there is only one land square on a shortest path between the
Xtt and the city.  This particular case wastes about 4 moves.
X
X2)  The computer's algorithm is optimized for 70% water and
Xa smoothness of 5.  (More accurately, I developed the algorithm
Xusing these parameters for all of my testing and debugging.)
XPotentially the computer algorithm will be extremely poor for
Xother parameter values.  For example, the computer currently
Xbuilds transports at the first possible opportunity.  Transports
Xwould not be very useful on a world with only 10% water.  (Still,
Xthere are checks to prevent ships from being built in inappropriate
Xcities, so this may not be too much of a problem.)
X
XPotentially, the computer should use a more dynamic alogorithm
Xwhere it "notices" that certain kinds of pieces are going to be
Xneed in the near future and then builds these things.  I've no ideas
Xin this area for concrete algorithms, however.
X
XA plausible alternative would be for the computer to examine the
Xparameters supplied by the user.  If the user knows the parameters,
Xwhy shouldn't the computer?
X
X3)  The computer should be willing to land a fighter on a
Xcarrier if the carrier can be reached in one turn.
X
X4)  The computer should "track" user ships.  That is, whenever the
Xcomputer sees a user ship, it should keep a list of possible locations
Xwhere that ship could be.  It should then make some attempt to find
Xand destroy the ship.  (See "Search and Destroy" under the user
Xinterface comments.)
X
XThis code may be more trouble then its worth.  Currently, it appears
Xthat the computer does a very good job of destroying user shipping.
XThe reason for this is that there are good reasons for the user's
Xships to reappear where ships were previously seen.  Also, the
Xcomputer tends to amass great amounts of fire power when a ship
Xhas been seen, so the computer tends to bump into any other user
Xship that happens to be in the area.  Also, the user is looking for
Xthe computer's ships, and is moving lots of ships into the sealanes
Xthat the computer tends to use.
X
X
XUser Interface Enhancements
X---------------------------
X
X1)  In edit mode, it would be nice if it was easier to move around
Xthe screen.  (Mouse based approaches where the user points and clicks
Xto say "move this piece to here" would be real nice.)  One idea
Xwould be to allow the user to type in four digits that specify the
Xsquare to move to; or to type in two digits where the first digit
Xis the 10's row, and the second digit is the 10's column.  (Thus,
Xif the user typed "43", the cursor would be moved to location "4030".)
X
X2)  Small screens should not be redrawn so often.  When moving
Xpieces, we should move everything that is on the current screen
X(except for stuff that is real close to the edge of the screen,
Xbut not the edge of the board).  If necessary, we might redraw
Xthe screen as the user moved off the screen.  Or we could allow
Xthe user to explicitly tell us to redraw a new sector.  If the
Xscreen was redrawn, we would then work on moving pieces that were
Xdisplayed on the new screen.  In general, we only want to redraw
Xthe screen if we absolutely have to.  (This approach would also
Xbe real, real useful on terminals that are just a little bit smaller
Xthan the complete map.  Using a terminal with something like 105
Xcolumns will be extremely disconcerting.  The screen will be
Xredrawn for what seems like no reason at all.)
X
X3)  It is extremely difficult to tell when a satellite has gone
Xby, and when an enemy piece displayed on the screen is current,
Xand when the piece is a ghost.
X
XOne possibility would be to highlight enemy pieces that have just
Xbeen seen.  Another possibility would be for the user to type
Xa "?" when the cursor is on top of any enemy piece, and the displayed
Xinformation would be how long ago that piece was seen.  (Also, see
Xsearch and destroy tracking below.)
X
X4)  The user should be allowed to "zoom in" and "zoom out".  Zooming
Xin causes the playing board to be printed at four times the density
Xof the previous display density.  Thus, four squares would be
Xdrawn as a single square on the screen.  Zooming out undoes the
Xaffects of zooming in.  Actually, there should be multiple
Xlevels of zooming; 10 levels would probably more than suffice.
XThis enhancement allows the user to easily get a big picture of what
Xis going on.  Most of the time, the user would be able to play
Xwith the board zoomed in.  The user might sometimes need to zoom
Xout to navigate a ship through a "river" or an army over an isthmus.
X
XCurrently, there is a command to display a "zoomed" version of the
Xmap.  This command prints a compact display of the map that fits on
Xthe screen.  This is not quite the same as the above, because what
XI'm suggesting is that the user be allowed to make moves on a compact
Xdisplay of the map.
X
X5)  Search and Destroy.  Here is an idea for a sizeable modification
Xto the user interface.
X
XBasically, we want the computer to keep track of some information
Xfor the user.  The information is possible locations of enemy ships.
XWhen the user sees a ship on the screen, the computer would start
Xtracking the ship.
X
X(Tracking would be implemented as follows:  The initial location
Xof the ship would be stored in a perimeter list.  On each round,
Xthe perimeter list would be expanded to encompass all cells which
Xthe ship could have reached.  Cells that the user has seen this
Xturn are removed from the perimeter list.  After the user's turn,
Xcells that the user has seen are removed from the list again.
XProblems arise when a tracked ship moves into unexplored territory.)
X
XNow the user should be able to give various commands.  Commands
Xwould be things like:
X
X"Describe to me all ships you are tracking."  This command would
Xprint a list of ships being tracked, the types, the number of
Xpossible locations for the ship, the time the ship was first seen,
Xand any code used to represent that ship in tracking displays.
XPossibly, the likelihood that the ship has been destroyed would
Xbe displayed as well.
X
X"Display possible locations for a ship."  This command would
Xdisplay the possible locations for a ship.  Possible locations
Xmight be printed by highlighting the squares where the ship could
Xbe.
X
X"Stop tracking a ship."  This command would delete information about
Xa ship that the user assumed she had destroyed.  Note that the computer
Xwill sometimes be able to tell that a tracked ship has been destroyed.
XThe user might also give this command if a ship was presumed to have
Xgotten itself lost.  The computer might also stop tracking a ship if
Xthe number of possible locations for the ship gets large.
X
XTo support the above, the user should be able to place fighters
Xand ships in "Search and Destroy" mode.  Here the user would specify
Xa tracked ship that was to be searched for.  The fighters and ships of
Xthe user would move in such a way as to minimize the size of the
Xperimeter list for the tracked ship.
X
X5)  It has been suggested that all pieces at a location be moved
Xat once instead of skipping around the screen in the order the
Xpieces happen to be allocated.  For example, the user is often
Xasked what to do with each of the armies aboard a transport.  If
Xthe user knows there are five armies aboard, she may start hitting
Xthe 's' key to put those armies to sleep.  If the user isn't paying
Xenough attention, she may discover that she has just put to sleep
Xsome piece on a few squares away.
X
X6)  When moving a ship or army toward a destination, or when
Xmoving a ship toward port to be repaired, the user will often
Xwant to avoid land that might contain enemy pieces.  This functionality
Xis partially implemented in that ships prefer to be at sea if there
Xis nothing to explore.  Pieces moving towards a destination also
Xprefer to move so that they are in the middle of all the shortest
Xpaths to the destination.
X
XDespite this, it might be desirable to implement a movement mode
Xwhereby ships went out of there way to avoid the enemy.
X
X7)  It should be possible for the user to obtain information about
Xpieces which are contained within another piece.  For example,
Xit would be nice to know the movement function of every ship
Xcontained in a city, or of every army on a transport.
X
X8)  The big problem...  The user needs to type one hell of a lot
Xof commands to control the movement of her pieces.  I've implemented
Xa lot of stuff to alleviate this problem, but lots more can be
Xdone.  If we assume that everything is implemented as a "movement
Xfunction", the following movement functions would be interesting:
X
X"load army" -- an army moves toward the nearest loading transport
Xor transport producing city.  Note that this is the same algorithm
Xas that used by the computer.
X
X(For armies, there are really only
Xthree functions that are needed:  "attack" (which is implemented)
Xwhere the army explores a continent and attackes the enemy or
Xunowned cities; "load" where armies leave a continent; and "unload"
Xwhere armies leave a boat for some other continent.  Also note that
Xwhen I play the game, most of the commands that I give are commands
Xto move the army to a place where a transport will pick up the army,
Xcommands to have the army wait for the transport and load, commands
Xto put the army to sleep while it is being transported, and command
Xto wake up and unload the armies.)
X
X"load transport" -- the transport is marked as "loading", and the
Xtransport moves toward the nearest loading army.
X
X"unload army" -- I'm not sure what this does, nor what "unload
Xtransport" does.  Basically, I want some pseudo-intelligent mechanism
Xfor telling the computer not to ask me questions about armies on
Xa transport until that transport reaches "something interesting".
X"load army" and "load transport" would be much easier to implement.
XUnloading is where intelligence and strategy can be important.
X
X"patrol" -- This causes a piece to move so as to decrease the
Xlikelihood that an enemy piece can sneak by.  One method of implementing
Xthis would be for the piece to move toward the square two away that
Xhad been least recently seen.  It might be desirable to constrain
Xa patrolling piece to a relatively small area of the board.
X
XNote that the "Grope" command (or "explore" mode) is no longer of
Xmuch use for armies.  Possibly "attack" mode would be useful for
Xships and fighters.
X
X9)  One possibility for all of the above might be to allow the
Xuser to specify some algorithm that describes how pieces should
Xmove.  I've thought along the lines of letting the user specify
Xsome sort of macro, and letting the user specify the arguments
Xto one of the 'vmap_find_*obj' routines.  This might require the
Xability for the user to place arbitrary labels on any square.
X
X
XGame Modifications
X------------------
X
X1)  Mobile repair platforms.  Currently a damaged boat moves very
Xslowly towards the nearest city.  A floating platform that could move
Xtowards damaged boats might be useful.  Also, this little baby would
Xstay out of cities and so not be capturable.  Hits would be 1; strength
Xzero; and speed might be greater than 2.
X
X2)  Setting transports to have a strength of zero might be interesting.
X
X3)  Implementing the world as a torus instead of a rectangle might
Xbe better.  This means having the map wrap at the edges instead of
Xterminating at the edges.
X
X4)  Increase the "logistics" aspects of the game.  What about
Xoil, iron, food, etc?  Oil might be used to determine the range
Xof a boat or ship.  Armies might starve without food.  Iron might
Xbe necessary for building a ship.
X
X5)  One of my goals has been to define lots of highly specialized
Xpieces so that each type of piece must be built and used in order
Xto have an effective strategy.  In the original game of empire,
XI eventually developed a strategy whereby the only pieces I would
Xbuild were armies and transports.  The computer basically couldn't
Xbeat me unless we both started on the same continent and it lucked out.
XThe game also ended within one hundred turns.
X
XNow, eventually, I might decide that the current program also has
Xthe same faults (in which case I'll tweak the computer movement
Xalgorithms so that it plays differently).
X
XHowever, I have been making changes to increase the specialization
Xof all the pieces.
X
XObviously, armies must be built because they are the only pieces that
Xcan capture cities.
X
XObviously, transports must be built (on worlds that have a reasonable
Xamount of water) because that's the only way an army can be carried
Xto another city.  Since transports don't move all that fast, and
Xsince transports are fairly weak, they aren't good for much else
X(in theory).
X
XBeyond this...  Patrol boats and fighters are very similar.  Both
Xare fast, both are quickly produced, both have low hits.
XI suspect that an effective strategy could be developed where one
Xor the other of these pieces, but not necessarily both, were built.
XPatrol boats have an advantage in their unlimited range.  This makes
Xthem useful for extremely long range exploration.  Fighters have
Xan advantage in their great speed.  This makes them extremely useful
Xfor tracking and patrolling.  Fighters also have a sufficient range
Xthat they can easily move across the board from one city to another
Xwithout really needing carriers.  Possibly, the range of fighters
Xis too great.  Possibly, the range of fighters should be 16.
X(For purposes of user friendliness, the range of fighters should be
Xa multiple of the speed.)
X
XNow, carriers, destroyers, subs, and battleships are very similar.
XCarriers and battleships have the advantage that they can take a
Xbeating and then be reparied.  Destroyers and subs have the advantage
Xthat lots of them can be produced which increases the amount of
Xterritory that can be seen at any time.
X
XDecreasing the range of fighters might increase the utility of
Xcarriers, but then the user would probably build more patrol boats
Xinstead.
X
XSo, I guess I'm looking for more specialized pieces.  Mobile
Xrepair platforms are one idea.  Satellites are an attempt at
Xa second idea, but this idea needs more refinement.  Currently,
Xthe computer does just fine without building satellites (as near
Xas I can tell).  Maybe satellites would be more useful if they
Xwere faster or scanned a larger area.
X
X
XUser Comments
X-------------
X> empire is getting very good about asking me about all the troups on a transport,
X> etc. before going on to another piece, but its still not perfect.
X> 
X> the computer still seems to be spending too much effort fighting, and not enough
X> exploring.  i think i've got it beat, although i'm not sure yet.  i was burning
X> transport after transport (of the computers) while he tried to take an island
X> from me.  he finally succeeded, but i must have killed 8 transports in the
X> process (he got two of mine).
X> 
X> early in the game, he had a real superiority with patrol boats, that was giving
X> me fits.  but now i think i've got him, and am making it very hard for him to
X> move around.  also, he still hasn't finished taking islands he's colonized--
X> hopefully i'll be able to take some away from him.  he should have consolidated
X> them long ago, and being harassing me.
X> 
X> The satellite is fun, although i wish it would head into enemy territory
X> instead of back into mine.
X
XThe first paragraph is a request that all pieces of a type in
Xone position be moved before moving pieces of that type at
Xanother position.  This fix should be combined with the needed fix
Xto move all pieces on the screen before redrawing the screen.
X
XThe second paragraph suggests to me that the computer should
Xmove lots of patrol boats and other support craft into an area
Xto clear it out before moving in transports.  The transports are
Xtoo vulnerable, and the computer isn't defending them very well.
XMaybe what I mean here is that the computer should have a concept
Xof "escorts".  When moving a transport, a destroyer, sub, or at
Xleast a patrol boat should try and remain near by.  Then if our
Xtransport gets destroyed by an enemy, at least there is some chance
Xthat we can kill off the attacker.
X
XOther problems probably exist in this area as well.  Early in the
Xgame, the computer will see an unowned city and drop some armies on
Xit.  A little later the computer will notice that there is a user
Xcity on the same continent.  Now, all the computer's transports
Xgo floating around that user city and dump armies on it.  The computer
Xhas used massive amounts of resources to take a continent instead
Xof exploring and sweeping up more easily defended continents.
X
XOn the other hand, the computer is very "contentious".  It's kind of
Xfun to have the computer fighting so hard to keep me from taking its
Xcities.  Also, if I don't use the current strategy, there is a danger
Xthat the computer will not fight hard enough to keep the user from
Xinvading and taking a computer-owned continent.
X
XColonization...  The computer takes new continents very slowly.
XI don't know why.  The computer should be producing armies in
Xthe first city it takes, and the computer will produce armies
Xin new cities as long as it sees an unowned city to attack.
XPotentially, there is a problem in that the computer might not
Xsee an unowned city, even though there is lots of unexplored territory,
Xand thus probably an unowned city, on the continent.
X
XThe bigger problem seems to be that the computer is producing too
Xmany armies and not enough other stuff.  In the particular game that
Xthese comments derived from, the computer had a massive continent
Xthat was smothered in armies.  Instead of producing so many armies,
Xthe computer should have produced more fighters and non-transport
Xships.  Tweaking the "ration?" arrays in compmove.c should make things
Xbetter.
X
XNote that the user's strategy was to seek and destroy transports.
XThe user would ignore other ships for relatively long periods of
Xtime for a chance to destroy one of the computer's transports.  This
Xstrategy was quite effective; the user tended to be able to keep
Xmany more transports on the board than the computer.
X
X> planes aren't that useful even as they are--sure they're good for zooming
X> in and destroying the odd troop transport, but they're not that helpful for
X> exploration.  i still suspect the optimal strategy is armies, transports,
X> and patrol boats, with a few planes.  Later in the game planes become
X> useful because they can be gotten to the scene so quickly.   If you want
X> to reduce them, how about reducing them to 24?   Also, when a plane is
X> about to fly past the point of no return (return to anything) how about
X> a question, a la "troops can't walk on water, Sir?".  as long as i can
X> override the objection, its not a problem.
X
X> oh, i think it would suffice to be able to launch satellites in a particular
X> direction.
X
XThe first paragraph is a response to my suggestion that a fighter's
Xrange should be reduced to 16 so as to make patrol boats and carriers
Xmore useful.
X
XMaybe we should crank up the hits on the various objects.  This
Xwould make attacks a little more deterministic.  For example:
X
Xarmies: 2               armies 10
Xtransports: 2           transports 10
Xfighters: 2             fighters 10
Xpatrol boats: 2         patrol boats 10
Xsubmarines: 4           submarines 20
Xdestoyers: 6            destroyers 30
Xcarriers: 10            carriers 80
Xbattleships: 12         battleships 100
X
XBut then we would have to worry about repairing armies?  Or maybe
Xthe effectiveness of an army simply goes down when it doesn't have
Xfull hit points?  This would also greatly increase the repair time
Xof an object.  Or maybe objects would be repaired two or 10 hits
Xper turn.
X
XOther bugs...
X-------------
X
XPossibly displayed messages should be of two types:  Important messages
Xand non-important messages.  After an important message, the computer
Xwould delay for the full amount of delay time.  After an unimportant
Xmessage, it might not do anything.
X
X
X1)  The "m" and "n" commands should work in movement mode.
XThey should also work when setting the function of a piece
Xfor a city.
X
X2)  Should there be a way to tell the program you are done
Xattempting to move a fighter, and don't ask me about moves
Xseven more times?
X
X3)  The program should use environment variables or command line
Xarguments to supply the filenames for "empsave.dat" and "empmovie.dat".
XA command line argument would also be useful for telling the
Xprogram how often the game should be saved.  Actually, all
Xcommand line arguments should have associated environment variables
Xthat can be used to set defaults.
X
X4)  When the user types "q" to quit, the program should exit
Ximmediately if the game has been saved.  If the game hasn't been
Xsaved, the user should be told, and asked if she really wants to
Xquit.
//E*O*F bugs//

echo x - display.c
sed -e 's/^X//' > "display.c" << '//E*O*F display.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/*
Xdisplay.c -- This file contains routines for displaying sectors and
Xmoving the cursor about in a sector.  We need to remember the following
Xinformation:
X
X	the current map portion displayed on the screen;
X
X	whether the displayed portion is from the user's or the computer's
X	point of view;
X*/
X
X#ifdef SYSV
X#include <string.h>
X#else
X#include <strings.h>
X#endif
X
X#include <curses.h>
X#include "empire.h"
X#include "extern.h"
X
Xstatic int whose_map = UNOWNED; /* user's or computer's point of view */
Xstatic int ref_row; /* map loc displayed in upper-left corner */
Xstatic int ref_col;
Xstatic int save_sector; /* the currently displayed sector */
Xstatic int save_cursor; /* currently displayed cursor position */
Xstatic int change_ok = TRUE; /* true if new sector may be displayed */
X
X/*
XThis routine is called when the current display has been
Xtrashed and no sector is shown on the screen.
X*/
X
Xvoid kill_display () {
X	whose_map = UNOWNED;
X}
X
X/*
XThis routine is called when a new sector may be displayed on the
Xscreen even if the location to be displayed is already on the screen.
X*/
X
Xvoid sector_change () {
X	change_ok = TRUE;
X}
X
X/*
XReturn the currently displayed user sector, if any.  If a user
Xsector is not displayed, return -1.
X*/
X
Xint cur_sector () {
X	if (whose_map != USER) return (-1);
X	return (save_sector);
X}
X
X/*
XReturn the current position of the cursor.  If the user's map
Xis not on the screen, we return -1.
X*/
X
Xlong cur_cursor () {
X	if (whose_map != USER) return (-1);
X	return (save_cursor);
X}
X
X/*
XDisplay a location on the screen. We figure out the sector the
Xlocation is in and display that sector.  The cursor is left at
Xthe requested location.
X
XWe redisplay the sector only if we either have been requested to
Xredisplay the sector, or if the location is not on the screen.
X*/
X
Xvoid
Xdisplay_loc (whose, vmap, loc)
Xint whose; /* whose map to display */
Xview_map_t vmap[];
Xlong loc; /* location to display */
X{
X	void print_sector(), show_loc();
X	
X	if (change_ok || whose != whose_map || !on_screen (loc))
X		print_sector (whose, vmap, loc_sector (loc));
X		
X	show_loc (vmap, loc);
X}
X
X/*
XDisplay a location iff the location is on the screen.
X*/
X
Xvoid
Xdisplay_locx (whose, vmap, loc)
Xint whose; /* whose map to display */
Xview_map_t vmap[];
Xlong loc; /* location to display */
X{
X	if (whose == whose_map && on_screen (loc))
X		show_loc (vmap, loc);
X}
X
X/*
XDisplay a location which exists on the screen.
X*/
X
Xvoid
Xshow_loc (vmap, loc)
Xview_map_t vmap[];
Xlong loc;
X{
X	int r, c;
X	
X	r = loc_row (loc);
X	c = loc_col (loc);
X	(void) move (r-ref_row+NUMTOPS, c-ref_col);
X	(void) addch ((chtype)vmap[loc].contents);;
X	save_cursor = loc; /* remember cursor location */
X	(void) move (r-ref_row+NUMTOPS, c-ref_col);
X}
X
X/*
XPrint a sector of the user's on the screen.  If it is already displayed,
Xwe do nothing.  Otherwise we redraw the screen.  Someday, some intelligence
Xin doing this might be interesting.  We heavily depend on curses to update
Xthe screen in a reasonable fashion.
X
XIf the desired sector
Xis not displayed, we clear the screen.  We then update the screen
Xto reflect the current map.  We heavily depend on curses to correctly
Xoptimize the redrawing of the screen.
X
XWhen redrawing the screen, we figure out where the
Xcenter of the sector is in relation to the map.  We then compute
Xthe screen coordinates where we want to display the center of the
Xsector.  We will remember the sector displayed, the map displayed,
Xand the map location that appears in the upper-left corner of the
Xscreen.
X*/
X 
Xvoid
Xprint_sector (whose, vmap, sector)
Xchar whose; /* USER or COMP */
Xview_map_t vmap[]; /* map to display */
Xint sector; /* sector to display */
X{
X	void display_screen();
X
X	int first_row, first_col, last_row, last_col;
X	int display_rows, display_cols;
X	int r, c;
X
X	save_sector = sector; /* remember last sector displayed */
X	change_ok = FALSE; /* we are displaying a new sector */
X
X	display_rows = lines - NUMTOPS - 1; /* num lines to display */
X	display_cols = cols - NUMSIDES;
X
X	/* compute row and column edges of sector */
X	first_row = sector_row (sector) * ROWS_PER_SECTOR;
X	first_col = sector_col (sector) * COLS_PER_SECTOR;
X	last_row = first_row + ROWS_PER_SECTOR - 1;
X	last_col = first_col + COLS_PER_SECTOR - 1;
X
X	if (!(whose == whose_map /* correct map is on screen? */
X	   && ref_row <= first_row /* top row on screen? */
X	   && ref_col <= first_col /* first col on screen? */
X	   && ref_row + display_rows - 1 >= last_row /* bot row on screen? */
X	   && ref_col + display_cols - 1 >= last_col)) /* last col on screen? */
X	(void) clear (); /* erase current screen */
X
X	/* figure out first row and col to print; subtract half
X	   the extra lines from the first line */
X
X	ref_row = first_row - (display_rows - ROWS_PER_SECTOR) / 2;
X	ref_col = first_col - (display_cols - COLS_PER_SECTOR) / 2;
X
X	/* try not to go past bottom of map */
X	if (ref_row + display_rows - 1 > MAP_HEIGHT - 1)
X		ref_row = MAP_HEIGHT - 1 - (display_rows - 1);
X
X	/* never go past top of map */
X        if (ref_row < 0) ref_row = 0;
X
X	/* same with columns */
X	if (ref_col + display_cols - 1 > MAP_WIDTH - 1)
X		ref_col = MAP_WIDTH - 1 - (display_cols - 1);
X
X	if (ref_col < 0) ref_col = 0;
X
X        whose_map = whose; /* remember whose map is displayed */
X	display_screen (vmap);
X
X	/* print x-coordinates along bottom of screen */
X	for (c = ref_col; c < ref_col + display_cols && c < MAP_WIDTH; c++)
X	if (c % 10 == 0) {
X		pos_str (lines-1, c-ref_col, "%d", c);
X	}
X	/* print y-coordinates along right of screen */
X	for (r = ref_row; r < ref_row + display_rows && r < MAP_HEIGHT; r++) {
X		if (r % 2 == 0)
X			pos_str (r-ref_row+NUMTOPS, cols-NUMSIDES+1, "%2d", r);
X		else pos_str (r-ref_row+NUMTOPS, cols-NUMSIDES+1, "  ");
X	}
X	/* print round number */
X	(void) sprintf (jnkbuf, "Sector %d Round %d", sector, date);
X	for (r = 0; jnkbuf[r] != '\0'; r++) {
X		if (r+NUMTOPS >= MAP_HEIGHT) break;
X		(void) move (r+NUMTOPS, cols-NUMSIDES+4);
X		(void) addch ((chtype)jnkbuf[r]);
X	}
X}
X
X/*
XDisplay the portion of the map that appears on the screen.
X*/
X
Xvoid display_screen (vmap)
Xview_map_t vmap[];
X{
X	int display_rows, display_cols;
X	int r, c;
X	long t;
X
X	display_rows = lines - NUMTOPS - 1; /* num lines to display */
X	display_cols = cols - NUMSIDES;
X
X	for (r = ref_row; r < ref_row + display_rows && r < MAP_HEIGHT; r++)
X	for (c = ref_col; c < ref_col + display_cols && c < MAP_WIDTH; c++) {
X		t = row_col_loc (r, c);
X		(void) move (r-ref_row+NUMTOPS, c-ref_col);
X		(void) addch ((chtype)vmap[t].contents);;
X	}
X}
X
X/*
XMove the cursor in a specified direction.  We return TRUE if the
Xcursor remains in the currently displayed screen, otherwise FALSE.
XWe display the cursor on the screen, if possible.
X*/
X
Xint
Xmove_cursor (cursor, offset)
Xlong *cursor; /* current cursor position */
Xint offset; /* offset to add to cursor */
X{
X	long t;
X	int r, c;
X 
X	t = *cursor + offset; /* proposed location */
X	if (!map[t].on_board) return (FALSE); /* trying to move off map */
X	if (!on_screen (t)) return (FALSE); /* loc is off screen */
X	
X	*cursor = t; /* update cursor position */
X	save_cursor = *cursor;
X	       
X	r = loc_row (save_cursor);
X	c = loc_col (save_cursor);
X	(void) move (r-ref_row+NUMTOPS, c-ref_col);
X       
X	return (TRUE);
X}
X
X/*
XSee if a location is displayed on the screen.
X*/
X
Xint on_screen (loc)
Xlong loc;
X{
X	int new_r, new_c;
X	
X	new_r = loc_row (loc);
X	new_c = loc_col (loc);
X
X	if (new_r < ref_row /* past top of screen */
X	 || new_r - ref_row > lines - NUMTOPS - 1 /* past bot of screen? */
X	 || new_c < ref_col /* past left edge of screen? */
X	 || new_c - ref_col > cols - NUMSIDES) /* past right edge of screen? */
X	return (FALSE);
X
X	return (TRUE);
X}
X
X/* Print a view map for debugging. */
X
Xvoid
Xprint_xzoom (vmap)
Xview_map_t *vmap;
X{
X	print_zoom (vmap);
X#if 0
X	prompt ("Hit a key: ");
X	(void) get_chx (); /* wait for user */
X#endif
X}
X
X/*
XPrint a condensed version of the map.
X*/
X
Xchar zoom_list[] = "XO*tcbsdpfaTCBSDPFAzZ+. ";
X
Xvoid
Xprint_zoom (vmap)
Xview_map_t *vmap;
X{
X	void print_zoom_cell();
X
X	int row_inc, col_inc;
X	int r, c;
X
X	kill_display ();
X
X	row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
X	col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
X
X	for (r = 0; r < MAP_HEIGHT; r += row_inc)
X	for (c = 0; c < MAP_WIDTH; c += col_inc)
X	print_zoom_cell (vmap, r, c, row_inc, col_inc);
X
X	pos_str (0, 0, "Round #%d", date);
X	
X	(void) refresh ();
X}
X
X/*
XPrint a single cell in condensed format.
X*/
X
Xvoid
Xprint_zoom_cell (vmap, row, col, row_inc, col_inc)
Xview_map_t *vmap;
Xint row, col;
Xint row_inc, col_inc;
X{
X	int r, c;
X	char cell;
X
X	cell = ' ';
X	for (r = row; r < row + row_inc; r++)
X	for (c = col; c < col + col_inc; c++)
X	if (strchr (zoom_list, vmap[row_col_loc(r,c)].contents)
X		< strchr (zoom_list, cell))
X	cell = vmap[row_col_loc(r,c)].contents;
X	
X	(void) move (row/row_inc + NUMTOPS, col/col_inc);
X	(void) addch ((chtype)cell);
X}
X
X/*
XPrint a condensed version of a pathmap.
X*/
X
Xvoid
Xprint_pzoom (s, pmap, vmap)
Xchar *s;
Xpath_map_t *pmap;
Xview_map_t *vmap;
X{
X	void print_pzoom_cell();
X
X	int row_inc, col_inc;
X	int r, c;
X
X	kill_display ();
X
X	row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
X	col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
X
X	for (r = 0; r < MAP_HEIGHT; r += row_inc)
X	for (c = 0; c < MAP_WIDTH; c += col_inc)
X	print_pzoom_cell (pmap, vmap, r, c, row_inc, col_inc);
X
X	prompt (s);
X	(void) get_chx (); /* wait for user */
X	
X	(void) refresh ();
X}
X
X/*
XPrint a single cell of a pathmap in condensed format.
XWe average all squares in the cell and take the mod 10 value.
XSquares with a value of -1 are printed with '-', squares with
Xa value of INFINITY/2 are printed with 'P', and squares with
Xa value of INFINITY are printed with 'Z'.  Squares with a value
Xbetween P and Z are printed as U.
X*/
X
Xvoid
Xprint_pzoom_cell (pmap, vmap, row, col, row_inc, col_inc)
Xpath_map_t *pmap;
Xview_map_t *vmap;
Xint row, col;
Xint row_inc, col_inc;
X{
X	int r, c;
X	int sum, d;
X	char cell;
X
X	sum = 0;
X	d = 0; /* number of squares in cell */
X	
X	for (r = row; r < row + row_inc; r++)
X	for (c = col; c < col + col_inc; c++) {
X		sum += pmap[row_col_loc(r,c)].cost;
X		d += 1;
X	}
X	sum /= d;
X	
X	if (pmap[row_col_loc(row,col)].terrain == T_PATH) cell = '-';
X	else if (sum < 0) cell = '!';
X	else if (sum == INFINITY/2) cell = 'P';
X	else if (sum == INFINITY) cell = ' ';
X	else if (sum > INFINITY/2) cell = 'U';
X	else {
X		sum %= 36;
X		if (sum < 10) cell = sum + '0';
X		else cell = sum - 10 + 'a';
X	}
X	
X	if (cell == ' ')
X		print_zoom_cell (vmap, row, col, row_inc, col_inc);
X	else {
X		(void) move (row/row_inc + NUMTOPS, col/col_inc);
X		(void) addch ((chtype)cell);
X	}
X}
X
X/*
XDisplay the score off in the corner of the screen.
X*/
X
Xvoid
Xdisplay_score ()
X{
X	pos_str (0, cols-12, " User  Comp");
X	pos_str (1, cols-12, "%5d %5d", user_score, comp_score);
X}
//E*O*F display.c//

echo x - empire.h
sed -e 's/^X//' > "empire.h" << '//E*O*F empire.h//'
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/*
Xempire.h -- type and constant declarations
X*/
X
X#ifndef TRUE
X#define TRUE 1
X#define FALSE 0
X#endif
X
X#ifndef NULL
X#define NULL 0
X#endif
X
X/* Redefine some functions for portability. */
X
X#ifndef SYSV
X#define memcpy(dst,src,len) bcopy((src),(dst),(len))
X#define strchr(s,c) index(s,c)
Xtypedef char chtype;
X#define beep() (putchar('\7'))
X#define napms(d) (usleep((d)*1000))
X#else
Xchar *memset();
Xchar *memcpy();
X#define bzero(dst,len) memset(dst,0,len)
X#endif
X
Xtypedef unsigned char uchar;
X
X#define ASSERT(x) if (!(x)) assert ("x", __FILE__, __LINE__);
X#define ABORT assert ("aborting", __FILE__, __LINE__)
X
X/* directions one can move */
X#define NORTH 0
X#define NORTHEAST 1
X#define EAST 2
X#define SOUTHEAST 3
X#define SOUTH 4
X#define SOUTHWEST 5
X#define WEST 6
X#define NORTHWEST 7
X
X#define NUMTOPS 3 /* number of lines at top of screen for messages */
X#define NUMSIDES 6 /* number of lines at side of screen */
X#define STRSIZE 80 /* number of characters in a string */
X
X/* Information we maintain about cities. */
X
X#define NUM_CITY 70
X#define UNOWNED 0
X#define USER 1
X#define COMP 2
X
X/* Piece types. */
X#define ARMY 0
X#define FIGHTER 1
X#define PATROL 2
X#define DESTROYER 3
X#define SUBMARINE 4
X#define TRANSPORT 5
X#define CARRIER 6
X#define BATTLESHIP 7
X#define SATELLITE 8
X#define NUM_OBJECTS 9 /* number of defined objects */
X#define NOPIECE ((char)255) /* a 'null' piece */
X
X#define LIST_SIZE 5000 /* max number of pieces on board */
X
Xtypedef struct city_info {
X	long loc; /* location of city */
X	uchar owner; /* UNOWNED, USER, COMP */
X	long func[NUM_OBJECTS]; /* function for each object */
X	long work; /* units of work performed */
X	char prod; /* item being produced */
X} city_info_t;
X
X/*
XTypes of programmed movement.  Use negative numbers for special
Xfunctions, use positive numbers to move toward a specific location.
X*/
X
X#define NOFUNC -1	/* no programmed function */
X#define RANDOM -2	/* move randomly */
X#define SENTRY -3	/* sleep */
X#define FILL -4         /* fill transport */
X#define LAND -5         /* land fighter at city */
X#define EXPLORE -6      /* piece explores nearby */
X#define ARMYLOAD -7     /* army moves toward and boards a transport */
X#define ARMYATTACK -8   /* army looks for city to attack */
X#define TTLOAD -9       /* transport moves toward loading armies */
X#define REPAIR -10      /* ship moves toward port */
X#define WFTRANSPORT -11 /* army boards a transport */
X#define MOVE_N -12      /* move north */
X#define MOVE_NE -13     /* move northeast */
X#define MOVE_E -14      /* move east */
X#define MOVE_SE -15     /* move southeast */
X#define MOVE_S -16      /* move south */
X#define MOVE_SW -17     /* move southwest */
X#define MOVE_W -18      /* move west */
X#define MOVE_NW -19     /* move northwest */
X
X/* Index to list of function names. */
X#define FUNCI(x) (-(x)-1)
X
X/*
XMacro to convert a movement function into a direction.
X*/
X
X#define MOVE_DIR(a) (-(a)+MOVE_N)
X
X/*
XInformation we maintain about each piece.
X*/
X
Xtypedef struct { /* ptrs for doubly linked list */
X	struct piece_info *next; /* pointer to next in list */
X	struct piece_info *prev; /* pointer to prev in list */
X} link_t;
X
Xtypedef struct piece_info {
X	link_t piece_link; /* linked list of pieces of this type */
X	link_t loc_link; /* linked list of pieces at a location */
X	link_t cargo_link; /* linked list of cargo pieces */
X	int owner; /* owner of piece */
X	int type; /* type of piece */
X	long loc; /* location of piece */
X	long func; /* programmed type of movement */
X	short hits; /* hits left */
X	int moved; /* moves made */
X	struct piece_info *ship; /* pointer to containing ship */
X	struct piece_info *cargo; /* pointer to cargo list */
X	short count; /* count of items on board */
X	short range; /* current range (if applicable) */
X} piece_info_t;
X
X/*
XMacros to link and unlink an object from a doubly linked list.
X*/
X
X#define LINK(head,obj,list) { \
X	obj->list.prev = NULL; \
X	obj->list.next = head; \
X	if (head) head->list.prev = obj; \
X	head = obj; \
X}
X
X#define UNLINK(head,obj,list) { \
X	if (obj->list.next) \
X		obj->list.next->list.prev = obj->list.prev; \
X        if (obj->list.prev) \
X		obj->list.prev->list.next = obj->list.next; \
X        else head = obj->list.next; \
X	obj->list.next = NULL; /* encourage mem faults in buggy code */ \
X	obj->list.prev = NULL; \
X}
X
X/* macros to set map and list of an object */
X#define MAP(owner) ((owner) == USER ? user_map : comp_map)
X#define LIST(owner) ((owner) == USER ? user_obj : comp_obj)
X
X/* macro to step through adjacent cells */
X#define FOR_ADJ(loc,new_loc,i) for (i=0; (i<8 ? new_loc=loc+dir_offset[i],1 : 0); i++)
X#define FOR_ADJ_ON(loc,new_loc,i) FOR_ADJ(loc,new_loc,i) if (map[new_loc].on_board)
X
X/*
XWe maintain attributes for each piece.  Attributes are currently constant,
Xbut the way has been paved to allow user's to change attributes at the
Xbeginning of a game.
X*/
X
X#define INFINITY 1000000 /* a large number */
X
Xtypedef struct piece_attr {
X	char sname; /* eg 'C' */
X	char name[20]; /* eg "aircraft carrier" */
X	char nickname[20]; /* eg "carrier" */
X	char article[20]; /* eg "an aircraft carrier" */
X	char plural[20]; /* eg "aircraft carriers" */
X	char terrain[4]; /* terrain piece can pass over eg "." */
X	uchar build_time; /* time needed to build unit */
X	uchar strength; /* attack strength */
X	uchar max_hits; /* number of hits when completely repaired */
X	uchar speed; /* number of squares moved per turn */
X	uchar capacity; /* max objects that can be held */
X	long range; /* range of piece */
X} piece_attr_t;
X
X/*
XThere are 3 maps.  'map' describes the world as it actually
Xexists; it tells whether each map cell is land, water or a city;
Xit tells whether or not a square is on the board.
X
X'user_map' describes the user's view of the world.  'comp_map' describes
Xthe computer's view of the world.
X*/
X
X#define MAP_WIDTH 100
X#define MAP_HEIGHT 60
X#define MAP_SIZE (MAP_WIDTH * MAP_HEIGHT)
X
Xtypedef struct real_map { /* a cell of the actual map */
X	char contents; /* '+', '.', or '*' */
X	uchar on_board; /* TRUE iff on the board */
X	city_info_t *cityp; /* ptr to city at this location */
X	piece_info_t *objp; /* list of objects at this location */
X} real_map_t;
X
Xtypedef struct view_map { /* a cell of one player's world view */
X	char contents; /* '+', '.', '*', 'A', 'a', etc */
X	long seen; /* date when last updated */
X} view_map_t;
X
X/* Define information we maintain for a pathmap. */
X
Xtypedef struct {
X	int cost; /* total cost to get here */
X	int inc_cost; /* incremental cost to get here */
X	char terrain; /* T_LAND, T_WATER, T_UNKNOWN, T_PATH */
X} path_map_t;
X
X#define T_UNKNOWN 0
X#define T_PATH 1
X#define T_LAND 2
X#define T_WATER 4
X#define T_AIR (T_LAND | T_WATER)
X
X/* A record for counts we obtain when scanning a continent. */
X
Xtypedef struct {
X	int user_cities; /* number of user cities on continent */
X	int user_objects[NUM_OBJECTS];
X	int comp_cities;
X	int comp_objects[NUM_OBJECTS];
X	int size; /* size of continent in cells */
X	int unowned_cities; /* number of unowned cities */
X	int unexplored; /* unexplored territory */
X} scan_counts_t;
X
X/* Define useful constants for accessing sectors. */
X
X#define SECTOR_ROWS 5 /* number of vertical sectors */
X#define SECTOR_COLS 2 /* number of horizontal sectors */
X#define NUM_SECTORS (SECTOR_ROWS * SECTOR_COLS) /* total sectors */
X#define ROWS_PER_SECTOR ((MAP_HEIGHT+SECTOR_ROWS-1)/SECTOR_ROWS)
X#define COLS_PER_SECTOR ((MAP_WIDTH+SECTOR_COLS-1)/SECTOR_COLS)
X
X/* Information we need for finding a path for moving a piece. */
X
Xtypedef struct {
X	char city_owner; /* char that represents home city */
X	char *objectives; /* list of objectives */
X	int weights[11]; /* weight of each objective */
X} move_info_t;
X
X/* special weights */
X#define W_TT_BUILD -1 /* special cost for city building a tt */
X
X/* List of cells in the perimeter of our searching for a path. */
X
Xtypedef struct {
X	long len; /* number of items in list */
X	long list[MAP_SIZE]; /* list of locations */
X} perimeter_t;
//E*O*F empire.h//

echo shar: End of archive 3 \(of 6\).
cp /dev/null ark3isdone
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

chuck@amdahl.uts.amdahl.com (Charles Simmons) (04/11/88)

# 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:
# usermove.c game.c

echo x - usermove.c
sed -e 's/^X//' > "usermove.c" << '//E*O*F usermove.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/*
Xusermove.c -- Let the user move her troops.
X*/
X
X#include <curses.h>
X#include <ctype.h>
X#include "empire.h"
X#include "extern.h"
X
Xvoid
Xuser_move () {
X	void piece_move();
X
X	int i, j, sec, sec_start;
X	piece_info_t *obj, *next_obj;
X	int prod;
X
X	/* First we loop through objects to update the user's view
X	of the world and perform any other necessary processing.
X	We would like to have the world view up to date before
X	asking the user any questions.  This means that we should
X	also scan through all cities before possibly asking the
X	user what to produce in each city. */
X
X	for (i = 0; i < NUM_OBJECTS; i++)
X	for (obj = user_obj[i]; obj != NULL; obj = obj->piece_link.next) {
X		obj->moved = 0; /* nothing moved yet */
X		scan (user_map, obj->loc); /* refresh user's view of world */
X	}
X
X	/* produce new hardware */
X	for (i = 0; i < NUM_CITY; i++)
X	    if (city[i].owner == USER) {
X		scan (user_map, city[i].loc);
X		prod = city[i].prod;
X
X		if (prod == NOPIECE) { /* need production? */
X			set_prod (&(city[i])); /* ask user what to produce */
X		}
X		else if (city[i].work++ >= (long)piece_attr[prod].build_time) {
X			comment ("City at %d has completed %s.",
X				city[i].loc, piece_attr[prod].article);
X
X			produce (&city[i]);
X			/* produce should set object.moved to 0 */
X		}
X	}
X
X	/* move all satellites */
X	for (obj = user_obj[SATELLITE]; obj != NULL; obj = next_obj) {
X		next_obj = obj->piece_link.next;
X		move_sat (obj);
X	}
X	
X	sec_start = cur_sector (); /* get currently displayed sector */
X	if (sec_start == -1) sec_start = 0;
X
X	/* loop through sectors, moving every piece in the sector */
X	for (i = sec_start; i < sec_start + NUM_SECTORS; i++) {
X		sec = i % NUM_SECTORS;
X		sector_change (); /* allow screen to be redrawn */
X
X		for (j = 0; j < NUM_OBJECTS; j++) /* loop through obj lists */
X		for (obj = user_obj[move_order[j]]; obj != NULL;
X			obj = next_obj) { /* loop through objs in list */
X			next_obj = obj->piece_link.next;
X
X			if (!obj->moved) /* object not moved yet? */
X			if (loc_sector (obj->loc) == sec) /* object in sector? */
X			piece_move (obj); /* yup; move the object */
X		}
X		if (cur_sector () == sec) { /* is sector displayed? */
X			print_sector_u (sec); /* make screen up-to-date */
X			(void) refresh (); /* show it to the user */
X		}
X	}
X	if (save_movie) save_movie_screen ();
X}
X
X/*
XMove a piece.  We loop until all the moves of a piece are made.  Within
Xthe loop, we first awaken the piece if it is adjacent to an enemy piece.
XThen we attempt to handle any preprogrammed function for the piece.  If
Xthe piece has not moved after this, we ask the user what to do.
X*/
X
Xvoid
Xpiece_move (obj)
Xpiece_info_t *obj;
X{
X	void move_random(), move_fill(), move_land(), move_explore();
X	void move_path(), move_dir(), move_armyload(), ask_user();
X	void move_armyattack(), move_ttload(), move_repair();
X	void move_transport();
X
X	int changed_loc;
X	int speed, max_hits;
X	int saved_moves;
X	int need_input;
X	long saved_loc;
X	city_info_t *cityp;
X
X	/* set func for piece if on city */
X	cityp = find_city (obj->loc);
X	if (cityp != NULL)
X		if (cityp->func[obj->type] != NOFUNC)
X			obj->func = cityp->func[obj->type];
X
X	changed_loc = FALSE; /* not changed yet */
X	speed = piece_attr[obj->type].speed;
X	max_hits = piece_attr[obj->type].max_hits;
X	need_input = FALSE; /* don't require user input yet */
X
X	while (obj->moved < obj_moves (obj)) {
X		saved_moves = obj->moved; /* save moves made */
X		saved_loc = obj->loc; /* remember starting location */
X
X		if (awake (obj) || need_input){ /* need user input? */
X			ask_user (obj);
X			topini (); /* clear info lines */
X			display_loc_u (obj->loc); /* let user see result */
X			(void) refresh ();
X			need_input = FALSE; /* we got it */
X		}
X		
X		if (obj->moved == saved_moves) /* user set function? */
X		switch (obj->func) { /* handle preprogrammed function */
X		case NOFUNC:    break;
X		case RANDOM:    move_random (obj); break;
X		case SENTRY:    obj->moved = speed; break;
X		case FILL:      move_fill (obj); break;
X		case LAND:      move_land (obj); break;
X		case EXPLORE:   move_explore (obj); break;
X		case ARMYLOAD:  move_armyload (obj); break;
X		case ARMYATTACK:move_armyattack (obj); break;
X		case TTLOAD:    move_ttload (obj); break;
X		case REPAIR:    move_repair (obj); break;
X		case WFTRANSPORT: move_transport (obj); break;
X
X		case MOVE_N:
X		case MOVE_NE:
X		case MOVE_E:
X		case MOVE_SE:
X		case MOVE_S:
X		case MOVE_SW:
X		case MOVE_W:
X		case MOVE_NW:
X			move_dir (obj); break;
X
X		default: move_path (obj); break;
X		}
X
X		if (obj->moved == saved_moves) need_input = TRUE;
X		
X		/* handle fighters specially.  If in a city or carrier, turn
X		is over and reset range to max.  Otherwise, if
X		range = 0, fighter crashes and burns and turn is over. */
X
X		if (obj->type == FIGHTER && obj->hits > 0) {
X			if ((user_map[obj->loc].contents == 'O'
X			  || user_map[obj->loc].contents == 'C')
X			&& obj->moved > 0) {
X				obj->range = piece_attr[FIGHTER].range;
X				obj->moved = speed;
X				obj->func = NOFUNC;
X				comment ("Landing confirmed.");
X			}
X			else if (obj->range == 0) {
X				comment ("Fighter at %d crashed and burned.",
X					obj->loc);
X				kill_obj (obj, obj->loc);
X			}
X		}
X
X		if (saved_loc != obj->loc) changed_loc = TRUE;
X	}
X	/* if a boat is in port, damaged, and never moved, fix some damage */
X	if (obj->hits > 0 /* still alive? */
X		&& !changed_loc /* object never changed location? */
X		&& obj->type != ARMY && obj->type != FIGHTER /* it is a boat? */
X		&& obj->hits < max_hits /* it is damaged? */
X		&& user_map[obj->loc].contents == 'O') /* it is in port? */
X	obj->hits++; /* fix some damage */
X}
X
X/*
XMove a piece at random.  We create a list of empty squares to which
Xthe piece can move.  If there are none, we do nothing, otherwise we 
Xmove the piece to a random adjacent square.
X*/
X
Xvoid move_random (obj)
Xpiece_info_t *obj;
X{
X	long loc_list[8];
X	int i, nloc;
X	long loc;
X
X	nloc = 0;
X
X	for (i = 0; i < 8; i++) {
X		loc = obj->loc + dir_offset[i];
X		if (good_loc (obj, loc)) {
X			loc_list[nloc] = loc; /* remember this location */
X			nloc++; /* count locations we can move to */
X		}
X	}
X	if (nloc == 0) return; /* no legal move */
X	i = irand ((long)nloc-1); /* choose random direction */
X	move_obj (obj, loc_list[i]); /* move the piece */
X}
X
X/*
XHave a piece explore.  We look for the nearest unexplored territory
Xwhich the piece can reach and have to piece move toward the
Xterritory.
X*/
X
Xvoid move_explore (obj)
Xpiece_info_t *obj;
X{
X	path_map_t path_map[MAP_SIZE];
X	long loc;
X	char *terrain;
X
X	switch (obj->type) {
X	case ARMY:
X		loc = vmap_find_lobj (path_map, user_map, obj->loc, &user_army);
X		terrain = "+";
X		break;
X	case FIGHTER:
X		loc = vmap_find_aobj (path_map, user_map, obj->loc, &user_fighter);
X		terrain = "+.O";
X		break;
X	default:
X		loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship);
X		terrain = ".O";
X		break;
X	}
X	
X	if (loc == obj->loc) return; /* nothing to explore */
X
X	if (user_map[loc].contents == ' ' && path_map[loc].cost == 2)
X		vmap_mark_adjacent (path_map, obj->loc);
X	else vmap_mark_path (path_map, user_map, loc);
X
X	loc = vmap_find_dir (path_map, user_map, obj->loc, terrain, " ");
X	if (loc != obj->loc) move_obj (obj, loc);
X}
X
X/*
XMove an army onto a transport when it arrives.  We scan around the
Xarmy to find a non-full transport.  If one is present, we move the
Xarmy to the transport and waken the army.
X*/
X
Xvoid
Xmove_transport (obj)
Xpiece_info_t *obj;
X{
X	long loc;
X
X	/* look for an adjacent transport */
X	loc = find_transport (USER, obj->loc);
X	
X	if (loc != obj->loc) {
X		move_obj (obj, loc);
X		obj->func = NOFUNC;
X	}
X	else obj->moved = piece_attr[obj->type].speed;
X}
X
X/*
XMove an army toward the nearest loading transport.
XIf there is an adjacent transport, move the army onto
Xthe transport, and awaken the army.
X*/
X
Xstatic view_map_t amap[MAP_SIZE];
X
Xvoid
Xmove_armyload (obj)
Xpiece_info_t *obj;
X{
X	long loc;
X	piece_info_t *p;
X	int i;
X
X	ABORT;
X	
X	/* look for an adjacent transport */
X	loc = find_transport (USER, obj->loc);
X
X	if (loc != obj->loc) {
X		move_obj (obj, loc);
X		obj->func = NOFUNC;
X	}
X	else { /* look for nearest non-full transport */
X		(void) memcpy (amap, user_map, sizeof (view_map_t) * MAP_SIZE);
X
X		/* mark loading transports or cities building transports */
X		for (p = user_obj[TRANSPORT]; p; p = p->piece_link.next)
X		if (p->count < obj_capacity (p)) /* not full? */
X		amap[p->loc].contents = '$';
X		
X		for (i = 0; i < NUM_CITY; i++)
X		if (city[i].owner == USER && city[i].prod == TRANSPORT)
X		amap[city[i].loc].contents = '$';
X	}
X}
X		
X/*
XMove an army toward an attackable city or enemy army.
X*/
X
Xvoid
Xmove_armyattack (obj)
Xpiece_info_t *obj;
X{
X	path_map_t path_map[MAP_SIZE];
X	long loc;
X
X	ASSERT (obj->type == ARMY);
X
X	loc = vmap_find_lobj (path_map, user_map, obj->loc, &user_army_attack);
X	
X	if (loc == obj->loc) return; /* nothing to attack */
X
X	vmap_mark_path (path_map, user_map, loc);
X
X	loc = vmap_find_dir (path_map, user_map, obj->loc, "+", "X*a");
X	if (loc != obj->loc) move_obj (obj, loc);
X}
X
Xvoid
Xmove_ttload (obj)
Xpiece_info_t *obj;
X{
X	ABORT;
X	obj = obj;
X}
X
X/*
XMove a ship toward port.  If the ship is healthy, wake it up.
X*/
X
Xvoid
Xmove_repair (obj)
Xpiece_info_t *obj;
X{
X	path_map_t path_map[MAP_SIZE];
X	long loc;
X
X	ASSERT (obj->type > FIGHTER);
X	
X	if (obj->hits == piece_attr[obj->type].max_hits) {
X		obj->func = NOFUNC;
X		return;
X	}
X	
X	if (user_map[obj->loc].contents == 'O') { /* it is in port? */
X		obj->moved += 1;
X		return;
X	}
X
X	loc = vmap_find_wobj (path_map, user_map, obj->loc, &user_ship_repair);
X	
X	if (loc == obj->loc) return; /* no reachable city */
X
X	vmap_mark_path (path_map, user_map, loc);
X
X	/* try to be next to ocean to avoid enemy pieces */
X	loc = vmap_find_dir (path_map, user_map, obj->loc, ".O", ".");
X	if (loc != obj->loc) move_obj (obj, loc);
X}
X
X/*
XHere we have a transport or carrier waiting to be filled.  If the
Xobject is not full, we set the move count to its maximum value.
XOtherwise we awaken the object.
X*/
X
Xvoid move_fill (obj)
Xpiece_info_t *obj;
X{
X	if (obj->count == obj_capacity (obj)) /* full? */
X		obj->func = NOFUNC; /* awaken full boat */
X	else obj->moved = piece_attr[obj->type].speed;
X}
X
X/*
XHere we have a piece that wants to land at the nearest carrier or
Xowned city.  We scan through the lists of cities and carriers looking
Xfor the closest one.  We then move toward that item's location.
XThe nearest landing field must be within the object's range.
X*/
X
Xvoid
Xmove_land (obj)
Xpiece_info_t *obj;
X{
X	void move_to_dest();
X
X	long best_dist, best_loc;
X	long new_dist;
X	piece_info_t *p;
X
X	best_dist = find_nearest_city (obj->loc, USER, &best_loc);
X
X	for (p = user_obj[CARRIER]; p != NULL; p = p->piece_link.next) {
X		new_dist = dist (obj->loc, p->loc);
X		if (new_dist < best_dist) {
X			best_dist = new_dist;
X			best_loc = p->loc;
X		}
X	}
X	if (best_dist == 0) obj->moved += 1; /* fighter is on a city */
X	
X	else if (best_dist <= obj->range)
X		move_to_dest (obj, best_loc);
X		
X	else obj->func = NOFUNC; /* can't reach city or carrier */
X}
X
X/*
XMove a piece in the specified direction if possible.
XIf the object is a fighter which has travelled for half its range,
Xwe wake it up.
X*/
X
Xvoid move_dir (obj)
Xpiece_info_t *obj;
X{
X	long loc;
X	int dir;
X
X	dir = MOVE_DIR (obj->func);
X	loc = obj->loc + dir_offset[dir];
X
X	if (good_loc (obj, loc))
X		move_obj (obj, loc);
X}
X
X/*
XMove a piece toward a specified destination if possible.  For each
Xdirection, we see if moving in that direction would bring us closer
Xto our destination, and if there is nothing in the way.  If so, we
Xmove in the first direction we find.
X*/
X
Xvoid move_path (obj)
Xpiece_info_t *obj;
X{
X	if (obj->loc == obj->func)
X		obj->func = NOFUNC;
X	else move_to_dest (obj, obj->func);
X}
X
X/*
XMove a piece toward a specific destination.  We first map out
Xthe paths to the destination, if we can't get there, we return.
XThen we mark the paths to the destination.  Then we choose a
Xmove.
X*/
X
Xvoid move_to_dest (obj, dest)
Xpiece_info_t *obj;
Xlong dest;
X{
X	path_map_t path_map[MAP_SIZE];
X	int fterrain;
X	char *mterrain;
X	long new_loc;
X	
X	switch (obj->type) {
X	case ARMY:
X		fterrain = T_LAND;
X		mterrain = "+";
X		break;
X	case FIGHTER:
X		fterrain = T_AIR;
X		mterrain = "+.O";
X		break;
X	default:
X		fterrain = T_WATER;
X		mterrain = ".O";
X		break;
X	}
X	
X	new_loc = vmap_find_dest (path_map, user_map, obj->loc, dest,
X                                  USER, fterrain);
X	if (new_loc == obj->loc) return; /* can't get there */
X	
X	vmap_mark_path (path_map, user_map, dest);
X	new_loc = vmap_find_dir (path_map, user_map, obj->loc, mterrain, " .");
X	if (new_loc == obj->loc) return; /* can't move ahead */
X	ASSERT (good_loc (obj, new_loc));
X	move_obj (obj, new_loc); /* everything looks good */
X}
X
X/*
XAsk the user to move her piece.
X*/
X
Xvoid ask_user (obj)
Xpiece_info_t *obj;
X{
X	void user_skip(), user_fill(), user_dir(), user_set_dir();
X	void user_wake(), user_set_city_func(), user_cancel_auto();
X	void user_redraw(), user_random(), user_land(), user_sentry();
X	void user_help(), reset_func(), user_explore();
X	void user_build(), user_transport();
X	void user_armyattack(), user_repair();
X
X	char c;
X
X    for (;;) {
X	display_loc_u (obj->loc); /* display piece to move */
X	describe_obj (obj); /* describe object to be moved */
X	display_score (); /* show current score */
X	display_loc_u (obj->loc); /* reposition cursor */
X
X	c = get_chx (); /* get command from user (no echo) */
X	switch (c) {
X	case 'Q': user_dir (obj, NORTHWEST); return;
X	case 'W': user_dir (obj, NORTH); return;
X	case 'E': user_dir (obj, NORTHEAST); return;
X	case 'D': user_dir (obj, EAST); return;
X	case 'C': user_dir (obj, SOUTHEAST); return;
X	case 'X': user_dir (obj, SOUTH); return;
X	case 'Z': user_dir (obj, SOUTHWEST); return;
X	case 'A': user_dir (obj, WEST); return;
X
X	case 'J': edit (obj->loc); reset_func (obj); return;
X	case 'V': user_set_city_func (obj); reset_func (obj); return;
X	
X	case ' ': user_skip (obj); return;
X	case 'F': user_fill (obj); return;
X	case 'I': user_set_dir (obj); return;
X	case 'R': user_random (obj); return;
X	case 'S': user_sentry (obj); return;
X	case 'L': user_land (obj); return;
X	case 'G': user_explore (obj); return;
X	case 'T': user_transport (obj); return;
X	case 'U': user_repair (obj); return;
X	case 'Y': user_armyattack (obj); return;
X
X	case 'B': user_build (obj); break;
X	case 'H': user_help (); break;
X	case 'K': user_wake (obj); break;
X	case 'O': user_cancel_auto (); break;
X	case '\014':
X	case 'P': user_redraw (); break;
X	case '?': describe_obj (obj); break;
X
X	default: (void) beep ();
X	}
X    }
X}
X
X/*
XHere, if the passed object is on a city, we assign
Xthe city's function to the object.  However, we then awaken the
Xobject if necessary because the user only changed the city
Xfunction, and did not tell us what to do with the object.
X*/
X
Xvoid
Xreset_func (obj)
Xpiece_info_t *obj;
X{
X	city_info_t *cityp;
X	
X	cityp = find_city (obj->loc);
X
X	if (cityp != NULL)
X	if (cityp->func[obj->type] != NOFUNC) {
X		obj->func = cityp->func[obj->type];
X		(void) awake (obj);
X	} 
X}
X
X/*
XIncrement the number of moves a piece has used.  If the piece
Xis an army and the army is in a city, move the army to
Xthe city.
X*/
X
Xvoid
Xuser_skip (obj)
Xpiece_info_t *obj;
X{
X	void move_army_to_city();
X
X	if (obj->type == ARMY && user_map[obj->loc].contents == 'O')
X		move_army_to_city (obj, obj->loc);
X	else obj->moved++;
X}
X
X/*
XPut an object in FILL mode.  The object must be either a transport
Xor carrier.  If not, we beep at the user.
X*/
X
Xvoid
Xuser_fill (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type != TRANSPORT && obj->type != CARRIER) (void) beep ();
X	else obj->func = FILL;
X}
X
X/*
XPrint out help information.
X*/
X
Xvoid
Xuser_help () {
X	char c;
X
X	help (help_user, user_lines);
X	prompt ("Press any key to continue: ");
X	c = get_chx ();
X	c = c; /* keep lint happy */
X}
X
X/*
XSet an object's function to move in a certain direction.
X*/
X
Xvoid
Xuser_set_dir (obj)
Xpiece_info_t *obj;
X{
X	char c;
X
X	c = get_chx ();
X	switch (c) {
X	case 'Q': obj->func = MOVE_NW; break;
X	case 'W': obj->func = MOVE_N ; break;
X	case 'E': obj->func = MOVE_NE; break;
X	case 'D': obj->func = MOVE_E ; break;
X	case 'C': obj->func = MOVE_SE; break;
X	case 'X': obj->func = MOVE_S ; break;
X	case 'Z': obj->func = MOVE_SW; break;
X	case 'A': obj->func = MOVE_W ; break;
X	default: (void) beep (); break;
X	}
X}
X
X/*
XWake up the current piece.
X*/
X
Xvoid
Xuser_wake (obj)
Xpiece_info_t *obj;
X{
X	obj->func = NOFUNC;
X}
X
X/*
XSet the piece's func to random.  
X*/
X
Xvoid
Xuser_random (obj)
Xpiece_info_t *obj;
X{
X	obj->func = RANDOM;
X}
X
X/*
XSet a piece's function to sentry.
X*/
X
Xvoid
Xuser_sentry (obj)
Xpiece_info_t *obj;
X{
X	obj->func = SENTRY;
X}
X
X/*
XSet a fighter's function to land at the nearest city.
X*/
X
Xvoid
Xuser_land (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type != FIGHTER) (void) beep ();
X	else obj->func = LAND;
X}
X
X/*
XSet the piece's func to explore.
X*/
X
Xvoid
Xuser_explore (obj)
Xpiece_info_t *obj;
X{
X	obj->func = EXPLORE;
X}
X
X/*
XSet an army's function to WFTRANSPORT.
X*/
X
Xvoid
Xuser_transport (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type != ARMY) (void) beep ();
X	else obj->func = WFTRANSPORT;
X}
X
X/*
XSet an army's function to ARMYATTACK.
X*/
X
Xvoid
Xuser_armyattack (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type != ARMY) (void) beep ();
X	else obj->func = ARMYATTACK;
X}
X
X/*
XSet a ship's function to REPAIR.
X*/
X
Xvoid
Xuser_repair (obj)
Xpiece_info_t *obj;
X{
X	if (obj->type == ARMY || obj->type == FIGHTER) (void) beep ();
X	else obj->func = REPAIR;
X}
X
X/*
XSet a city's function.
X*/
X
Xvoid
Xuser_set_city_func (obj)
Xpiece_info_t *obj;
X{
X	void e_city_fill(), e_city_explore(), e_city_stasis();
X	void e_city_wake(), e_city_random(), e_city_repair();
X	void e_city_attack();
X	
X	int type;
X	char e;
X	city_info_t *cityp;
X
X	cityp = find_city (obj->loc);
X	if (!cityp || cityp->owner != USER) {
X		(void) beep ();
X		return;
X	}
X
X	type = get_piece_name();
X	if (type == NOPIECE) {
X		(void) beep ();
X		return;
X	}
X	
X	e = get_chx ();
X	
X	switch (e) {
X	case 'F': /* fill */
X		e_city_fill (cityp, type);
X		break;
X	case 'G': /* explore */
X		e_city_explore (cityp, type);
X		break;
X	case 'I': /* directional stasis */
X		e_city_stasis (cityp, type);
X		break;
X	case 'K': /* turn off function */
X		e_city_wake (cityp, type);
X		break;
X	case 'R': /* make piece move randomly */
X		e_city_random (cityp, type);
X		break;
X	case 'U': /* repair ship */
X		e_city_repair (cityp, type);
X		break;
X	case 'Y': /* set army func to attack */
X		e_city_attack (cityp, type);
X		break;
X	default: /* bad command? */
X		(void) beep ();
X		break;
X	}
X}
X
X/*
XChange a city's production.
X*/
X
Xvoid
Xuser_build (obj)
Xpiece_info_t *obj;
X{
X	city_info_t *cityp;
X
X	if (user_map[obj->loc].contents != 'O') { /* no user city here? */
X		(void) beep ();
X		return;
X	}
X	cityp = find_city (obj->loc);
X	ASSERT (cityp != NULL);
X	set_prod (cityp);
X}
X
X/*
XMove a piece in the direction specified by the user.
XThis routine handles attacking objects.
X*/
X
Xvoid
Xuser_dir (obj, dir)
Xpiece_info_t *obj;
Xint dir;
X{
X	void user_dir_army(), user_dir_fighter(), user_dir_ship();
X
X	long loc;
X
X	loc = obj->loc + dir_offset[dir];
X
X	if (good_loc (obj, loc)) {
X		move_obj (obj, loc);
X		return;
X	}
X	if (!map[loc].on_board) {
X		error ("You cannot move to the edge of the world.");
X		delay ();
X		return;
X	}
X	switch (obj->type) {
X	case ARMY: user_dir_army (obj, loc); break;
X	case FIGHTER: user_dir_fighter (obj, loc); break;
X	default: user_dir_ship (obj, loc); break;
X	}
X}
X
X/*
XWe have an army that wants to attack something or move onto some
Xunreasonable terrain.  We check for errors, question the user if
Xnecessary, and attack if necessary.
X*/
X
Xvoid
Xuser_dir_army (obj, loc)
Xpiece_info_t *obj;
Xlong loc;
X{
X	void fatal();
X	
X	int enemy_killed;
X	
X	enemy_killed = FALSE;
X
X	if (user_map[loc].contents == 'O') /* attacking own city */
X		move_army_to_city (obj, loc);
X
X	else if (user_map[loc].contents == 'T') /* transport full? */
X		fatal (obj, loc,
X	"Sorry, sir.  There is no more room on the transport.  Do you insist? ",
X	"Your army jumped into the briny and drowned.");
X
X	else if (map[loc].contents == '.') { /* going for a swim? */
X		if (!getyn ( /* thanks to Craig Hansen for this next message */
X	"Troops can't walk on water, sir.  Do you really want to go to sea? "))
X		return;
X
X		if (user_map[obj->loc].contents == 'T')
X			comment ("Your army jumped into the briny and drowned.");
X
X		else if (user_map[loc].contents == '.')
X			comment ("Your army marched dutifully into the sea and drowned.");
X
X		else { /* attack something at sea */
X			enemy_killed = islower (user_map[loc].contents);
X			attack (obj, loc);
X	
X			if (obj->hits > 0) /* ship won? */
X			comment ("Your army regretfully drowns after its successful assault.");
X		}
X		if (obj->hits > 0) {
X			kill_obj (obj, loc);
X			if (enemy_killed) scan (comp_map, loc);
X		}
X	}
X		
X	else if (isupper (user_map[loc].contents)
X		&& user_map[loc].contents != 'X') { /* attacking self */
X		if (!getyn (
X	"Sir, those are our men!  Do you really want to attack them? "))
X		return;
X
X		attack (obj, loc);
X	}
X
X	else attack (obj, loc);
X}
X
X/*
XHere we have a fighter wanting to attack something.  There are only
Xthree cases:  attacking a city, attacking ourself, attacking the enemy.
X*/
X
Xvoid
Xuser_dir_fighter (obj, loc)
Xpiece_info_t *obj;
Xlong loc;
X{
X	if (map[loc].contents == '*')
X		fatal (obj, loc,
X	"That's never worked before, sir.  Do you really want to try? ",
X	"Your fighter was shot down.");
X
X	else if (isupper (user_map[loc].contents)) {
X		if (!getyn (
X	"Sir, those are our men!  Do you really want to attack them? "))
X		return;
X
X		attack (obj, loc);
X	}
X
X	else attack (obj, loc);
X}
X
X/*
XHere we have a ship attacking something, or trying to move on
Xshore.  Our cases are: moving ashore (and subcases), attacking
Xa city, attacking self, attacking enemy.
X*/
X	
Xvoid
Xuser_dir_ship (obj, loc)
Xpiece_info_t *obj;
Xlong loc;
X{
X	int enemy_killed;
X
X	enemy_killed = FALSE;
X
X	if (map[loc].contents == '*') {
X		(void) sprintf (jnkbuf, "Your %s broke up on shore.",
X				piece_attr[obj->type].name);
X
X		fatal (obj, loc,
X	"That's never worked before, sir.  Do you really want to try? ",
X			jnkbuf);
X	}
X
X	else if (map[loc].contents == '+') { /* moving ashore? */
X		if (!getyn (
X	"Ships need sea to float, sir.  Do you really want to go ashore? "))
X		return;
X
X		if (user_map[loc].contents == '+')
X			comment ("Your %s broke up on shore.",
X				 piece_attr[obj->type].name);
X
X		else { /* attack something on shore */
X			enemy_killed = islower (user_map[loc].contents);
X			attack (obj, loc);
X
X			if (obj->hits > 0) /* ship won? */
X				comment ("Your %s breaks up after its successful assault.",
X					 piece_attr[obj->type].name);
X		}
X		if (obj->hits > 0) {
X			kill_obj (obj, loc);
X			if (enemy_killed) scan (comp_map, loc);
X		}
X	}
X		
X	else if (isupper (user_map[loc].contents)) { /* attacking self */
X		if (!getyn (
X	"Sir, those are our men!  Do you really want to attack them? "))
X		return;
X
X		attack (obj, loc);
X	}
X
X	else attack (obj, loc);
X}
X
X/*
XHere a user wants to move an army to a city.  If the city contains
Xa non-full transport, we make the move.  Otherwise we ask the user
Xif she really wants to attack the city.
X*/
X
Xvoid
Xmove_army_to_city (obj, city_loc)
Xpiece_info_t *obj;
Xlong city_loc;
X{
X	piece_info_t *tt;
X
X	tt = find_nfull (TRANSPORT, city_loc);
X
X	if (tt != NULL) move_obj (obj, city_loc);
X
X	else fatal (obj, city_loc,
X	"That's our city, sir!  Do you really want to attack the garrison? ",
X	"Your rebel army was liquidated.");
X}
X
X/*
XCancel automove mode.
X*/
X
Xvoid
Xuser_cancel_auto () {
X	if (!automove)
X		comment ("Not in auto mode!");
X	else {
X		automove = FALSE;
X		comment ("Auto mode cancelled.");
X	}
X}
X
X/*
XRedraw the screen.
X*/
X
Xvoid
Xuser_redraw () {
X	redraw ();
X}
X
X/*
XAwaken an object if it needs to be.  Normally, objects are awakened
Xwhen they are next to an enemy object or an unowned city.  Armies
Xon troop transports are not awakened if they are surrounded by sea.
XWe return true if the object is now awake.  Objects are never
Xcompletely awoken here if their function is a destination.  But we
Xwill return TRUE if we want the user to have control.
X*/
X
Xint
Xawake (obj)
Xpiece_info_t *obj;
X{
X	int i;
X	char c;
X	long t;
X
X	if (obj->type == ARMY && vmap_at_sea (user_map, obj->loc)) {
X	    obj->moved = piece_attr[ARMY].range;
X	    return (FALSE);
X	}
X	if (obj->func == NOFUNC) return (TRUE); /* object is awake */
X	
X	if (obj->type == FIGHTER /* wake fighters */
X	    && obj->func != LAND /* that aren't returning to base */
X	    && obj->func < 0 /* and which don't have a path */
X	    && obj->range <= find_nearest_city (obj->loc, USER, &t) + 2) {
X		obj->func = NOFUNC; /* wake piece */
X		return (TRUE);
X	}
X	for (i = 0; i < 8; i++) { /* for each surrounding cell */
X		c = user_map[obj->loc+dir_offset[i]].contents;
X
X		if (islower (c) || c == '*' || c == 'X') {
X			if (obj->func < 0) obj->func = NOFUNC; /* awaken */
X			return (TRUE);
X		}
X	}
X	return (FALSE);
X}
X
X/*
XQuestion the user about a fatal move.  If the user responds 'y',
Xprint out the response and kill the object.
X*/
X
Xvoid
Xfatal (obj, loc, message, response)
Xpiece_info_t *obj;
Xlong loc;
Xchar *message;
Xchar *response;
X{
X	if (getyn (message)) {
X		comment (response);
X		kill_obj (obj, loc);
X	}
X}
//E*O*F usermove.c//

echo x - game.c
sed -e 's/^X//' > "game.c" << '//E*O*F game.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/*
Xgame.c -- Routines to initialize, save, and restore a game.
X*/
X
X#ifdef SYSV
X#include <string.h>
X#else
X#include <strings.h>
X#endif
X
X#include <ctype.h>
X#include <curses.h>
X#include "empire.h"
X#include "extern.h"
X
X/*
XInitialize a new game.  Here we generate a new random map, put cities
Xon the map, select cities for each opponent, and zero out the lists of
Xpieces on the board.
X*/
X
Xvoid init_game () {
X	void make_map(), place_cities();
X
X	long i;
X
X	kill_display (); /* nothing on screen */
X	automove = FALSE;
X	resigned = FALSE;
X	debug = FALSE;
X	print_debug = FALSE;
X	print_vmap = FALSE;
X	trace_pmap = FALSE;
X	save_movie = FALSE;
X	win = 0;
X	date = 0; /* no date yet */
X	user_score = 0;
X	comp_score = 0;
X	
X	for (i = 0; i < MAP_SIZE; i++) {
X		user_map[i].contents = ' '; /* nothing seen yet */
X		user_map[i].seen = 0;
X		comp_map[i].contents = ' ';
X		comp_map[i].seen = 0;
X	}
X	for (i = 0; i < NUM_OBJECTS; i++) {
X		user_obj[i] = NULL;
X		comp_obj[i] = NULL;
X	}
X	free_list = NULL; /* nothing free yet */
X	for (i = 0; i < LIST_SIZE; i++) { /* for each object */
X		piece_info_t *obj = &(object[i]);
X		obj->hits = 0; /* mark object as dead */
X		obj->owner = UNOWNED;
X		LINK (free_list, obj, piece_link); 
X	}
X
X	make_map (); /* make land and water */
X
X	do {
X		for (i = 0; i < MAP_SIZE; i ++) { /* remove cities */
X			if (map[i].contents == '*')
X				map[i].contents = '+'; /* land */
X		}
X		place_cities (); /* place cities on map */
X	} while (!select_cities ()); /* choose a city for each player */
X}
X
X/*
XCreate a map.  To do this, we first randomly assign heights to each
Xmap location.  Then we smooth these heights.  The more we smooth,
Xthe better the land and water will clump together.  Then we decide
Xhow high the land will be.  We attempt to choose enough land to meet
Xsome required percentage.
X
XThere are two parameters to this algorithm:  the amount we will smooth,
Xand the ratio of land to water.  The user can provide these numbers
Xat program start up.
X*/
X
X#define MAX_HEIGHT 999	/* highest height */
X
X/* these arrays give some compilers problems when they are automatic */
Xstatic int height[2][MAP_SIZE];
Xstatic int height_count[MAX_HEIGHT+1];
X
Xvoid make_map () {
X	int from, to, k;
X	long i, j, sum, loc;
X
X	for (i = 0; i < MAP_SIZE; i++) /* fill map with random sand */
X		height[0][i] = irand (MAX_HEIGHT);
X
X	from = 0;
X	to = 1;
X	for (i = 0; i < SMOOTH; i++) { /* smooth the map */
X	    for (j = 0; j < MAP_SIZE; j++) {
X		sum = height[from][j];
X		for (k = 0; k < 8; k++) {
X			loc = j + dir_offset[k];
X			/* edges get smoothed in a wierd fashion */
X			if (loc < 0 || loc >= MAP_SIZE) loc = j;
X			sum += height[from][loc];
X		}
X		height[to][j] = sum / 9;
X	    }
X	    k = to; /* swap to and from */
X	    to = from;
X	    from = k;
X	}
X
X	/* count the number of cells at each height */
X	for (i = 0; i <= MAX_HEIGHT; i++)
X		height_count[i] = 0;
X
X	for (i = 0; i <= MAP_SIZE; i++)
X		height_count[height[from][i]]++;
X
X	/* find the water line */
X	loc = MAX_HEIGHT; /* default to all water */
X	sum = 0;
X	for (i = 0; i <= MAX_HEIGHT; i++) {
X		sum += height_count[i];
X		if (sum * 100 / MAP_SIZE > WATER_RATIO && sum >= NUM_CITY) {
X			loc = i; /* this is last height that is water */
X			break;
X		}
X	}
X
X	/* mark the land and water */
X	for (i = 0; i < MAP_SIZE; i ++) {
X		if (height[from][i] > loc)
X			map[i].contents = '+'; /* land */
X		else map[i].contents = '.'; /* water */
X
X		map[i].objp = NULL; /* nothing in cell yet */
X		map[i].cityp = NULL;
X
X		j = loc_col (i);
X		k = loc_row (i);
X
X		map[i].on_board = !(j == 0 || j == MAP_WIDTH-1 
X				 || k == 0 || k == MAP_HEIGHT-1);
X	}
X}
X
X/*
XRandomly place cities on the land.  There is a minimum distance that
Xshould exist between cities.  We maintain a list of acceptable land cells
Xon which a city may be placed.  We randomly choose elements from this
Xlist until all the cities are placed.  After each choice of a land cell
Xfor a city, we remove land cells which are too close to the city.
X*/
X
X/* avoid compiler problems with large automatic arrays */
Xstatic long land[MAP_SIZE];
X
Xvoid place_cities () {
X	long regen_land(), remove_land();
X
X	long placed, i, loc;
X	long num_land;
X
X	num_land = 0; /* nothing in land array yet */
X	placed = 0; /* nothing placed yet */
X	while (placed < NUM_CITY) {
X		while (num_land == 0) num_land = regen_land (placed);
X		i = irand (num_land-1); /* select random piece of land */
X		loc = land[i];
X		
X		city[placed].loc = loc;
X		city[placed].owner = UNOWNED;
X		city[placed].work = 0;
X		city[placed].prod = NOPIECE;
X		
X		for (i = 0; i < NUM_OBJECTS; i++)
X			city[placed].func[i] = NOFUNC; /* no function */
X			
X		map[loc].contents = '*';
X		map[loc].cityp = &(city[placed]);
X		placed++;
X
X		/* Now remove any land too close to selected land. */
X		num_land = remove_land (loc, num_land);
X	}
X}
X
X/*
XWhen we run out of available land, we recreate our land list.  We
Xput all land in the list, decrement the min_city_dist, and then
Xremove any land which is too close to a city.
X*/
X
Xlong regen_land (placed)
Xlong placed;
X{
X	long num_land;
X	long i;
X
X	num_land = 0;
X	for (i = 0; i < MAP_SIZE; i++) {
X		if (map[i].on_board && map[i].contents == '+') {
X			land[num_land] = i; /* remember piece of land */
X			num_land++; /* remember number of pieces */
X		}
X	}
X	if (placed > 0) { /* don't decrement 1st time */
X		MIN_CITY_DIST -= 1;
X		ASSERT (MIN_CITY_DIST >= 0);
X	}
X	for (i = 0; i < placed; i++) { /* for each placed city */
X		num_land = remove_land (city[i].loc, num_land);
X	}
X	return (num_land);
X}
X
X/*
XRemove land that is too close to a city.
X*/
X
Xlong remove_land (loc, num_land)
Xlong loc, num_land;
X{
X	long new, i;
X
X	new = 0; /* nothing kept yet */
X	for (i = 0; i < num_land; i++) {
X		if (dist (loc, land[i]) >= MIN_CITY_DIST) {
X			land[new] = land[i];
X			new++;
X		}
X	}
X	return (new);
X}
X
X/*
XHere we select the cities for the user and the computer.  Our choice of
Xcities will be heavily dependent on the difficulty level the user desires.
X
XOur algorithm will not guarantee that either player will eventually be
Xable to move armies to any continent on the map.  There may be continents
Xwhich are unreachable by sea.  Consider the case of an island in a lake.
XIf the lake has no shore cities, then there is no way for a boat to reach
Xthe island.  Our hope is that there will be enough water on the map, or enough
Xland, and that there will be enough cities, to make this case extremely rare.
X
XFirst we make a list of continents which contain at least two cities, one
Xor more of which is on the coast.  If there are no such continents, we return
XFALSE, and our caller should decide again where cities should be placed
Xon the map.  While making this list, we will rank the continents.  Our ranking
Xis based on the thought that shore cities are better than inland cities,
Xthat any city is very important, and that the land area of a continent
Xis mildly important.  Usually, we expect every continent to have a different
Xranking.  It will be unusual to have two continents with the same land area,
Xthe same number of shore cities, and the same number of inland cities.  When
Xthis is not the case, the first city encountered will be given the highest
Xrank.
X
XWe then rank pairs of continents.  We tell the user the number of different
Xranks, and ask the user what rank they want to use.  This is how the
Xuser specifies the difficulty level.  Using that pair, we have now decided
Xon a continent for each player.  We now choose a random city on each continent,
Xmaking sure the cities are not the same.
X*/
X
X#define MAX_CONT 10 /* most continents we will allow */
X
Xtypedef struct cont { /* a continent */
X	long value; /* value of continent */
X	int ncity; /* number of cities */
X	city_info_t * cityp[NUM_CITY]; /* pointer to city */
X} cont_t;
X
Xtypedef struct pair {
X	long value; /* value of pair for user */
X	int user_cont; /* index to user continent */
X	int comp_cont; /* index to computer continent */
X} pair_t;
X
Xstatic int marked[MAP_SIZE]; /* list of examine cells */
Xstatic int ncont; /* number of continents */
Xstatic cont_t cont_tab[MAX_CONT]; /* list of good continenets */
Xstatic int rank_tab[MAX_CONT]; /* indices to cont_tab in order of rank */
Xstatic pair_t pair_tab[MAX_CONT*MAX_CONT]; /* ranked pairs of continents */
X
Xint select_cities () {
X	void find_cont(), make_pair();
X
X	long compi, useri;
X	city_info_t *compp, *userp;
X	int comp_cont, user_cont;
X	int pair;
X
X	find_cont (); /* find and rank the continents */
X	if (ncont == 0) return (FALSE); /* there are no good continents */
X
X	make_pair (); /* create list of ranked pairs */
X
X	(void) sprintf (jnkbuf,
X		"Choose a difficulty level where 0 is easy and %d is hard: ",
X		ncont*ncont-1);
X
X	pair = get_range (jnkbuf, 0, ncont*ncont-1);
X	comp_cont = pair_tab[pair].comp_cont;
X	user_cont = pair_tab[pair].user_cont;
X
X	compi = irand ((long)cont_tab[comp_cont].ncity);
X	compp = cont_tab[comp_cont].cityp[compi];
X
X	do { /* select different user city */
X		useri = irand ((long)cont_tab[user_cont].ncity);
X		userp = cont_tab[user_cont].cityp[useri];
X	} while (userp == compp);
X
X	addprintf ("Your city is at %d.", userp->loc);
X	delay (); /* let user see output before we set_prod */
X
X	/* update city and map */
X	compp->owner = COMP;
X	compp->prod = ARMY;
X	compp->work = 0;
X	scan (comp_map, compp->loc);
X
X	userp->owner = USER;
X	userp->work = 0;
X	scan (user_map, userp->loc);
X	set_prod (userp);
X	return (TRUE);
X}
X
X/*
XFind all continents with 2 cities or more, one of which must be a shore
Xcity.  We rank the continents.
X*/
X
Xvoid find_cont () {
X	long i;
X	long mapi;
X
X	for (i = 0; i < MAP_SIZE; i++) marked[i] = 0; /* nothing marked yet */
X
X	ncont = 0; /* no continents found yet */
X	mapi = 0;
X
X	while (ncont < MAX_CONT)
X		if (!find_next (&mapi)) return; /* all found */
X}
X
X/*
XFind the next continent and insert it in the rank table.
XIf there are no more continents, we return false.
X*/
X
Xint find_next (mapi)
Xlong *mapi;
X{
X	long i, val;
X
X	for (;;) {
X		if (*mapi >= MAP_SIZE) return (FALSE);
X
X		if (!map[*mapi].on_board || marked[*mapi]
X			|| map[*mapi].contents == '.') *mapi += 1;
X		else if (good_cont (*mapi)) {
X			rank_tab[ncont] = ncont; /* insert cont in rank tab */
X			val = cont_tab[ncont].value;
X
X			for (i = ncont; i > 0; i--) { /* bubble up new rank */
X				if (val > cont_tab[rank_tab[i-1]].value) {
X					rank_tab[i] = rank_tab[i-1];
X					rank_tab[i-1] = ncont;
X				}
X				else break;
X			}
X			ncont++; /* count continents */
X			return (TRUE);
X		}
X	}
X}
X
X/*
XMap out the current continent.  We mark every piece of land on the continent,
Xcount the cities, shore cities, and land area of the continent.  If the
Xcontinent contains 2 cities and a shore city, we set the value of the
Xcontinent and return true.  Otherwise we return false.
X*/
X
Xstatic long ncity, nland, nshore;
X
Xint good_cont (mapi)
Xlong mapi;
X{
X	static void mark_cont();
X
X	long val;
X
X	ncity = 0; /* nothing seen yet */
X	nland = 0;
X	nshore = 0;
X
X	mark_cont (mapi);
X
X	if (nshore < 1 || ncity < 2) return (FALSE);
X
X	/* The first two cities, one of which must be a shore city,
X	don't contribute to the value.  Otherwise shore cities are
X	worth 3/2 an inland city.  A city is worth 1000 times as much
X	as land area. */
X
X	if (ncity == nshore) val = (nshore - 2) * 3;
X	else val = (nshore-1) * 3 + (ncity - nshore - 1) * 2;
X
X	val *= 1000; /* cities are worth a lot */
X	val += nland;
X	cont_tab[ncont].value = val;
X	cont_tab[ncont].ncity = ncity;
X	return (TRUE);
X}
X
X/*
XMark a continent.  This recursive algorithm marks the current square
Xand counts it if it is land or city.  If it is city, we also check
Xto see if it is a shore city, and we install it in the list of
Xcities for the continent.  We then examine each surrounding cell.
X*/
X
Xstatic void
Xmark_cont (mapi)
Xlong mapi;
X{
X	int i;
X
X	if (marked[mapi] || map[mapi].contents == '.'
X		|| !map[mapi].on_board) return;
X
X	marked[mapi] = 1; /* mark this cell seen */
X	nland++; /* count land on continent */
X
X	if (map[mapi].contents == '*') { /* a city? */
X		cont_tab[ncont].cityp[ncity] = map[mapi].cityp;
X		ncity++;
X		if (rmap_shore (mapi)) nshore++;
X	}
X
X	for (i = 0; i < 8; i++) /* look at surrounding squares */
X		mark_cont (mapi + dir_offset[i]);
X}
X
X/*
XCreate a list of pairs of continents in a ranked order.  The first
Xelement in the list is the pair which is easiest for the user to
Xwin with.  Our ranking is simply based on the difference in value
Xbetween the user's continent and the computer's continent.
X*/
X
Xvoid make_pair () {
X	int i, j, k, npair;
X	long val;
X
X	npair = 0; /* none yet */
X
X	for (i = 0; i < ncont; i++)
X	for (j = 0; j < ncont; j++) { /* loop through all continents */
X		val = cont_tab[i].value - cont_tab[j].value;
X		pair_tab[npair].value = val;
X		pair_tab[npair].user_cont = i;
X		pair_tab[npair].comp_cont = j;
X
X		for (k = npair; k > 0; k--) { /* bubble up new rank */
X			if (val > pair_tab[k-1].value) {
X				pair_tab[k] = pair_tab[k-1];
X				pair_tab[k-1].user_cont = i;
X				pair_tab[k-1].comp_cont = j;
X			}
X			else break;
X		}
X		npair++; /* count pairs */
X	}
X}
X
X/*
XSave a game.  We save the game in emp_save.dat.  Someday we may want
Xto ask the user for a file name.  If we cannot save the game, we will
Xtell the user why.
X*/
X
X/* macro to save typing; write an array, return if it fails */
X#define wbuf(buf) if (!xwrite (f, (char *)buf, sizeof (buf))) return
X#define wval(val) if (!xwrite (f, (char *)&val, sizeof (val))) return
X
Xvoid save_game () {
X	FILE *f; /* file to save game in */
X
X	f = fopen ("empsave.dat", "w"); /* open for output */
X	if (f == NULL) {
X		perror ("Cannot save empsave.dat");
X		return;
X	}
X	wbuf (map);
X	wbuf (comp_map);
X	wbuf (user_map);
X	wbuf (city);
X	wbuf (object);
X	wbuf (user_obj);
X	wbuf (comp_obj);
X	wval (free_list);
X	wval (date);
X	wval (automove);
X	wval (resigned);
X	wval (debug);
X	wval (win);
X	wval (save_movie);
X	wval (user_score);
X	wval (comp_score);
X
X	(void) fclose (f);
X	topmsg (3, "Game saved.");
X}
X
X/*
XRecover a saved game from emp_save.dat.
XWe return TRUE if we succeed, otherwise FALSE.
X*/
X
X#define rbuf(buf) if (!xread (f, (char *)buf, sizeof(buf))) return (FALSE);
X#define rval(val) if (!xread (f, (char *)&val, sizeof(val))) return (FALSE);
X
Xint restore_game () {
X	void read_embark();
X	
X	FILE *f; /* file to save game in */
X	long i;
X	piece_info_t **list;
X	piece_info_t *obj;
X
X	f = fopen ("empsave.dat", "r"); /* open for input */
X	if (f == NULL) {
X		perror ("Cannot open empsave.dat");
X		return (FALSE);
X	}
X	rbuf (map);
X	rbuf (comp_map);
X	rbuf (user_map);
X	rbuf (city);
X	rbuf (object);
X	rbuf (user_obj);
X	rbuf (comp_obj);
X	rval (free_list);
X	rval (date);
X	rval (automove);
X	rval (resigned);
X	rval (debug);
X	rval (win);
X	rval (save_movie);
X	rval (user_score);
X	rval (comp_score);
X
X	/* Our pointers may not be valid because of source
X	changes or other things.  We recreate them. */
X	
X	free_list = NULL; /* zero all ptrs */
X	for (i = 0; i < MAP_SIZE; i++) {
X		map[i].cityp = NULL;
X		map[i].objp = NULL;
X	}
X	for (i = 0; i < LIST_SIZE; i++) {
X		object[i].loc_link.next = NULL;
X		object[i].loc_link.prev = NULL;
X		object[i].cargo_link.next = NULL;
X		object[i].cargo_link.prev = NULL;
X		object[i].piece_link.next = NULL;
X		object[i].piece_link.prev = NULL;
X		object[i].ship = NULL;
X		object[i].cargo = NULL;
X	}
X	for (i = 0; i < NUM_OBJECTS; i++) {
X		comp_obj[i] = NULL;
X		user_obj[i] = NULL;
X	}
X	/* put cities on map */
X	for (i = 0; i < NUM_CITY; i++)
X		map[city[i].loc].cityp = &(city[i]);
X	
X	/* put pieces in free list or on map and in object lists */
X	for (i = 0; i < LIST_SIZE; i++) {
X		obj = &(object[i]);
X		if (object[i].owner == UNOWNED || object[i].hits == 0) {
X			LINK (free_list, obj, piece_link);
X		}
X		else {
X			list = LIST (object[i].owner);
X			LINK (list[object[i].type], obj, piece_link);
X			LINK (map[object[i].loc].objp, obj, loc_link);
X		}
X	}
X	
X	/* Embark armies and fighters. */
X	read_embark (user_obj[TRANSPORT], ARMY);
X	read_embark (user_obj[CARRIER], FIGHTER);
X	read_embark (comp_obj[TRANSPORT], ARMY);
X	read_embark (comp_obj[CARRIER], FIGHTER);
X	
X	(void) fclose (f);
X	kill_display (); /* what we had is no longer good */
X	topmsg (3, "Game restored from empsave.dat.");
X	return (TRUE);
X}
X	
X/*
XEmbark cargo on a ship.  We loop through the list of ships.
XWe then loop through the pieces at the ship's location until
Xthe ship has the same amount of cargo it previously had.
X*/
X
Xvoid read_embark (list, piece_type)
Xpiece_info_t *list;
Xint piece_type;
X{
X	void inconsistent();
X
X	piece_info_t *ship;
X	piece_info_t *obj;
X	int count;
X
X	for (ship = list; ship != NULL; ship = ship->piece_link.next) {
X		count = ship->count; /* get # of pieces we need */
X		if (count < 0) inconsistent ();
X		ship->count = 0; /* nothing on board yet */
X		for (obj = map[ship->loc].objp; obj && count;
X		    obj = obj->loc_link.next) {
X			if (obj->ship == NULL && obj->type == piece_type) {
X				embark (ship, obj);
X				count -= 1;
X			}
X		}
X		if (count) inconsistent ();
X	}
X}
X
Xvoid inconsistent () {
X	(void) printf ("empsave.dat is inconsistent.  Please remove it.\n");
X	exit (1);
X}
X
X/*
XWrite a buffer to a file.  If we cannot write everything, return FALSE.
XAlso, tell the user why the write did not work if it didn't.
X*/
X
Xint xwrite (f, buf, size)
XFILE *f;
Xchar *buf;
Xint size;
X{
X	int bytes;
X 
X	bytes = fwrite (buf, 1, size, f);
X	if (bytes == -1) {
X		perror ("Write to save file failed");
X		return (FALSE);
X	}
X	if (bytes != size) {
X		perror ("Cannot complete write to save file.\n");
X		return (FALSE);
X	}
X	return (TRUE);
X}
X
X/*
XRead a buffer from a file.  If the read fails, we tell the user why
Xand return FALSE.
X*/
X
Xint xread (f, buf, size)
XFILE *f;
Xchar *buf;
Xint size;
X{
X	int bytes;
X
X	bytes = fread (buf, 1, size, f);
X	if (bytes == -1) {
X		perror ("Read from save file failed");
X		return (FALSE);
X	}
X	if (bytes != size) {
X		perror ("Saved file is too short.\n");
X		return (FALSE);
X	}
X	return (TRUE);
X}
X
X/*
XSave a movie screen.  For each cell on the board, we write out
Xthe character that would appear on either the user's or the
Xcomputer's screen.  This information is appended to 'empmovie.dat'.
X*/
X
Xextern char city_char[];
Xstatic char mapbuf[MAP_SIZE];
X
Xvoid
Xsave_movie_screen ()
X{
X	FILE *f; /* file to save game in */
X	long i;
X	piece_info_t *p;
X
X	f = fopen ("empmovie.dat", "a"); /* open for append */
X	if (f == NULL) {
X		perror ("Cannot open empmovie.dat");
X		return;
X	}
X
X	for (i = 0; i < MAP_SIZE; i++) {
X		if (map[i].cityp) mapbuf[i] = city_char[map[i].cityp->owner];
X		else {
X			p = find_obj_at_loc (i);
X			
X			if (!p) mapbuf[i] = map[i].contents;
X			else if (p->owner == USER)
X				mapbuf[i] = piece_attr[p->type].sname;
X			else mapbuf[i] = tolower (piece_attr[p->type].sname);
X		}
X	}
X	wbuf (mapbuf);
X	(void) fclose (f);
X}
X
X/*
XReplay a movie.  We read each buffer from the file and
Xprint it using a zoomed display.
X*/
X
Xvoid
Xreplay_movie ()
X{
X	void print_movie_cell();
X
X	FILE *f; /* file to save game in */
X	int row_inc, col_inc;
X	int r, c;
X	int round;
X
X	
X	f = fopen ("empmovie.dat", "r"); /* open for input */
X	if (f == NULL) {
X		perror ("Cannot open empmovie.dat");
X		return;
X	}
X	round = 0;
X	clear_screen ();
X	for (;;) {
X		if (fread ((char *)mapbuf, 1, sizeof (mapbuf), f) != sizeof (mapbuf)) break;
X		round += 1;
X		
X		stat_display (mapbuf, round);
X		
X		row_inc = (MAP_HEIGHT + lines - NUMTOPS - 1) / (lines - NUMTOPS);
X		col_inc = (MAP_WIDTH + cols - 1) / (cols - 1);
X	
X		for (r = 0; r < MAP_HEIGHT; r += row_inc)
X		for (c = 0; c < MAP_WIDTH; c += col_inc)
X		print_movie_cell (mapbuf, r, c, row_inc, col_inc);
X		
X		(void) refresh ();
X		delay ();
X	}
X	(void) fclose (f);
X}
X
X/*
XDisplay statistics about the game.  At the top of the screen we
Xprint:
X
Xnn O  nn A  nn F  nn P  nn D  nn S  nn T  nn C  nn B  nn Z  xxxxx
Xnn X  nn a  nn f  nn p  nn d  nn s  nn t  nn c  nn b  nn z  xxxxx
X
XThere may be objects in cities and boats that aren't displayed.
XThe "xxxxx" field is the cumulative cost of building the hardware.
X*/
X
X/* in declared order, with city first */
Xstatic char *pieces = "OAFPDSTCBZXafpdstcbz";
X
Xstat_display (mbuf, round)
Xchar *mbuf;
Xint round;
X{
X	long i;
X	int counts[2*NUM_OBJECTS+2];
X	int user_cost, comp_cost;
X	char *p;
X	
X	(void) bzero ((char *)counts, sizeof (counts));
X	
X	for (i = 0; i < MAP_SIZE; i++) {
X		p = strchr (pieces, mbuf[i]);
X		if (p) counts[p-pieces] += 1;
X	}
X	user_cost = 0;
X	for (i = 1; i <= NUM_OBJECTS; i++)
X		user_cost += counts[i] * piece_attr[i-1].build_time;
X		
X	comp_cost = 0;
X	for (i = NUM_OBJECTS+2; i <= 2*NUM_OBJECTS+1; i++)
X		comp_cost += counts[i] * piece_attr[i-NUM_OBJECTS-2].build_time;
X		
X	for (i = 0; i < NUM_OBJECTS+1; i++) {
X		pos_str (1, (int) i * 6, "%2d %c  ", counts[i], pieces[i]);
X		pos_str (2,(int) i * 6, "%2d %c  ", counts[i+NUM_OBJECTS+1], pieces[i+NUM_OBJECTS+1]);
X	}
X
X	pos_str (1, (int) i * 6, "%5d", user_cost);
X	pos_str (2, (int) i * 6, "%5d", comp_cost);
X	pos_str (0, 0, "Round %3d", (round + 1) / 2);
X}
X
X/*
XPrint a single cell in condensed format.
X*/
X
Xextern char zoom_list[];
X
Xvoid
Xprint_movie_cell (mbuf, row, col, row_inc, col_inc)
Xchar *mbuf;
Xint row, col;
Xint row_inc, col_inc;
X{
X	int r, c;
X	char cell;
X
X	cell = ' ';
X	for (r = row; r < row + row_inc; r++)
X	for (c = col; c < col + col_inc; c++)
X	if (strchr (zoom_list, mbuf[row_col_loc(r,c)])
X		< strchr (zoom_list, cell))
X	cell = mbuf[row_col_loc(r,c)];
X	
X	(void) move (row/row_inc + NUMTOPS, col/col_inc);
X	(void) addch ((chtype)cell);
X}
//E*O*F game.c//

echo shar: End of archive 4 \(of 6\).
cp /dev/null ark4isdone
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

chuck@amdahl.uts.amdahl.com (Charles Simmons) (04/11/88)

# 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:
# empire.6 util.c data.c COPYING

echo x - empire.6
sed -e 's/^X//' > "empire.6" << '//E*O*F empire.6//'
X.\" %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */
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.TH Empire 6
X.SH NAME
Xempire -- the wargame of the century
X.SH SYNOPSIS
Xempire [-w water] [-s smooth] [-d delay]
X.SH DESCRIPTION
XEmpire is a simulation of a full-scale war between two
Xemperors, the computer and you.  Naturally, there is only
Xroom for one, so the object of the game is to destroy
Xthe other.  The computer plays by the same rules that you
Xdo.
X.TP 10
X.BI \-w water\^
XThis option controls the amount of water on the map.
XThis is given as the percentage of the map which should
Xbe water.  The default is 70% water.  \f2water\fP
Xmust be an integer in the between 10 and 90 inclusive.
X.TP
X.BI \-s smooth\^
XThis controls the smoothness of the map.  A low value
Xwill produce a highly chaotic map with lots of small
Xislands or lakes.  A high value will produce a map
Xwith a few scattered continents.  Be forewarned that
Xa high value will cause the program to take a long
Xtime to start up.  The default value is 5.
X.TP
X.BI \-d delay\^
XThis option controls the length of time the computer will
Xdelay after printing informational messages at the top of
Xthe screen.  \f2delay\fP is specified in milliseconds.  The
Xdefault value is 2000 which allows the user two seconds to
Xread a message.
X.SH EXAMPLES
Xempire -w90 -s2
X.P
XThis produces a map with many islands.
X.P
Xempire -w50 -s0
X.P
XThis produces a really strange map.  These values
Xare not recommended for the faint at heart.
X.P
Xempire -w10
X.P
XThis produces a map with lots of land and a few lakes.
XThe computer will have a hard time on this sort of map
Xas it will try and produce lots of troop transports,
Xwhich are fairly useless.
X.SH INTRODUCTION
XEmpire is a war game played between you and the user.  The world
Xon which the game takes place is a square rectangle containing cities,
Xland, and water.  Cities are used to build armies, planes, and ships
Xwhich can move across the world destroying enemy pieces, exploring,
Xand capturing more cities.  The objective of the game is to destroy
Xall the enemy pieces, and capture all the cities.
X.P
XThe world is a rectangle 60 by 100 squares on a side.  The world
Xconsists of sea (.), land (+), uncontrolled
Xcities (*), computer-controlled cities (X), and cities that you
Xcontrol (O).
X.P
XThe world is displayed on the player's screen during
Xmovement.  (On terminals with small screens, only a portion of
Xthe world is shown at any one time.)
XEach piece is represented by a unique
Xcharacter on the map.  With a few exceptions, you can
Xonly have one piece on a given location.  On the map, you
Xare shown only the 8 squares adjacent to your units.
XThis information is updated before and during each of your moves.
XThe map displays the most recent information known.
X.P
XThe game starts by assigning you one city and the
Xcomputer one city.  Cities can produce new pieces.  Every
Xcity that you own produces more pieces for you according
Xto the cost of the desired piece.  The typical play of the
Xgame is to issue the Automove command until you decide
Xto do something special.  During movement in each round,
Xthe player is prompted to move each piece that does not
Xotherwise have an assigned function.
X.P
XMap coordinates are 4-digit numbers.  The first two
Xdigits are the row, the second two digits are the
Xcolumn.
X.SH PIECES
XThe pieces are as follows:
X.P
X.TS
Xcenter box tab(;);
Xl | c | c | r | r | r | c.
XPiece;You;Enemy;Moves;Hits;Str;Cost
X_
XArmy;A;a;1;1;1;5(6)
XFighter;F;f;8;1;1;10(12)
XPatrol Boat;P;p;4;1;1;15(18)
XDestroyer;D;d;2;3;1;20(24)
XSubmarine;S;s;2;2;3;20(24)
XTroop Transport;T;t;2;1;1;30(36)
XAircraft Carrier;C;c;2;8;1;30(36)
XBattleship;B;b;2;10;2;40(48)
XSatellite;Z;z;10;--;--;50(60)
X.TE
X.P
XThe second column shows the map representation for your
Xunits.
X.P
XThe third shows the representations of enemy units.
X.P
XMoves is the number of squares that the unit can move in a
Xsingle round.
X.P
XHits is the amount of damage a unit can take before it
Xis destroyed.
X.P
XStrength is the amount of damage a unit can inflict upon an
Xenemy during each round of an attack.
X.P
XCost is the number of rounds needed for a city to produce
Xthe piece.
X.P
XThe number in parenthesis is the cost for a city to
Xproduce the first unit.
X.P
XEach piece has certain advantages associated with it that
Xcan make it useful.  One of the primary strategic aspects
Xof this game is deciding which pieces will be produced
Xand in what quantities.
X.P
X\f3Armies\f1 can only move on land, and are the only piece that can
Xmove on land.  Only armies can capture cities.
XThis means that you must produce armies in order to win the
Xgame.  Armies have a 50% chance of capturing a city when they
Xattack.  (Attacking one's own city results in the army's
Xdestruction.  Armies that move onto the sea will drown.
XArmies can attack objects at sea, but even if they win, they
Xwill drown.)  Armies can be carried by troop transports.
XIf an army is moved onto a troop transport, then whenever
Xthe transport is moved, the army will be moved with the
Xtransport.  You cannot attack any piece at sea while on a
Xtransport.
X.P
X\f3Fighters\f1 move over both land and sea, and they move 8 squares
Xper round.  Their high speed and great mobility make fighters
Xideal for exploring.  However, fighters must periodically land
Xat user-owned cities for refueling.  A fighter can travel 32
Xsquares without refeuling.  Fighters are also shot down if they
Xattempt to fly over a city which is not owned by the user.
X.P
X\f3Patrol boats\f1 are fast but lightly armored.  Therefore
Xthey are useful for patrolling ocean waters and exploring.
XIn an attack against a stronger boat, however, patrol boats
Xwill suffer heavy casulties.
X.P
X\f3Destroyers\f1 are fairly heavily armored and reasonably quick
Xto produce.  Thus they are useful for destroying enemy transports
Xwhich may be trying to spread the enemy across the face of the
Xworld.
X.P
XWhen a \f3submarine\f1 scores a hit, 3 hits are exacted instead of 1.
XThus submarines can inflict heavy damage in a fight against
Xheavily armored boats.  Notice that healthy submarines will
Xtypically defeat healthy destroyers two-thirds of the time.
XHowever, a submarine will defeat a fighter about two-thirds
Xof the time, while a destroyer will defeat a fighter three-fourths
Xof the time.
X.P
X\f3Troop transports\f1 are the only pieces that can carry armies.
XA maximum of six armies can be carried by a transport.
XOn any world containing a reasonable amount of water,
Xtransports will be a critical resource in winning the game.
XNotice that the weakness of transports implies they need
Xprotection from stronger ships.
X.P
X\f3Aircraft carriers\f1 are the only ships that can
Xcarry fighters.  Carriers carry a maximum of the number
Xof hits left of fighters.  Fighters are refueled when they
Xland on a carrier.
X.P
X\f3Battleships\f1 are similar to destroyers except that they are
Xmuch stronger.
X.P
X\f3Satellites\f1 are only useful for reconaissance.  They can not
Xbe attacked.  They are launched in a random diagonal orbit, and
Xstay up for 50 turns.  They can see one square farther than other
Xobjects.
X.P
XAll ships can move only on sea.  Ships can also dock in a
Xuser-owned city.  Docked ships have damage repaired at the
Xrate of 1 hit per turn.  Ships which have suffered a lot
Xof damage will move more slowly.
X.P
XBecause of their ability
Xto be repaired, ships with lots of hits such as Carriers and
XBattleships have an additional advantage.
XAfter suffering minor damage while destroying enemy shipping,
Xthese ships can sail back to port and be quickly repaired before
Xthe enemy has time to replenish her destroyed shipping.
X.P
XThe following table gives the probability that the piece listed
Xon the side will defeat the piece listed at the top in a battle.
X(The table assumes that both pieces are undamaged.)
X.P
X.TS
Xcenter box tab(;);
Xl | c | c | c | c | c | c | c | c.
X;A;F;P;D;S;T;C;B
X_
XA;50%;50%;50%;25%;33%;50%;13%;10%
XF;50%;50%;50%;25%;33%;50%;13%;10%
XP;50%;50%;50%;25%;33%;50%;13%;10%
XD;75%;75%;75%;50%;33%;75%;27%;17%
XS;67%;67%;67%;67%;50%;67%;40%;20%
XT;50%;50%;50%;25%;33%;50%;13%;10%
XC;88%;88%;88%;73%;60%;88%;50%;29%
XB;90%;90%;90%;84%;80%;90%;71%;50%
X.TE
X.P
XNotice, however, that when a ship has been damaged, the odds of
Xbeing defeated can go up quite a bit.  For example, a healthy
Xsubmarine has a 25% chance of defeating a battleship that has
Xhad one hit of damage done to it, and a healthy submarine has
Xa 50% chance of defeating a carrier which has suffered two hits
Xof damage.
X.SH "MOVEMENT FUNCTIONS"
XThere are a variety of movement functions.  The movement functions
Xof pieces can be specified in user mode and edit mode.
XCities can have movement functions set for each type of piece.
XWhen a movement function for a type of pieces is set for a city,
Xthen every time that type of piece appears in the city, the piece
Xwill acquire that movement function.  Be forewarned that moving
Xloaded transports or loaded carriers into a city can have undesirable
Xside effects.
X.P
XNormally, when a movement
Xfunction has been specified, the piece will continue moving according to
Xthat function until one of the following happen:
X.TP 5
X.B *
XAn enemy piece or unowned city appears next to the piece.  In this case
Xthe piece will be completely awoken, unless its movement function has
Xbeen set to a specific destination.
XArmies on ships and pieces inside
Xcities will not be awoken if the enemy piece is gone by the time it is
Xtheir turn to move.
X.TP
X.B *
XYou explicitly awaken the piece.
X.TP
X.B *
XThe piece can no longer move in accordance with its programmed function.
XIn this case, the piece will awaken \f2temporarily\fP.  You will be asked
Xto move the piece at which time you may awaken it.
X.TP
X.B *
XThe piece is a fighter which has just enough fuel (plus a small reserve)
Xto get to the nearest city.
XIn this case,
Xthe piece will awaken completely, unless its movement function has
Xbeen set to a specific destination, or its movement function has been
Xset to \f2land\fP.
X.P
XThe rationale behind this complexity is that fighters must be awoken
Xcompletely before they are out of range of a city
Xto prevent one from accidentally forgetting to waken the
Xfighter and then watching it fly off to its doom.  However, it is presumed
Xthat when a path is set for the fighter, the fighter is not in danger of
Xrunning out of fuel.
X.P
XPieces do not completely awaken when their function has been set to a
Xdestination because it is slightly time consuming to reset the destination,
Xbut very simple (one keystroke) to wake the piece.
X.P
XThe movement functions are:
X.TP 10
X.B Attack
XThis function applies only to armies.  When this function is set,
Xthe army will move toward the nearest enemy city, unowned city, or
Xenemy army.  This is useful when fighting off an invading enemy or
Xtaking over a new continent.  When an army is set to this mode,
Xit will also explore nearby territory.  This tends to make
Xthe "grope" movement mode pretty useless.
X.TP
X.B Awake
XWhen pieces are awake, you will be asked for the direction in which
Xthe piece should move on each turn.
X.TP
X.B Fill
XThis function applies to carriers and transports.  When this function is
Xspecified, these ships sleep until they have been filled with fighters or
Xarmies respectively.
X.TP
X.B Grope
XThis function causes a piece to explore.  The piece heads toward the nearest
Xunseen square of the map on each of its moves.  Some attempt is made to 
Xexplore in an optimal fashion.
X.TP
X.B Land
XThis function applies to fighters and causes the fighter to head toward
Xthe nearest transport or carrier.
X.TP
X.B Random
XThis movement function causes a piece to move at random to an adjacent
Xempty square.
X.TP
X.B Sentry
XThis movement function puts a piece to sleep.
XThe function of a city cannot be set to 'sleep'.
X.TP
X.B Transport
XThis movement function only works on armies.  The army sleeps until
Xan unfull transport passes by, at which point the army wakes up and
Xboards the transport.
X.TP
X.B Upgrade
XThis movement function only works with ships.  The ship will move
Xto the nearest owned city and remain there until it is repaired.
X.TP
X.B <dir>
XPieces can be set to move in a specified direction.
X.TP
X.B <dest>
XPieces can be set to move toward a specified square.  In this movement
Xmode, pieces take a shortest path toward the destination.  Pieces moving
Xin accordance with this function prefer diagonal moves that explore
Xterritory.  Because of this, the movement of the piece may be
Xnon-intuitive.
X.P
XAs examples of how to use these movement functions, typically
Xwhen I have a new city on a continent, I set the Army function of the
Xcity to \f2attack\f1.  Whenever an army is produced, it merrily goes off
Xon its way exploring the continent and moving towards unowned cities
Xor enemy armies or cities.
X.P
XI frequently set the ship functions for cities that are far from the
Xfront to automatically move ships towards the front.
X.P
XWhen I have armies on a continent, but there is nothing to explore
Xor attack, I move the army to the shore and use the \f2transport\f1
Xfunction to have that army hop aboard the first passing transport.
X.SH COMMANDS
XThere are three command modes.  The first of these is "command mode".
XIn this mode, you give commands that affect the game as a whole.
XIn the second mode, "move mode", you give commands to move your
Xpieces.  The third mode is "edit mode", and in this mode you can
Xedit the functions of your pieces and examine various portions of
Xthe map.
X.P
XAll commands are one character long.  The full mnemonic names are
Xlisted below as a memorization aid.
XThe mnemonics are somewhat contrived because there are so few
Xcharacters in the English language.  Too bad this program isn't
Xwritten in Japanese, neh?
X.P
XIn all command modes, typing "H" will print out a screen of help
Xinformation, and typing <ctrl-L> will redraw the screen.
X.P
X.SH COMMAND MODE
XIn command mode, the computer will prompt you for your orders.
XThe following commands can be given at this time:
X.TP 10
X.B Automove
XEnter automove mode.  This command begins a new round of movement.
XYou will remain in move mode after each of the computer's turns.
X(In move mode, the "O" command will return you to command mode
Xafter the computer finishes its next turn.
X.TP
X.B City
XGive the computer a random unowned city.  This command is useful if you
Xfind that the computer is getting too easy to beat.
X.TP
X.B Date
XThe current round is displayed.
X.TP
X.B Examine
XExamine the enemy's map.  This command is only valid after the computer
Xhas resigned.
X.TP
X.B File
XPrint a copy of the map to the specified file.
X.TP
X.B Give
XThis command gives the computer a free move.
X.TP
X.B J
XEnter edit mode where you can examine and change the functions
Xassociated with your pieces and cities.
X.TP
X.B Move
XEnter move mode for a single round.
X.TP
X.B N
XGive the computer the number of free moves you specify.
X.TP
X.B Print
XDisplay a sector on the screen.
X.TP
X.B Quit
XQuit the game.  (Note that your game will be saved in 'empsave.dat'
Xafter each command you give, and after each move you make.)
X.TP
X.B Restore
XRestore the game from empsave.dat.  (Currently, this command is useless
Xsince the game is restored when the program first starts up, and the
Xgame is saved after each move.)
X.TP
X.B Save
XSave the game in empsave.dat.  (Currently, this command is useless
Xsince the game is saved after each of your commands and after each move.)
X.TP
X.B Trace
XThis command toggles a flag.  When the flag is set,
Xafter each move, either yours or the computer's,
Xa picture of the world is written out to the file
X'empmovie.dat'.  \f3Watch out!  This command produces lots of
Xoutput.\f1
X.TP
X.B Watch
XThis command allows you to watch a saved movie.
XThe movie is displayed in a condensed version so that
Xit will fit on a single screen, so the output may be
Xa little confusing.  This command is only legal if the
Xcomputer resigns.  If you lose the game, you cannot replay
Xa movie to learn the secrets of how the computer beat you.
XNor can you replay a movie to find out the current positions
Xof the computer's pieces.  When replaying a movie, it is
Xrecommended that you use the \f2-d\f1 option to set the delay
Xto around 2000 milliseconds or so.  Otherwise the screen will be
Xupdated too quickly for you to really grasp what is going on.
X.TP
X.B Zoom
XDisplay a condensed version of the map on the screen.  The user map is
Xdivided into small rectangles.  Each rectangle is displayed as one square
Xon the screen.  If there is a city in a rectangle, then it
Xis displayed.  Otherwise enemy pieces are displayed, then user pieces,
Xthen land, then water, and then unexplored territory.  When pieces are
Xdisplayed, ships are preferred to fighters and armies.
X.SH MOVE MODE
XIn move mode, the cursor will appear on the screen at the position
Xof each piece that needs to be moved.  You can then give commands
Xto move the piece.  Directions to move are specified by the
Xfollowing keys:
X.P
X.fp 5 TT
X.ft 5
X.nf
X        QWE
X        A D
X        ZXC
X.fi
X.ft 1
X.P
XThese keys move in the direction of the key from S.  The
Xcharacters are not echoed and only 1 character is
Xaccepted, so there is no need for a <Return>.  Hit the <Space>
Xbar if you want the piece to stay put.
X.P
XOther commands are:
X.TP 10
X.B Build
XChange the production of a city.
X.TP
X.B Fill
XSet the function of a troop transport or aircraft carrier to
X\f2fill\f1.
X.TP
X.B Grope
XSet the function of a piece to \f2grope\f1.
X.TP
X.BI I dir
XSet the direction for a piece to move.
X.TP
X.B J
XEnter edit mode.
X.TP
X.B Kill
XWake up the piece.  If the piece is a transport or carrier,
Xpieces on board will not be awoken.
X.TP
X.B Land
XSet a fighter's function to \f2land\f1.
X.TP
X.B Out
XCancel automove mode.  At the end of the round, you will
Xbe placed in command mode.
X.TP
X.B Print
XRedraw the screen.
X.TP
X.B Random
XSet a piece's function to \f2random\f1.
X.TP
X.B Sentry
XSet a piece's function to \f2sentry\f1.
X.TP
X.B Transport
XSet an army's function to \f2transport\f1.
X.TP
X.B Upgrade
XSet a ship's function to \f2upgrade\f1.
X.TP
X.BI V "piece func"
XSet the city movement function for the specified piece
Xto the specified function.  For example, typing "VAY" would
Xset the city movement function for armies to \f2attack\f1.  Whenever
Xan army is produced in the city (or whenever a loaded transport
Xenters the city), the army's movement function
Xwould be set to \f2attack\f1.
X.TP
X.B Y
XSet an army's function to \f2attack\f1.
X.TP
X.B ?
XDisplay information about the piece.  The
Xfunction, hits left, range, and number of items on board are
Xdisplayed.
X.P
XAttacking something is accomplished by  moving  onto  the
Xsquare of the unit you wish to attack.  Hits are traded
Xoff at 50% probability of a hit landing on one or the
Xother units until one unit is totally destroyed.  There
Xis only 1 possible winner.
X.P
XYou are "allowed" to do \f3fatal\f1 things like attack your
Xown cities or other pieces.  If you try to make a fatal
Xmove, the computer will warn you and give you a chance to
Xchange your mind.
X.P
XYou cannot move onto the edge of the world.
X.SH EDIT MODE
XIn edit mode, you can move around the world and examine pieces
Xor assign them new functions.
XTo move the cursor around, use the standard direction
Xkeys.
XOther commands are:
X.TP 10
X.B Build
XChange the production of the city under the cursor.
XThe program will prompt for the new production, and you
Xshould respond with the key corresponding to the letter of the piece
Xthat you want produced.
X.TP
X.B Fill
XSet a transport's or carrier's function to \f2fill\f1.
X.TP
X.B Grope
XSet a piece's function to \f2grope\f1.
X.TP
X.BI I dir
XSet the function of a piece (or city) to the specified direction.
X.TP
X.B Kill
XWake all pieces at the current location.  If the location is a city,
Xthe fighter path will also be canceled.
X.TP
X.B Mark
XSelect the piece or city at the current location.  This command
Xis used with the "N" command.
X.TP
X.B N
XSet the destination of the piece previously selected with the "M"
Xcommand to the current square.
X.TP
X.B Out
XExit edit mode.
X.TP
X.BI Print sector
XDisplay a new sector of the map.  The map is divided into
Xten sectors of size 20 by 70.  Sector zero is in the upper-left
Xcorner of the map.  Sector four is in the lower-left corner of
Xthe map.  Sector five is in the upper-right corner, and sector
Xnine is in the lower-right corner.
X.TP
X.B Random
XSet a piece to move randomly.
X.TP
X.B Sentry
XPut a piece to sleep.
X.TP
X.B Transport
XSet an army's function to \f2transport\f1.
X.TP
X.B Upgrade
XSet a ship's function to \f2upgrade\f1.
X.TP
X.BI V "piece func"
XSet the city movement function for a piece.
X.TP
X.B Y
XSet an army's function to \f2attack\f1.
X.TP
X.B ?
XDisplay information about a piece or city.
XFor a city, the production, time of completion of
Xthe next piece, movement functions, and the number of fighters and ships
Xin the city are displayed.
X.P
XNote that you cannot directly affect anything inside a city with
Xthe editor.
X.SH HINTS
XAfter you have played this game for a while, you will probably
Xfind that the computer is immensely easy to beat.  Here are some
Xideas you can try that may make the game more interesting.
X.TP 5
X.B *
XGive the computer one or more extra cities before starting the game.
X.TP
X.B *
XTry playing the game with a low smoothness value (try using the
X-s2 or even -s0 option).
X.TP
X.B *
XWhen starting the game, the program will ask you what difficulty
Xlevel you want.  Here "difficulty level" is a misnomer.  To compute
Xa difficulty level, the program looks at each continent and counts
Xthe number of cities on the continents.  A high "difficulty level"
Xgives the computer a large continent with many cities, while the
Xuser gets a small continent with few cities.  A low "difficulty level"
Xhas the opposite effect.  It may be the case that the computer will
Xplay better when the "difficulty level" is low.  The reason for this
Xis that the computer is forced to move armies to multiple continents
Xearly in the game.
X.SH HISTORY
XApparently, this game was originally written outside of Digital,
Xprobably at a university.  The game was ported to DEC's VAX/VMS
Xfrom the TOPS-10/20 FORTRAN sources available around fall 1979.
XThe original authors listed in my old documentation are
XMario DeNobili and Thomas N. Paulson.
XSupport for different terminal types was added by Craig Leres.
X.P
XEd James got hold of the sources at Berkeley and converted
Xportions of the code to C, mostly to use curses for the screen
Xhandling.  He published his modified sources on the net in
XDecember 1986.  Because this game ran on VMS machines for so
Xlong, a previous version is known as VMS Empire.
X.P
XIn 1987 Chuck Simmons at Amdahl
Xreverse engineered the program and wrote a
Xversion completely written in C.  In doing so, he completely
Xmodified the computer strategy, the commands, the piece types,
Xmany of the piece attributes, and the algorithm for creating maps.
X.SH FILES
X.TP 10
X\f2empsave.dat\f1
Xholds a backup of the game.  Whenever empire is run,
Xit will reload any game in this file.
X.TP
X\f2empmovie.dat\f1
Xholds a history of the game so that the game can be replayed as
Xa "movie".
X.SH BUGS
XNo doubt numerous.
X.P
XSatellites are not completely implemented.  You should be able to
Xmove to a square that contains a satellite, but the program won't
Xlet you.  Enemy satellites should not cause your pieces to awaken.
X.SH AUTHORS
X.nf
XOriginal concept by Mario DeNobili and Thomas N. Paulson.
XSupport for different terminal types added by Craig Leres.
XCurses support added by Ed James.
XC/Unix version written by Chuck Simmons
X.fi
X.SH COPYLEFT
X.fn
XCopyright (C) 1987, 1988 Chuck Simmons
X
XSee the file COPYING, distributed with empire, for restriction
Xand warranty information.
X.fi
//E*O*F empire.6//

echo x - util.c
sed -e 's/^X//' > "util.c" << '//E*O*F util.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/*
Xutil.c -- various utility routines.
X*/
X
X#include <curses.h>
X#include <ctype.h>
X#include "empire.h"
X#include "extern.h"
X
X
X/*
XConvert a string to uppercase.
XShirley this is defined elsewhere?
X*/
X
X#include <ctype.h>
X
Xvoid
Xtupper (str)
Xchar	*str;
X{
X	while (*str) {
X		if (islower (*str)) *str = upper (*str);
X		str++;
X	}
X}
X
X/*
XConvert a character to uppercase (if it is lowercase)
X*/
X
Xchar
Xupper (c)
Xchar c;
X{
X	if (islower (c))
X		return toupper (c);
X	else return c;
X}
X
X/*
XClear the end of a specified line starting at the specified column.
X*/
X
Xvoid
Xclreol(linep, colp)
Xint linep, colp;
X{
X	(void) move (linep, colp);
X	(void) clrtoeol();
X}
X
X/*
XInitialize the terminal.
X*/
X
Xvoid
Xttinit()
X{
X	(void) initscr();
X	(void) noecho();
X	(void) crmode();
X	lines = LINES;
X	cols = COLS;
X	if (lines > MAP_HEIGHT + NUMTOPS + 1)
X		lines = MAP_HEIGHT + NUMTOPS + 1;
X	if (cols > MAP_WIDTH + NUMSIDES)
X		cols = MAP_WIDTH + NUMSIDES;
X}
X
X
X/*
XClear the screen.  We must also kill information maintained about the
Xdisplay.
X*/
X
Xvoid
Xclear_screen () {
X	(void) clear ();
X	(void) refresh ();
X	kill_display ();
X}
X
X/*
XRedraw the screen.
X*/
X
Xvoid
Xredraw () {
X	(void) clearok (curscr, TRUE);
X	(void) refresh ();
X}
X
X/*
XWait a little bit to give user a chance to see a message.  We refresh
Xthe screen and pause for a few milliseconds.
X*/
X
Xvoid
Xdelay () {
X	(void) refresh ();
X	(void) napms (delay_time); /* pause a bit */
X}
X
X
X/*
XClean up the display.  This routine gets called as we leave the game.
X*/
X
Xvoid
Xclose_disp()
X{
X	(void) move (LINES - 1, 0);
X	(void) clrtoeol ();
X	(void) refresh ();
X	(void) endwin ();
X}
X
X/*
XPosition the cursor and output a string.
X*/
X
Xvoid
X/* VARARGS3 */
Xpos_str (row, col, str, a, b, c, d, e, f, g, h)
Xint row, col;
Xchar *str;
Xint a, b, c, d, e, f, g, h;
X{
X	(void) move (row, col);
X	addprintf (str, a, b, c, d, e, f, g, h);
X}
X
Xvoid
X/* VARARGS1 */
Xaddprintf (str, a, b, c, d, e, f, g, h)
Xchar *str;
Xint a, b, c, d, e, f, g, h;
X{
X	char junkbuf[STRSIZE];
X	
X	(void) sprintf (junkbuf, str, a, b, c, d, e, f, g, h);
X	(void) addstr (junkbuf);
X}
X
X/*
XReport a bug.
X*/
X
Xvoid
Xassert (expression, file, line)
Xchar *expression;
Xchar *file;
Xint line;
X{
X	char buf[STRSIZE];
X	int a;
X
X	(void) move (lines, 0);
X	close_disp ();
X
X	(void) sprintf (buf, "assert failed: file %s line %d: %s",
X			file, line, expression);
X
X	a = 1; /* keep lint quiet */
X	a /= 0; /* force a core dump */
X	a = a; /* keep lint quiet */
X}
X
X/*
XEnd the game by cleaning up the display.
X*/
X
Xvoid
Xempend ()
X{
X	close_disp ();
X	exit (0);
X}
X
X/*
X * 03a 01Apr88 aml .Hacked movement algorithms for computer.
X * 02b 01Jun87 aml .First round of bug fixes.
X * 02a 01Jan87 aml .Translated to C.
X * 01b 27May85 cal .Fixed round number update bug. Made truename simple.
X * 01a 01Sep83 cal .Taken from a Decus tape
X */
X
Xvoid
Xver ()
X{
X        (void) addstr ("EMPIRE, Version 5.00 site Amdahl 1-Apr-1988");
X}
X
X/*
XHere is a little routine to perform consistency checking on the
Xdatabase.  I'm finding that my database is becoming inconsistent,
Xand I've no idea why.  Possibly this will help.
X
XWe perform the following functions:
X
X1)  Make sure no list contains loops.
X
X2)  Make sure every object is in either the free list with 0 hits,
Xor it is in the correct object list and a location list with non-zero hits,
Xand an appropriate owner.
X
X3)  Make sure every city is on the map.
X
X4)  Make sure every object is in the correct location and that
Xobjects on the map have non-zero hits.
X
X5)  Make sure every object in a cargo list has a ship pointer.
X
X6)  Make sure every object with a ship pointer is in that ship's
Xcargo list.
X*/
X
Xstatic int in_free[LIST_SIZE]; /* TRUE if object in free list */
Xstatic int in_obj[LIST_SIZE]; /* TRUE if object in obj list */
Xstatic int in_loc[LIST_SIZE]; /* TRUE if object in a loc list */
Xstatic int in_cargo[LIST_SIZE]; /* TRUE if object in a cargo list */
X
Xvoid
Xcheck () {
X	void check_cargo(), check_obj(), check_obj_cargo();
X	
X	long i, j;
X	piece_info_t *p;
X	
X	/* nothing in any list yet */
X	for (i = 0; i < LIST_SIZE; i++) {
X		in_free[i] = 0;
X		in_obj[i] = 0;
X		in_loc[i] = 0;
X		in_cargo[i] = 0;
X	}
X		
X	/* Mark all objects in free list.  Make sure objects in free list
X	have zero hits. */
X	
X	for (p = free_list; p != NULL; p = p->piece_link.next) {
X		i = p - object;
X		ASSERT (!in_free[i]);
X		in_free[i] = 1;
X		ASSERT (p->hits == 0);
X		if (p->piece_link.prev)
X			ASSERT (p->piece_link.prev->piece_link.next == p);
X	}
X	
X	/* Mark all objects in the map.
X	Check that cities are in corect location.
X	Check that objects are in correct location,
X	have a good owner, and good hits. */
X	
X	for (i = 0; i < MAP_SIZE; i++) {
X		if (map[i].cityp) ASSERT (map[i].cityp->loc == i);
X		
X		for (p = map[i].objp; p != NULL; p = p->loc_link.next) {
X			ASSERT (p->loc == i);
X			ASSERT (p->hits > 0);
X			ASSERT (p->owner == USER || p->owner == COMP);
X				
X			j = p - object;
X			ASSERT (!in_loc[j]);
X			in_loc[j] = 1;
X			
X			if (p->loc_link.prev)
X				ASSERT (p->loc_link.prev->loc_link.next == p);
X		}
X	}
X
X	/* make sure all cities are on map */
X
X	for (i = 0; i < NUM_CITY; i++)
X		ASSERT (map[city[i].loc].cityp == &(city[i]));
X
X	/* Scan object lists. */
X	
X	check_obj (comp_obj, COMP);
X	check_obj (user_obj, USER);
X	
X	/* Scan cargo lists. */
X	
X	check_cargo (user_obj[TRANSPORT], ARMY);
X	check_cargo (comp_obj[TRANSPORT], ARMY);
X	check_cargo (user_obj[CARRIER], FIGHTER);
X	check_cargo (comp_obj[CARRIER], FIGHTER);
X	
X	/* Make sure all objects with ship pointers are in cargo. */
X
X	check_obj_cargo (comp_obj);
X	check_obj_cargo (user_obj);
X	
X	/* Make sure every object is either free or in loc and obj list. */
X
X	for (i = 0; i < LIST_SIZE; i++)
X		ASSERT (in_free[i] != (in_loc[i] && in_obj[i]));
X}
X
X/*
XCheck object lists.  We look for:
X
X1)  Loops and bad prev pointers.
X
X2)  Dead objects.
X
X3)  Invalid types.
X
X4)  Invalid owners.
X*/
X
Xvoid
Xcheck_obj (list, owner)
Xpiece_info_t **list;
Xint owner;
X{
X	long i, j;
X	piece_info_t *p;
X	
X	for (i = 0; i < NUM_OBJECTS; i++)
X	for (p = list[i]; p != NULL; p = p->piece_link.next) {
X		ASSERT (p->owner == owner);
X		ASSERT (p->type == i);
X		ASSERT (p->hits > 0);
X		
X		j = p - object;
X		ASSERT (!in_obj[j]);
X		in_obj[j] = 1;
X	
X		if (p->piece_link.prev)
X			ASSERT (p->piece_link.prev->piece_link.next == p);
X	}
X}
X
X/*
XCheck cargo lists.  We assume object lists are valid.
Xas we will place bits in the 'in_cargo' array that are used by
X'check_obj'.
X
XCheck for:
X
X1)  Number of items in list is same as cargo count.
X
X2)  Type of cargo is correct.
X
X3)  Location of cargo matches location of ship.
X
X4)  Ship pointer of cargo points to correct ship.
X
X5)  There are no loops in cargo list and prev ptrs are correct.
X
X6)  All cargo is alive.
X*/
X
Xvoid
Xcheck_cargo (list, cargo_type)
Xpiece_info_t *list;
Xint cargo_type;
X{
X	piece_info_t *p, *q;
X	long j, count;
X	
X	for (p = list; p != NULL; p = p->piece_link.next) {
X		count = 0;
X		for (q = p->cargo; q != NULL; q = q->cargo_link.next) {
X			count += 1; /* count items in list */
X			ASSERT (q->type == cargo_type);
X			ASSERT (q->owner == p->owner);
X			ASSERT (q->hits > 0);
X			ASSERT (q->ship == p);
X			ASSERT (q->loc == p->loc);
X			
X			j = q - object;
X			ASSERT (!in_cargo[j]);
X			in_cargo[j] = 1;
X
X			if (p->cargo_link.prev)
X				ASSERT (p->cargo_link.prev->cargo_link.next == p);
X                }
X		ASSERT (count == p->count);
X        }
X}
X
X/*
XScan through object lists making sure every object with a ship
Xpointer appears in a cargo list.  We assume object and cargo
Xlists are valid.
X*/
X
Xvoid
Xcheck_obj_cargo (list)
Xpiece_info_t **list;
X{
X	piece_info_t *p;
X	long i;
X
X	for (i = 0; i < NUM_OBJECTS; i++)
X	for (p = list[i]; p != NULL; p = p->piece_link.next) {
X		if (p->ship) ASSERT (in_cargo[p-object]);
X	}
X}
//E*O*F util.c//

echo x - data.c
sed -e 's/^X//' > "data.c" << '//E*O*F data.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/*
XStatic data.
X
XOne of our hopes is that changing the types of pieces that
Xexist in the game is mostly a matter of modifying this file.
XHowever, see also the help routine, empire.h, and empire.doc.
X*/
X
X#include "empire.h"
X
X/*
XPiece attributes.  Notice that the Transport is allowed only one hit.
XIn the previous version of this game, the user could easily win simply
Xby building armies and troop transports.  We attempt to alleviate this
Xproblem by making transports far more fragile.  We have also increased
Xthe range of a fighter from 20 to 30 so that fighters will be somewhat
Xmore useful.
X*/
X
Xpiece_attr_t piece_attr[] = {
X	{'A', /* character for printing piece */
X	 "army", /* name of piece */ 
X	 "army", /* nickname */
X	 "an army", /* name with preceding article */
X	 "armies", /* plural */
X	 "+", /* terrain */
X	 5, /* units to build */
X	 1, /* strength */
X	 1, /* max hits */
X	 1, /* movement */
X	 0, /* capacity */
X	 INFINITY}, /* range */
X
X	/*
X	 For fighters, the range is set to an even multiple of the speed.
X	 This allows user to move fighter, say, two turns out and two
X	 turns back.
X	*/
X	 
X	{'F', "fighter", "fighter", "a fighter", "fighters",
X		".+", 10, 1,  1, 8, 0, 32},
X
X	{'P', "patrol boat", "patrol", "a patrol boat", "patrol boats",
X		".",  15, 1,  1, 4, 0, INFINITY},
X		
X	{'D', "destroyer", "destroyer", "a destroyer", "destroyers",
X		".",  20, 1,  3, 2, 0, INFINITY},
X
X	{'S', "submarine", "submarine", "a submarine", "submarines",
X		".",  20, 3,  2, 2, 0, INFINITY},
X
X	{'T', "troop transport", "transport", "a troop transport", "troop transports",
X		".",  30, 1,  1, 2, 6, INFINITY},
X
X	{'C', "aircraft carrier", "carrier", "an aircraft carrier", "aircraft carriers",
X		".",  30, 1,  8, 2, 8, INFINITY},
X
X	{'B', "battleship", "battleship", "a battleship", "battleships",
X		".",  40, 2, 10, 2, 0, INFINITY},
X		
X	{'Z', "satellite", "satellite", "a satellite", "satellites",
X		".+", 50, 0, 1, 10, 0, 500}
X};
X
X/* Direction offsets. */
X
Xint dir_offset [] = {-MAP_WIDTH, /* north */
X		     -MAP_WIDTH+1, /* northeast */
X		     1, /* east */
X		     MAP_WIDTH+1, /* southeast */
X		     MAP_WIDTH, /* south */
X		     MAP_WIDTH-1, /* southwest */
X		     -1, /* west */
X		     -MAP_WIDTH-1}; /* northwest */
X
X/* Names of movement functions. */
X
Xchar *func_name[] = {"none", "random", "sentry", "fill", "land",
X			"explore", "load", "attack", "load", "repair",
X			"transport",
X			"W", "E", "D", "C", "X", "Z", "A", "Q"};
X
X/* The order in which pieces should be moved. */
Xint move_order[] = {SATELLITE, TRANSPORT, CARRIER, BATTLESHIP, 
X		    PATROL, SUBMARINE, DESTROYER, ARMY, FIGHTER};
X
X/* types of pieces, in declared order */
Xchar type_chars[] = "AFPDSTCBZ";
X
X/* Lists of attackable objects if object is adjacent to moving piece. */
X
Xchar tt_attack[] = "T";
Xchar army_attack[] = "O*TACFBSDP";
Xchar fighter_attack[] = "TCFBSDPA";
Xchar ship_attack[] = "TCBSDP";
X
X/* Define various types of objectives */
X
Xmove_info_t tt_explore = { /* water objectives */
X	COMP, /* home city */
X	" ", /* objectives */
X	{1} /* weights */
X};
Xmove_info_t tt_load = { /* land objectives */
X	COMP, "$",           {1}
X};
X
X/*
XRationale for 'tt_unload':
X
X     Any continent with four or more cities is extremely attractive,
Xand we should grab it quickly.  A continent with three cities is
Xfairly attractive, but we are willing to go slightly out of our
Xway to find a better continent.  Similarily for two cities on a
Xcontinent.  At one city on a continent, things are looking fairly
Xunattractive, and we are willing to go quite a bit out of our way
Xto find a better continent.
X
X     Cities marked with a '0' are on continents where we already
Xhave cities, and these cities will likely fall to our armies anyway,
Xso we don't need to dump armies on them unless everything else is
Xreal far away.  We would prefer to use unloading transports for
Xtaking cities instead of exploring, but we are willing to explore
Xif interesting cities are too far away.
X
X     It has been suggested that continents containing one city
Xare not interesting.  Unfortunately, most of the time what the
Xcomputer sees is a single city on a continent next to lots of
Xunexplored territory.  So it dumps an army on the continent to
Xexplore the continent and the army ends up attacking the city
Xanyway.  So, we have decided we might as well send the tt to
Xthe city in the first place and increase the speed with which
Xthe computer unloads its tts.
X*/
X
Xmove_info_t tt_unload     = {
X	COMP, "9876543210 ", {1, 1, 1, 1, 1, 1, 11, 21, 41, 101, 61}
X};
X
X/*
X '$' represents loading tt must be first
X 'x' represents tt producing city
X '0' represnets explorable territory
X*/
Xmove_info_t army_fight    = { /* land objectives */
X	COMP, "O*TA ",       {1, 1, 1, 1, 11}
X};
Xmove_info_t army_load     = { /* water objectives */
X	COMP, "$x",          {1, W_TT_BUILD}
X};
X
Xmove_info_t fighter_fight = {
X	COMP, "TCFBSDPA ",   {1, 1, 5, 5, 5, 5, 5, 5, 9}
X};
Xmove_info_t ship_fight    = {
X	COMP, "TCBSDP ",     {1, 1, 3, 3, 3, 3, 21}
X};
Xmove_info_t ship_repair   = {
X	COMP, "X",           {1}
X};
X
Xmove_info_t user_army        = {
X	USER, " ",   {1}
X};
Xmove_info_t user_army_attack = {
X	USER, "*Xa ", {1, 1, 1, 12}
X};
Xmove_info_t user_fighter     = {
X	USER, " ",   {1}
X};
Xmove_info_t user_ship        = {
X	USER, " ",   {1}
X};
Xmove_info_t user_ship_repair = {
X	USER, "O",   {1}
X};
X
X/*
XVarious help texts.
X*/
X
Xchar *help_cmd[] = {
X	"COMMAND MODE",
X	"Auto:     enter automove mode",
X	"City:     give city to computer",
X	"Date:     print round",
X	"Examine:  examine enemy map",
X	"File:     print map to file",
X	"Give:     give move to computer",
X	"Help:     display this text",
X	"J:        enter edit mode",
X	"Move:     make a move",
X	"N:        give N moves to computer",
X	"Print:    print a sector",
X	"Quit:     quit game",
X	"Restore:  restore game",
X	"Save:     save game",
X	"Trace:    save movie in empmovie.dat",
X	"Watch:    watch movie",
X	"Zoom:     display compressed map",
X	"<ctrl-L>: redraw screen"
X};
Xint cmd_lines = 19;
X
Xchar *help_user[] = {
X	"USER MODE",
X	"QWE",
X	"A D       movement directions",
X	"ZXC",
X	"<space>:  sit",
X	"Build:    change city production",
X	"Fill:     set func to fill",
X	"Grope:    set func to explore",
X	"Help:     display this text",
X	"I <dir>:  set func to dir",
X	"J:        enter edit mode",
X	"Kill:     set func to awake",
X	"Land:     set func to land",
X	"Out:      leave automove mode",
X	"Print:    redraw screen",
X	"Random:   set func to random",
X	"Sentry:   set func to sentry",
X	"Upgrade:  set func to repair",
X	"V <piece> <func>:  set city func",
X	"Y:        set func to attack",
X	"<ctrl-L>: redraw screen",
X	"?:        describe piece"
X};
Xint user_lines = 22;
X	
Xchar *help_edit[] = {
X	"EDIT MODE",
X	"QWE",
X	"A D       movement directions",
X	"ZXC",
X	"Build:    change city production",
X	"Fill:     set func to fill",
X	"Grope:    set func to explore",
X	"Help:     display this text",
X	"I <dir>:  set func to dir",
X	"Kill:     set func to awake",
X	"Land:     set func to land",
X	"Mark:     mark piece",
X	"N:        set dest for marked piece",
X	"Out:      exit edit mode",
X	"Print:    print sector",
X	"Random:   set func to random",
X	"Sentry:   set func to sentry",
X	"Upgrade:  set func to repair",
X	"V <piece> <func>:  set city func",
X	"Y:        set func to attack",
X	"<ctrl-L>: redraw screen",
X	"?:        describe piece"
X};
Xint edit_lines = 22;
//E*O*F data.c//

echo x - COPYING
sed -e 's/^X//' > "COPYING" << '//E*O*F COPYING//'
X
X		    EMPIRE GENERAL PUBLIC LICENSE
X
X Copyright (C)  1987, 1988 Chuck Simmons
X Copyleft (GNU) 1987, 1988 Chuck Simmons
X
X Everyone is permitted to copy and distribute verbatim copies
X of this license, but changing it is not allowed.  You can also
X use this wording to make the terms for other programs.
X
X  The license agreements of most software companies keep you at the
Xmercy of those companies.  By contrast, our general public license is
Xintended to give everyone the right to share EMPIRE.  To make sure that
Xyou get the rights we want you to have, we need to make restrictions
Xthat forbid anyone to deny you these rights or to ask you to surrender
Xthe rights.  Hence this license agreement.
X
X  Specifically, we want to make sure that you have the right to give
Xaway copies of EMPIRE, that you receive source code or else can get it
Xif you want it, that you can change EMPIRE or use pieces of it in new
Xfree programs, and that you know you can do these things.
X
X  To make sure that everyone has such rights, we have to forbid you to
Xdeprive anyone else of these rights.  For example, if you distribute
Xcopies of EMPIRE, you must give the recipients all the rights that you
Xhave.  You must make sure that they, too, receive or can get the
Xsource code.  And you must tell them their rights.
X
X  Also, for our own protection, we must make certain that everyone
Xfinds out that there is no warranty for EMPIRE.  If EMPIRE is modified by
Xsomeone else and passed on, we want its recipients to know that what
Xthey have is not what we distributed, so that any problems introduced
Xby others will not reflect on our reputation.
X
X  Therefore we (Chuck Simmons) make the following terms which say what
Xyou must do to be allowed to distribute or change EMPIRE.
X
X
X			COPYING POLICIES
X
X  1. You may copy and distribute verbatim copies of EMPIRE source code
Xas you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy a valid copyright notice "Copyright
X(C) 1988 Chuck Simmons" (or with whatever year is appropriate); keep
Xintact the notices on all files that refer to this License Agreement
Xand to the absence of any warranty; and give any other recipients of
Xthe EMPIRE program a copy of this License Agreement along with the
Xprogram.  You may charge a distribution fee for the physical act of
Xtransferring a copy.
X
X  2. You may modify your copy or copies of EMPIRE or any portion of it,
Xand copy and distribute such modifications under the terms of
XParagraph 1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating
X    that you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish,
X    that in whole or in part contains or is a derivative of EMPIRE or
X    any part thereof, to be licensed at no charge to all third
X    parties on terms identical to those contained in this License
X    Agreement (except that you may choose to grant more extensive
X    warranty protection to some or all third parties, at your option).
X
X    c) You may charge a distribution fee for the physical act of
X    transferring a copy, and you may at your option offer warranty
X    protection in exchange for a fee.
X
XMere aggregation of another unrelated program with this program (or its
Xderivative) on a volume of a storage or distribution medium does not bring
Xthe other program under the scope of these terms.
X
X  3. You may copy and distribute EMPIRE (or a portion or derivative of it,
Xunder Paragraph 2) in object code or executable form under the terms of
XParagraphs 1 and 2 above provided that you also do one of the following:
X
X    a) accompany it with the complete corresponding machine-readable
X    source code, which must be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    b) accompany it with a written offer, valid for at least three
X    years, to give any third party free (except for a nominal
X    shipping charge) a complete machine-readable copy of the
X    corresponding source code, to be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    c) accompany it with the information you received as to where the
X    corresponding source code may be obtained.  (This alternative is
X    allowed only for noncommercial distribution and only if you
X    received the program in object code or executable form alone.)
X
XFor an executable file, complete source code means all the source code for
Xall modules it contains; but, as a special exception, it need not include
Xsource code for modules which are standard libraries that accompany the
Xoperating system on which the executable file runs.
X
X  4. You may not copy, sublicense, distribute or transfer EMPIRE
Xexcept as expressly provided under this License Agreement.  Any attempt
Xotherwise to copy, sublicense, distribute or transfer EMPIRE is void and
Xyour rights to use the program under this License agreement shall be
Xautomatically terminated.  However, parties who have received computer
Xsoftware programs from you with this License Agreement will not have
Xtheir licenses terminated so long as such parties remain in full compliance.
X
X  5. If you wish to incorporate parts of EMPIRE into other free programs
Xwhose distribution conditions are different, write to Chuck Simmons
Xat 1250 E. Arques Ave (MS 269), Box 3470, Sunnyvale, CA  94088-3470,
XUSA.  We have not yet worked out a simple rule that can be stated here,
Xbut we will often permit this.  We will be guided by the two goals of
Xpreserving the free status of all derivatives of our free software and
Xof promoting the sharing and reuse of software.
X
XYour comments and suggestions about our licensing policies and our
Xsoftware are welcome!  This contract was based on the contract made by
Xthe Free Software Foundation.  Please contact the Free Software
XFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or call (617)
X876-3296 for details on copylefted materal in general, or contact us
Xvia electronic mail at amdahl!chuck.
X
X(Please note that the Free Software Foundation is in no way associated
Xwith EMPIRE, and any lack of quality inherent in EMPIRE should not
Xreflect on the reputation of that noble enterprise.)
X
X
X		       NO WARRANTY
X
X  BECAUSE EMPIRE IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO
XWARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT WHEN
XOTHERWISE STATED IN WRITING, CHUCK SIMMONS AND/OR OTHER PARTIES PROVIDE
XEMPIRE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
XINCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
XAND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
XAND PERFORMANCE OF EMPIRE IS WITH YOU.  SHOULD EMPIRE PROVE DEFECTIVE,
XYOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
X
X  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL CHUCK SIMMONS
XAND/OR ANY OTHER PARTY WHO MAY MODIFY AND REDISTRIBUTE EMPIRE AS PERMITTED
XABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST
XMONIES, OR OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
XOUT OF THE USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS
XOF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD
XPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS)
XEMPIRE, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES,
XOR FOR ANY CLAIM BY ANY OTHER PARTY.
//E*O*F COPYING//

echo shar: End of archive 5 \(of 6\).
cp /dev/null ark5isdone
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

chuck@amdahl.uts.amdahl.com (Charles Simmons) (04/11/88)

# 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:
# term.c README extern.h attack.c math.c makefile MANIFEST

echo x - term.c
sed -e 's/^X//' > "term.c" << '//E*O*F term.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/*
Xterm.c -- this file contains various routines used to control the
Xuser communications area of the terminal.  This area consists of
Xthe top 3 lines of the terminal where messages are displayed to the
Xuser and input is acquired from the user.
X
XThere are two types of output in this area.  One type is interactive
Xoutput.  This consists of a prompt line and an error message line.
XThe other type of output is informational output.  The user must
Xbe given time to read informational output.
X
XWhenever input is received, the top three lines are cleared and the
Xscreen refreshed as the user has had time to read these lines.  We
Xalso clear the 'need_delay' flag, saying that the user has read the
Xinformation on the screen.
X
XWhen information is to be displayed, if the 'need_delay' flag is set,
Xwe refresh the screen and pause momentarily to give the user a chance
Xto read the lines.  The new information is then displayed, and the
X'need_delay' flag is set.
X*/
X
X#include <stdio.h>
X#include <curses.h>
X#include <ctype.h>
X
X#include "empire.h"
X#include "extern.h"
X
Xstatic int need_delay;
X
Xvoid
X/* VARARGS1 */
Xpdebug (s, a, b, c, d, e, f, g, h)
Xchar *s;
Xint a, b, c, d, e, f, g, h;
X{
X	if (!print_debug) return;
X	comment (s, a, b, c, d, e, f, g, h);
X}
X
X/*
XHere are routines that handle printing to the top few lines of the
Xscreen.  'topini' should be called at initialization, and whenever
Xwe finish printing information to the screen.
X*/
X
Xvoid
Xtopini()
X{
X	info (0, 0, 0);
X}
X/*
XWrite a message to one of the top lines.
X*/
X
Xvoid
X/* VARARGS2 */
Xtopmsg(linep, buf, a, b, c, d, e, f, g, h)
Xint linep;
Xchar *buf;
Xint a, b, c, d, e, f, g, h;
X{
X	if (linep < 1 || linep > NUMTOPS)
X		linep = 1;
X	(void) move (linep - 1, 0);
X	
X	if (buf != NULL && strlen (buf) > 0)
X		addprintf (buf, a, b, c, d, e, f, g, h);
X	
X	(void) clrtoeol ();
X}
X
X/*
XPrint a prompt on the first message line.
X*/
X
Xvoid
X/* VARARGS1 */
Xprompt (buf, a, b, c, d, e, f, g, h)
Xchar *buf;
Xint a, b, c, d, e, f, g, h;
X{
X	topmsg (1, buf, a, b, c, d, e, f, g, h);
X}
X
X/*
XPrint an error message on the second message line.
X*/
X
Xvoid
X/* VARARGS1 */
Xerror (buf, a, b, c, d, e, f, g, h)
Xchar *buf;
Xint a, b, c, d, e, f, g, h;
X{
X	topmsg (2, buf, a, b, c, d, e, f, g, h);
X}
X
X/*
XPrint out extra information.
X*/
X
Xvoid
X/* VARARGS1 */
Xextra (buf, a, b, c, d, e, f, g, h)
Xchar *buf;
Xint a, b, c, d, e, f, g, h;
X{
X	topmsg (3, buf, a, b, c, d, e, f, g, h);
X}
X
X/*
XPrint out a generic error message.
X*/
X
Xvoid
Xhuh ()
X{
X	error ("Type H for Help.");
X}
X
X/*
XDisplay information on the screen.  If the 'need_delay' flag is set,
Xwe force a delay, then print the information.  After we print the
Xinformation, we set the need_delay flag.
X*/
X
Xvoid
Xinfo (a, b, c)
Xchar *a, *b, *c;
X{
X	if (need_delay) delay ();
X	topmsg (1, a);
X	topmsg (2, b);
X	topmsg (3, c);
X	need_delay = (a || b || c);
X}
X
Xvoid
Xset_need_delay () {
X	need_delay = 1;
X}
X
Xvoid
X/* VARARGS1 */
Xcomment (buf, a, b, c, d, e, f, g, h)
Xchar *buf;
Xint a, b, c, d, e, f, g, h;
X{
X	if (need_delay) delay ();
X	topmsg (1, 0);
X	topmsg (2, 0);
X	topmsg (3, buf, a, b, c, d, e, f, g, h);
X	need_delay = (buf != 0);
X}
X	
X/*
XGet a string from the user, echoing characters all the while.
X*/
X
Xvoid
Xget_str (buf, sizep)
Xchar *buf;
Xint sizep;
X{
X	(void) echo();
X	get_strq(buf, sizep);
X	(void) noecho();
X}
X
X/*
XGet a string from the user, ignoring the current echo mode.
X*/
X
Xvoid
Xget_strq (buf, sizep)
Xchar *buf;
Xint sizep;
X{
X	sizep = sizep; /* size of buf, currently unused */
X
X	(void) nocrmode ();
X	(void) refresh ();
X	(void) getstr (buf);
X	need_delay = FALSE;
X	info (0, 0, 0);
X	(void) crmode ();
X}
X
X/*
XGet a character from the user and convert it to uppercase.
X*/
X
Xchar
Xget_chx()
X{
X	char c;
X
X	c = get_cq ();
X
X	if (islower(c))
X		return (upper(c));
X	else
X		return (c);
X}
X
X/*
XInput an integer from the user.
X*/
X
Xint
Xgetint (message)
Xchar *message;
X{
X	char buf[STRSIZE];
X	char *p;
X
X	for (;;) { /* until we get a legal number */
X		prompt (message);
X		get_str (buf, sizeof (buf));
X		
X		for (p = buf; *p; p++) {
X			if (*p < '0' || *p > '9') {
X				error ("Please enter an integer.");
X				break;
X			}
X		}
X		if (*p == 0) { /* no error yet? */
X			if (p - buf > 7) /* too many digits? */
X				error ("Please enter a small integer.");
X			else return (atoi (buf));
X		}
X	}
X}
X
X/*
XInput a character from the user with echoing.
X*/
X
Xchar
Xget_c ()
X{
X	char c; /* one char and a null */
X
X	(void) echo ();
X	c = get_cq ();
X	(void) noecho ();
X	return (c);
X}
X
X/*
XInput a character quietly.
X*/
X
Xchar
Xget_cq ()
X{
X	char c;
X
X	(void) crmode ();
X	(void) refresh ();
X	c = getch ();
X	topini (); /* clear information lines */
X	(void) nocrmode ();
X	return (c);
X}
X
X/*
XInput a yes or no response from the user.  We loop until we get
Xa valid response.  We return TRUE iff the user replies 'y'.
X*/
X
Xint
Xgetyn (message)
Xchar *message;
X{
X	char c;
X
X	for (;;) {
X		prompt (message);
X		c = get_chx ();
X
X		if (c == 'Y') return (TRUE);
X		if (c == 'N') return (FALSE);
X
X		error ("Please answer Y or N.");
X	}
X}
X
X/*
XInput an integer in a range.
X*/
X
Xint
Xget_range (message, low, high)
Xchar *message;
Xint low, high;
X{
X	int result;
X
X	for (;;) {
X		result = getint (message);
X
X		if (result >= low && result <= high) return (result);
X
X		error ("Please enter an integer in the range %d..%d.",
X			low, high);
X	}
X}
X
X/*
XPrint a screen of help information.
X*/
X
Xvoid
Xhelp (text, nlines)
Xchar **text;
Xint nlines;
X{
X	int i, r, c;
X	int text_lines;
X
X	text_lines = (nlines + 1) / 2; /* lines of text */
X
X	clear_screen ();
X
X	pos_str (NUMTOPS, 1, text[0]); /* mode */
X	pos_str (NUMTOPS, 41, "See empire.doc for more information.");
X
X	for (i = 1; i < nlines; i++) {
X		if (i > text_lines)
X			pos_str (i - text_lines + NUMTOPS + 1, 41, text[i]);
X		else pos_str (i + NUMTOPS + 1, 1, text[i]);
X	}
X
X	pos_str (text_lines + NUMTOPS + 2,  1, "--Piece---Yours-Enemy-Moves-Hits-Cost");
X	pos_str (text_lines + NUMTOPS + 2, 41, "--Piece---Yours-Enemy-Moves-Hits-Cost");
X
X	for (i = 0; i < NUM_OBJECTS; i++) {
X		if (i >= (NUM_OBJECTS+1)/2) {
X			r = i - (NUM_OBJECTS+1)/2;
X			c = 41;
X		}
X		else {
X			r = i;
X			c = 1;
X		}
X		pos_str (r + text_lines + NUMTOPS + 3, c, 
X			"%-12s%c     %c%6d%5d%6d",
X			piece_attr[i].nickname,
X			piece_attr[i].sname,
X			tolower (piece_attr[i].sname),
X			piece_attr[i].speed,
X			piece_attr[i].max_hits,
X			piece_attr[i].build_time);
X
X	}
X	(void) refresh ();
X}
X
//E*O*F term.c//

echo x - README
sed -e 's/^X//' > "README" << '//E*O*F README//'
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
XC Empire Sources
X
XHistory
X
X	Apparently, this game was originally written outside of Digital,
X	probably at a university.  The game was ported to DEC's VAX/VMS
X	from the TOPS-10/20 FORTRAN sources available around fall 1979.
X	Ed James got hold of the sources at Berkeley and converted
X	portions of the code to C, mostly to use curses for the screen
X	handling.  He published his modified sources on the net in
X	December 1986.  Because this game ran on VMS machines for so
X        long, a previous version is known as VMS Empire.
X
X	In early 1987 I reverse engineered the program and wrote a
X	version completely written in C.  In doing this, I used lots
X	of structures and defined constants, and I attempted to make
X	the code flexible and easy to modify.  The algorithms used
X	in this C version are completely new, the names of the commands
X	have been changed to be more mnemonic, and new commands have
X	been implemented.  Only the format of the display is the same.
X        I suspect that many of my changes are slower and less
X        intelligently implemented than the originals.  Also, I have
X	not implemented some of the original functionality.
X	However, my hope is that the commented C sources I have written
X	will prove far easier to modify and enhance than the original
X        FORTRAN sources.  If you make changes for the better, by
X        all means send Ed James and I a copy.
X
X	The basic game has been heavily modified.  I've changed the
X	types of objects built, modified the parameters on others,
X	and added lots of new kinds of movement functions.  Read
X	the man page for a complete description.
X
X	The file 'bugs' contains lots of ideas for enhancements,
X	and describes the bugs I haven't been able to find.
X
XOrganization
X
X        I have attempted to organize the sources into relatively few
X        coherent pieces.  The pieces are:
X
X        empire.h   -- definitions of data structures
X        extern.h   -- definitions of global variables
X        data.c     -- constant data
X	main.c     -- option parsing
X        empire.c   -- main program loop and outermost command handler
X        usermove.c -- move the user's pieces
X        compmove.c -- move the computer's pieces
X        edit.c     -- handle the user's edit mode commands
X        game.c     -- saving, restoring, and initializing the game board
X        display.c  -- update the screen
X	term.c     -- deal with information area of screen
X        math.c     -- mathematical routines
X        object.c   -- routines for manipulating objects
X	attack.c   -- handle attacks between pieces
X	map.c      -- find paths for moving pieces
X	util.c     -- miscellaneous routines, especially I/O.
X
XDebugging notes
X
X	From command mode, there are two special commands that
X	can be used to turn debugging mode on or off.  "++" turns
X	debugging mode on.  "+-" turns debugging mode off.
X
X	When debugging mode is turned on, the following commands are
X	available:
X
X	"#" -- display a sector of the computer's map.
X
X	"%" -- enter "movie" mode.  The computer continuously makes
X	       moves, and the computer's map is shown on the screen.
X	       This is useful for debugging the algorithm used by the
X	       computer when it makes a move.  Don't confuse this
X	       with saving a movie and replaying it.
X
X	"@" -- enable/disable "trace pathmap" mode.  If this command
X	       is followed by a "+", trace pathmap mode is enabled.
X	       If this command is followed by a "-", trace pathmap
X	       mode is disabled.  In this mode, every time a "pathmap"
X	       is created, it is displayed.  This is useful for
X	       debugging the subroutines that search for an optimal
X	       path along which to move a piece.
X
X	"$" -- enable/disable "print_debug".  This command is also
X	       followed by either a "+" or "-".  In this mode,
X	       various messages will be printed out at times which
X	       may indicate that something is being done non-optimally.
X
X	"&" -- enable/disable "print_vmap".  This command is followed
X	       by a char that specifies the type of vmap to be
X	       displayed.  Values are
X
X		"a" -- army load maps
X		"l" -- transport load maps
X		"u" -- transport unload maps
X		"s" -- ship maps
X		"i" -- pruned explore map
X
X	       Any other character disables the printing of vmaps.
X
X	The program will not provide any prompts for the debugging
X	commands.  If you make a mistake, the computer just beeps.
X
X	You can also replay a saved movie with the normal "W" command
X	when debugging mode is turned on.
X
X	Also, the -DDEBUG flag can be turned on to cause consistency
X	checking to be performed frequently on the internal database.
X	This consistency checking is fairly exhaustive and checks for
X	all sorts of screwed up pointers.  My measurements suggest
X	that consistency checking causes the program to run half
X	as fast.
X
XFinal Notes
X
X	Unfortunately, I have a rather powerful mainframe at my
X	disposal which is somewhere between 10 and 40 times as
X	fast as a 68020 based computer.  This means I can afford
X	to use extremely inefficient algorithms.  I suspect that
X	running this program on a smaller machine, such as a Sun
X	workstation or Vax will not be overly rewarding.  In particular,
X	the computer will take a very long time to move its pieces,
X	and it may not be desirable to save the game after every move.
X	(You mean your system doesn't write out 1/2 megabyte files in a
X	few milliseconds?)  This second problem is easily fixed, but
X	I don't yet have any good ideas for fixing the first problem.
X
X	The size of a saved file can be easily tuned by reducing the
X	LIST_SIZE constant in empire.h.  The only current simple tweak
X	for making the computer move faster is to reduce the size
X	of a map.
X
XChuck Simmons
Xamdahl!chuck
X
XEd James
Xedjames@ic.berkeley.edu
Xucbvax!edjames
//E*O*F README//

echo x - extern.h
sed -e 's/^X//' > "extern.h" << '//E*O*F extern.h//'
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/*
Xextern.h -- define global non-constant storage.
X*/
X
X/* user-supplied parameters */
Xint SMOOTH;        /* number of times to smooth map */
Xint WATER_RATIO;   /* percentage of map that is water */
Xint MIN_CITY_DIST; /* cities must be at least this far apart */
Xint delay_time;
X
Xreal_map_t map[MAP_SIZE]; /* the way the world really looks */
Xview_map_t comp_map[MAP_SIZE]; /* computer's view of the world */
Xview_map_t user_map[MAP_SIZE]; /* user's view of the world */
X
Xcity_info_t city[NUM_CITY]; /* city information */
X
X/*
XThere is one array to hold all allocated objects no matter who
Xowns them.  Objects are allocated from the array and placed on
Xa list corresponding to the type of object and its owner.
X*/
X
Xpiece_info_t *free_list; /* index to free items in object list */
Xpiece_info_t *user_obj[NUM_OBJECTS]; /* indices to user lists */
Xpiece_info_t *comp_obj[NUM_OBJECTS]; /* indices to computer lists */
Xpiece_info_t object[LIST_SIZE]; /* object list */
X
X/* Display information. */
Xint lines; /* lines on screen */
Xint cols; /* columns on screen */
X
X/* constant data */
Xextern piece_attr_t piece_attr[];
Xextern int dir_offset[];
Xextern char *func_name[];
Xextern int move_order[];
Xextern char type_chars[];
Xextern char tt_attack[];
Xextern char army_attack[];
Xextern char fighter_attack[];
Xextern char ship_attack[];
X
Xextern move_info_t tt_load;
Xextern move_info_t tt_explore;
Xextern move_info_t tt_unload;
Xextern move_info_t army_fight;
Xextern move_info_t army_load;
Xextern move_info_t fighter_fight;
Xextern move_info_t ship_fight;
Xextern move_info_t ship_repair;
Xextern move_info_t user_army;
Xextern move_info_t user_army_attack;
Xextern move_info_t user_fighter;
Xextern move_info_t user_ship;
Xextern move_info_t user_ship_repair;
X
Xextern char *help_cmd[];
Xextern char *help_edit[];
Xextern char *help_user[];
Xextern int cmd_lines;
Xextern int edit_lines;
Xextern int user_lines;
X
X/* miscellaneous */
Xlong date; /* number of game turns played */
Xchar automove; /* TRUE iff user is in automove mode */
Xchar resigned; /* TRUE iff computer resigned */
Xchar debug; /* TRUE iff in debugging mode */
Xchar print_debug; /* TRUE iff we print debugging stuff */
Xchar print_vmap; /* TRUE iff we print view maps */
Xchar trace_pmap; /* TRUE if we are tracing pmaps */
Xint win; /* set when game is over */
Xchar jnkbuf[STRSIZE]; /* general purpose temporary buffer */
Xchar save_movie; /* TRUE iff we should save movie screens */
Xint user_score; /* "score" for user and computer */
Xint comp_score;
X
X/* Screen updating macros */
X#define display_loc_u(loc) display_loc(USER,user_map,loc)
X#define display_loc_c(loc) display_loc(COMP,comp_map,loc)
X#define print_sector_u(sector) print_sector(USER,user_map,sector)
X#define print_sector_c(sector) print_sector(COMP,comp_map,sector)
X#define loc_row(loc) ((loc)/MAP_WIDTH)
X#define loc_col(loc) ((loc)%MAP_WIDTH)
X#define row_col_loc(row,col) ((long)((row)*MAP_WIDTH + (col)))
X#define sector_row(sector) ((sector)%SECTOR_ROWS)
X#define sector_col(sector) ((sector)/SECTOR_ROWS)
X#define row_col_sector(row,col) ((int)((col)*SECTOR_ROWS+(row)))
X
X#define loc_sector(loc) \
X	row_col_sector(loc_row(loc)/ROWS_PER_SECTOR, \
X                       loc_col(loc)/COLS_PER_SECTOR)
X		       
X#define sector_loc(sector) row_col_loc( \
X		sector_row(sector)*ROWS_PER_SECTOR+ROWS_PER_SECTOR/2, \
X		sector_col(sector)*COLS_PER_SECTOR+COLS_PER_SECTOR/2)
X		
X/* global routines */
X
Xvoid empire();
X
Xvoid attack();
Xvoid comp_move();
Xvoid user_move();
Xvoid edit();
X
X/* map routines */
Xvoid vmap_cont();
Xvoid rmap_cont();
Xvoid vmap_mark_up_cont();
Xscan_counts_t vmap_cont_scan();
Xscan_counts_t rmap_cont_scan();
Xint map_cont_edge();
Xlong vmap_find_aobj();
Xlong vmap_find_wobj();
Xlong vmap_find_lobj();
Xlong vmap_find_lwobj();
Xlong vmap_find_wlobj();
Xlong vmap_find_dest();
Xvoid vmap_prune_explore_locs();
Xvoid vmap_mark_path();
Xvoid vmap_mark_adjacent();
Xvoid vmap_mark_near_path();
Xlong vmap_find_dir();
Xint vmap_count_adjacent();
Xint vmap_shore();
Xint rmap_shore();
Xint vmap_at_sea();
Xint rmap_at_sea();
X
Xvoid kill_display(); /* display routines */
Xvoid sector_change();
Xint cur_sector();
Xlong cur_cursor();
Xvoid display_loc();
Xvoid display_locx();
Xvoid print_sector();
Xint move_cursor();
Xvoid print_zoom();
Xvoid print_pzoom();
Xvoid print_xzoom();
Xvoid display_score();
X
Xvoid init_game(); /* game routines */
Xvoid save_game();
Xint restore_game();
Xvoid save_movie_screen();
Xvoid replay_movie();
X
Xvoid get_str(); /* input routines */
Xvoid get_strq();
Xchar get_chx();
Xint getint();
Xchar get_c();
Xchar get_cq();
Xint getyn();
Xint get_range();
X
Xvoid rndini(); /* math routines */
Xlong irand();
Xint dist();
Xint sqrt();
X
Xint find_nearest_city();
Xcity_info_t *find_city(); /* object routines */
Xpiece_info_t *find_obj();
Xpiece_info_t *find_nfull();
Xlong find_transport();
Xpiece_info_t *find_obj_at_loc();
Xint obj_moves();
Xint obj_capacity();
Xvoid kill_obj();
Xvoid kill_city();
Xvoid produce();
Xvoid move_obj();
Xvoid move_sat();
Xint good_loc();
Xvoid embark();
Xvoid disembark();
Xvoid describe_obj();
Xvoid scan();
Xvoid scan_sat();
Xvoid set_prod();
X
X/* terminal routines */
Xvoid pdebug();
Xvoid topini();
Xvoid clreol();
Xvoid topmsg();
Xvoid prompt();
Xvoid error();
Xvoid info();
Xvoid comment();
Xvoid extra();
Xvoid huh();
Xvoid help();
Xvoid set_need_delay();
X
X/* utility routines */
Xvoid ttinit();
Xvoid redraw();
Xvoid clear_screen();
Xvoid delay();
Xvoid close_disp();
Xvoid pos_str();
Xvoid addprintf();
Xvoid assert();
Xvoid empend();
Xvoid ver();
Xchar upper();
Xvoid tupper();
Xvoid check();
X
X/* randon routines we use */
Xlong time();
Xvoid exit();
Xvoid perror();
Xvoid srand();
Xchar *strcpy();
//E*O*F extern.h//

echo x - attack.c
sed -e 's/^X//' > "attack.c" << '//E*O*F attack.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/*
Xattack.c -- handle an attack between two pieces.  We do everything from
Xfighting it out between the pieces to notifying the user who won and
Xkilling off the losing object.  Somewhere far above, our caller is
Xresponsible for actually removing the object from its list and actually
Xupdating the player's view of the world.
X
XFind object being attacked.  If it is a city, attacker has 50% chance
Xof taking city.  If successful, give city to attacker.  Otherwise
Xkill attacking piece.  Tell user who won.
X
XIf attacking object is not a city, loop.  On each iteration, select one
Xpiece to throw a blow.  Damage the opponent by the strength of the blow
Xthrower.  Stop looping when one object has 0 or fewer hits.  Kill off 
Xthe dead object.  Tell user who won and how many hits her piece has left,
Xif any.
X*/
X
X#include "empire.h"
X#include "extern.h"
X
Xvoid
Xattack (att_obj, loc)
Xpiece_info_t *att_obj;
Xlong loc;
X{
X	void attack_city();
X	void attack_obj();
X	
X	if (map[loc].contents == '*') /* attacking a city? */
X		attack_city (att_obj, loc);
X	else attack_obj (att_obj, loc); /* attacking a piece */
X}
X
Xvoid
Xattack_city (att_obj, loc)
Xpiece_info_t *att_obj;
Xlong loc;
X{
X	city_info_t *cityp;
X	int att_owner, city_owner;
X
X	cityp = find_city (loc);
X	ASSERT (cityp);
X	
X	att_owner = att_obj->owner;
X	city_owner = cityp->owner;
X
X	if (irand (2) == 0) { /* attack fails? */
X		if (att_owner == USER)
X			comment ("The scum defending the city crushed your attacking blitzkrieger.");
X
X		else if (city_owner == USER) {
X			comment ("Your city at %d is under attack.",
X				cityp->loc);
X		}
X		kill_obj (att_obj, loc);
X	}
X	else { /* attack succeeded */
X		kill_city (cityp);
X		cityp->owner = att_owner;
X		kill_obj (att_obj, loc);
X
X		if (att_owner == USER) {
X			error ("City at %d has been subjugated!",
X				cityp->loc);
X
X			extra ("Your army has been dispersed to enforce control.");
X			set_prod (cityp);
X		}
X		else if (city_owner == USER) {
X			comment ("City at %d has been lost to the enemy!",
X				cityp->loc);
X		}
X	}
X	/* let city owner see all results */
X	if (city_owner != UNOWNED) scan (MAP(city_owner), loc);
X}
X
X/*
XAttack a piece other than a city.  The piece could be anyone's.
XFirst we have to figure out what is being attacked.
X*/
X
Xvoid
Xattack_obj (att_obj, loc)
Xpiece_info_t *att_obj;
Xlong loc;
X{
X	void describe(), survive();
X	
X	piece_info_t *def_obj; /* defender */
X	int owner;
X
X	def_obj = find_obj_at_loc (loc);
X	ASSERT (def_obj != NULL); /* can't find object to attack? */
X	
X	if (def_obj->type == SATELLITE) return; /* can't attack a satellite */
X
X	while (att_obj->hits > 0 && def_obj->hits > 0) {
X		if (irand (2) == 0) /* defender hits? */
X		     att_obj->hits -= piece_attr[def_obj->type].strength;
X		else def_obj->hits -= piece_attr[att_obj->type].strength;
X	}
X
X	if (att_obj->hits > 0) { /* attacker won? */
X		describe (att_obj, def_obj, loc);
X		owner = def_obj->owner;
X		kill_obj (def_obj, loc); /* kill loser */
X		survive (att_obj, loc); /* move attacker */
X	}
X	else { /* defender won */
X		describe (def_obj, att_obj, loc);
X		owner = att_obj->owner;
X		kill_obj (att_obj, loc);
X		survive (def_obj, loc);
X	}
X	/* show results to first killed */
X	scan (MAP(owner), loc);
X}
X
X/*
XHere we look to see if any cargo was killed in the attack.  If
Xa ships contents exceeds its capacity, some of the survivors
Xfall overboard and drown.  We also move the survivor to the given
Xlocation.
X*/
X
Xvoid
Xsurvive (obj, loc)
Xpiece_info_t *obj;
Xlong loc;
X{
X	while (obj_capacity (obj) < obj->count)
X		kill_obj (obj->cargo, loc);
X		
X	move_obj (obj, loc);
X}
X
Xvoid
Xdescribe (win_obj, lose_obj, loc)
Xpiece_info_t *win_obj, *lose_obj;
Xlong loc;
X{
X	char buf[STRSIZE];
X	char buf2[STRSIZE];
X	int diff;
X	
X	*buf = '\0';
X	*buf2 = '\0';
X	
X	if (win_obj->owner != lose_obj->owner) {
X		if (win_obj->owner == USER) {
X			user_score += piece_attr[lose_obj->type].build_time;
X			
X			topmsg (1, "Enemy %s at %d destroyed.",
X				piece_attr[lose_obj->type].name,
X				loc);
X				
X			topmsg (2, "Your %s has %d hits left.",
X			       piece_attr[win_obj->type].name,
X			       win_obj->hits);
X				
X			diff = win_obj->count - obj_capacity (win_obj);
X			if (diff > 0) switch (win_obj->cargo->type) {
X			case ARMY:
X			     topmsg (3,
X				     "%d armies fell overboard and drowned in the assault.",
X				     diff);
X			     break;
X			case FIGHTER:
X			     topmsg (3,
X				     "%d fighters fell overboard and were lost in the assault.",
X				     diff);
X			     break;
X			}
X		}
X		else {
X			comp_score += piece_attr[lose_obj->type].build_time;
X			
X			topmsg (3, "Your %s at %d destroyed.",
X				piece_attr[lose_obj->type].name,
X				loc);
X		}
X		set_need_delay ();
X	}
X}
//E*O*F attack.c//

echo x - math.c
sed -e 's/^X//' > "math.c" << '//E*O*F math.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/*
Xmath.c -- various mathematical routines.
X
XThis file contains routines used to create random integers.  The
Xinitialization routine 'rndini' should be called at program startup.
XThe flavors of random integers that can be generated are:
X
X    irand(n) -- returns a random integer in the range 0..n-1
X    rndint(a,b) -- returns a random integer in the range a..b
X
XOther routines include:
X
X    dist (a, b) -- returns the straight-line distance between two locations.
X*/
X
X#include "empire.h"
X#include "extern.h"
X
Xvoid rndini()
X{
X	srand((unsigned)(time(0) & 0xFFFF));
X}
X
Xlong irand(high)
Xlong high;
X{
X	if (high < 2) {
X		return (0);
X	}
X	return (rand() % high);
X}
X
Xint rndint(minp, maxp)
Xint minp, maxp;
X{
X	int size;
X
X	size = maxp - minp + 1;
X	return ((rand() % size) + minp);
X}
X
X/*
XReturn the distance between two locations.  This is simply
Xthe max of the absolute differnce between the x and y coordinates.
X*/
X
X#define MIN(a,b) ((a)<(b) ? (a) : (b))
X#define MAX(a,b) ((a)>(b) ? (a) : (b))
X#define ABS(a) ((a) < 0 ? -(a) : (a))
X
Xdist (a, b)
Xlong a, b;
X{
X	int ax, ay, bx, by;
X
X	ax = loc_row (a);
X	ay = loc_col (a);
X	bx = loc_row (b);
X	by = loc_col (b);
X
X	return (MAX (ABS (ax-bx), ABS (ay-by)));
X}
X
X/*
XFind the square root of an integer.  We actually return the floor
Xof the square root using Newton's method.
X*/
X
Xint sqrt (n)
Xint n;
X{
X	int guess;
X	
X	ASSERT (n >= 0); /* can't take sqrt of negative number */
X
X	if (n <= 1) return (n); /* do easy cases and avoid div by zero */
X		
X	guess = 2; /* gotta start somewhere */
X	guess = (guess + n/guess) / 2;
X	guess = (guess + n/guess) / 2;
X	guess = (guess + n/guess) / 2;
X	guess = (guess + n/guess) / 2;
X	guess = (guess + n/guess) / 2;
X	
X	if (guess * guess > n) guess -= 1; /* take floor */
X	return (guess);
X}
//E*O*F math.c//

echo x - makefile
sed -e 's/^X//' > "makefile" << '//E*O*F makefile//'
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# Change the line below for your compiler.  You will probably want
X# 'cc'.  ('mcc' is a "merge compiler" that takes errors, merges them
X# with the source, and tosses you into an editor.  I also use various
X# cross compilers on this line.)
X
X#CC = mcc
X#CC = cpccc
XCC = cc
X
X# Change the line below for your system.  If you are on a Sun or Vax,
X# you may want BSD.
X
X#SYS = BSD
XSYS = SYSV
X
X# Use -g to compile the program for debugging.
X
X#DEBUG = -g -DDEBUG
XDEBUG = -O
X
X# Use -p to profile the program.
X#PROFILE = -p -DPROFILE
XPROFILE =
X
X# Define all necessary libraries.  'curses' is necessary.  'termcap'
X# is needed on BSD systems.
XLIBS = -lcurses
X#LIBS = -lcurses -ltermcap
X
X# You shouldn't have to modify anything below this line.
X
XFLAGS = $(DEBUG) $(PROFILE) -c -D$(SYS)
XCCCMD = $(CC) $(FLAGS)
XINS   = /etc/install
X
XFILES = \
X	attack.c \
X	compmove.c \
X	data.c \
X	display.c \
X	edit.c \
X	empire.c \
X	game.c \
X	main.c \
X	map.c \
X	math.c \
X	object.c \
X	term.c \
X	usermove.c \
X	util.c
X
XSHARFILES = COPYING $(FILES) bugs README makefile empire.6 empire.h extern.h
X
XOFILES = \
X	attack.o \
X	compmove.o \
X	data.o \
X	display.o \
X	edit.o \
X	empire.o \
X	game.o \
X	main.o \
X	map.o \
X	math.o \
X	object.o \
X	term.o \
X	usermove.o \
X	util.o
X
Xall: empire
X
Xempire: $(OFILES)
X	$(CC) $(PROFILE) -o empire $(OFILES) $(LIBS)
X
X$(OFILES): extern.h empire.h
X	$(CCCMD) $<
X
Xlint: $(FILES)
X	lint -u -D$(SYS) $(FILES) -lcurses
X
Xclean:
X	rm -f *.o
X
Xclobber: clean
X	rm -f empire
X
Xsharsplit: $(SHARFILES)
X	sharsplit -m -r -o empire $(SHARFILES)
X
Xinstall: empire
X	$(INS) -o -f /usr/local/games empire
X
Xinstallman: empire.6
X	$(INS) -f /usr/local/man/man6 empire.6
//E*O*F makefile//

echo x - MANIFEST
sed -e 's/^X//' > "MANIFEST" << '//E*O*F MANIFEST//'
Xempire1.ar               46987
X     main.c               2090
X     object.c            14016
X     map.c               30881
X
Xempire2.ar               47903
X     empire.c             6282
X     edit.c              11119
X     compmove.c          30502
X
Xempire3.ar               47979
X     empire.h             8117
X     display.c           10953
X     bugs                28909
X
Xempire4.ar               46435
X     game.c              21323
X     usermove.c          25112
X
Xempire5.ar               46430
X     COPYING              7420
X     data.c               7529
X     util.c               7827
X     empire.6            23654
X
Xempire6.ar               28563
X     MANIFEST             1760 (est)
X     makefile             1827
X     math.c               1959
X     attack.c             4813
X     extern.h             5788
X     README               5950
X     term.c               6466
//E*O*F MANIFEST//

echo shar: End of archive 6 \(of 6\).
cp /dev/null ark6isdone
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

chuck@amdahl.uts.amdahl.com (Charles Simmons) (04/12/88)

Apparently part 5 didn't make it to all sites.  Here it is again.
If it doesn't work this time, send me mail and I'll mail copies
by hand to individual people.  In your mail, please send me the
path the message took to reach your site, and please send me a
copy of the last 5 lines or so that you received.  This may help
me to track down bugs, or at least increase my knowledge about
possible bugs in the network software.

Thanks, Chuck

# 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:
# empire.6 util.c data.c COPYING

echo x - empire.6
sed -e 's/^X//' > "empire.6" << '//E*O*F empire.6//'
X.\" %W% %G% %U% - (c) Copyright 1987, 1988 Chuck Simmons */
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.TH Empire 6
X.SH NAME
Xempire -- the wargame of the century
X.SH SYNOPSIS
Xempire [-w water] [-s smooth] [-d delay]
X.SH DESCRIPTION
XEmpire is a simulation of a full-scale war between two
Xemperors, the computer and you.  Naturally, there is only
Xroom for one, so the object of the game is to destroy
Xthe other.  The computer plays by the same rules that you
Xdo.
X.TP 10
X.BI \-w water\^
XThis option controls the amount of water on the map.
XThis is given as the percentage of the map which should
Xbe water.  The default is 70% water.  \f2water\fP
Xmust be an integer in the between 10 and 90 inclusive.
X.TP
X.BI \-s smooth\^
XThis controls the smoothness of the map.  A low value
Xwill produce a highly chaotic map with lots of small
Xislands or lakes.  A high value will produce a map
Xwith a few scattered continents.  Be forewarned that
Xa high value will cause the program to take a long
Xtime to start up.  The default value is 5.
X.TP
X.BI \-d delay\^
XThis option controls the length of time the computer will
Xdelay after printing informational messages at the top of
Xthe screen.  \f2delay\fP is specified in milliseconds.  The
Xdefault value is 2000 which allows the user two seconds to
Xread a message.
X.SH EXAMPLES
Xempire -w90 -s2
X.P
XThis produces a map with many islands.
X.P
Xempire -w50 -s0
X.P
XThis produces a really strange map.  These values
Xare not recommended for the faint at heart.
X.P
Xempire -w10
X.P
XThis produces a map with lots of land and a few lakes.
XThe computer will have a hard time on this sort of map
Xas it will try and produce lots of troop transports,
Xwhich are fairly useless.
X.SH INTRODUCTION
XEmpire is a war game played between you and the user.  The world
Xon which the game takes place is a square rectangle containing cities,
Xland, and water.  Cities are used to build armies, planes, and ships
Xwhich can move across the world destroying enemy pieces, exploring,
Xand capturing more cities.  The objective of the game is to destroy
Xall the enemy pieces, and capture all the cities.
X.P
XThe world is a rectangle 60 by 100 squares on a side.  The world
Xconsists of sea (.), land (+), uncontrolled
Xcities (*), computer-controlled cities (X), and cities that you
Xcontrol (O).
X.P
XThe world is displayed on the player's screen during
Xmovement.  (On terminals with small screens, only a portion of
Xthe world is shown at any one time.)
XEach piece is represented by a unique
Xcharacter on the map.  With a few exceptions, you can
Xonly have one piece on a given location.  On the map, you
Xare shown only the 8 squares adjacent to your units.
XThis information is updated before and during each of your moves.
XThe map displays the most recent information known.
X.P
XThe game starts by assigning you one city and the
Xcomputer one city.  Cities can produce new pieces.  Every
Xcity that you own produces more pieces for you according
Xto the cost of the desired piece.  The typical play of the
Xgame is to issue the Automove command until you decide
Xto do something special.  During movement in each round,
Xthe player is prompted to move each piece that does not
Xotherwise have an assigned function.
X.P
XMap coordinates are 4-digit numbers.  The first two
Xdigits are the row, the second two digits are the
Xcolumn.
X.SH PIECES
XThe pieces are as follows:
X.P
X.TS
Xcenter box tab(;);
Xl | c | c | r | r | r | c.
XPiece;You;Enemy;Moves;Hits;Str;Cost
X_
XArmy;A;a;1;1;1;5(6)
XFighter;F;f;8;1;1;10(12)
XPatrol Boat;P;p;4;1;1;15(18)
XDestroyer;D;d;2;3;1;20(24)
XSubmarine;S;s;2;2;3;20(24)
XTroop Transport;T;t;2;1;1;30(36)
XAircraft Carrier;C;c;2;8;1;30(36)
XBattleship;B;b;2;10;2;40(48)
XSatellite;Z;z;10;--;--;50(60)
X.TE
X.P
XThe second column shows the map representation for your
Xunits.
X.P
XThe third shows the representations of enemy units.
X.P
XMoves is the number of squares that the unit can move in a
Xsingle round.
X.P
XHits is the amount of damage a unit can take before it
Xis destroyed.
X.P
XStrength is the amount of damage a unit can inflict upon an
Xenemy during each round of an attack.
X.P
XCost is the number of rounds needed for a city to produce
Xthe piece.
X.P
XThe number in parenthesis is the cost for a city to
Xproduce the first unit.
X.P
XEach piece has certain advantages associated with it that
Xcan make it useful.  One of the primary strategic aspects
Xof this game is deciding which pieces will be produced
Xand in what quantities.
X.P
X\f3Armies\f1 can only move on land, and are the only piece that can
Xmove on land.  Only armies can capture cities.
XThis means that you must produce armies in order to win the
Xgame.  Armies have a 50% chance of capturing a city when they
Xattack.  (Attacking one's own city results in the army's
Xdestruction.  Armies that move onto the sea will drown.
XArmies can attack objects at sea, but even if they win, they
Xwill drown.)  Armies can be carried by troop transports.
XIf an army is moved onto a troop transport, then whenever
Xthe transport is moved, the army will be moved with the
Xtransport.  You cannot attack any piece at sea while on a
Xtransport.
X.P
X\f3Fighters\f1 move over both land and sea, and they move 8 squares
Xper round.  Their high speed and great mobility make fighters
Xideal for exploring.  However, fighters must periodically land
Xat user-owned cities for refueling.  A fighter can travel 32
Xsquares without refeuling.  Fighters are also shot down if they
Xattempt to fly over a city which is not owned by the user.
X.P
X\f3Patrol boats\f1 are fast but lightly armored.  Therefore
Xthey are useful for patrolling ocean waters and exploring.
XIn an attack against a stronger boat, however, patrol boats
Xwill suffer heavy casulties.
X.P
X\f3Destroyers\f1 are fairly heavily armored and reasonably quick
Xto produce.  Thus they are useful for destroying enemy transports
Xwhich may be trying to spread the enemy across the face of the
Xworld.
X.P
XWhen a \f3submarine\f1 scores a hit, 3 hits are exacted instead of 1.
XThus submarines can inflict heavy damage in a fight against
Xheavily armored boats.  Notice that healthy submarines will
Xtypically defeat healthy destroyers two-thirds of the time.
XHowever, a submarine will defeat a fighter about two-thirds
Xof the time, while a destroyer will defeat a fighter three-fourths
Xof the time.
X.P
X\f3Troop transports\f1 are the only pieces that can carry armies.
XA maximum of six armies can be carried by a transport.
XOn any world containing a reasonable amount of water,
Xtransports will be a critical resource in winning the game.
XNotice that the weakness of transports implies they need
Xprotection from stronger ships.
X.P
X\f3Aircraft carriers\f1 are the only ships that can
Xcarry fighters.  Carriers carry a maximum of the number
Xof hits left of fighters.  Fighters are refueled when they
Xland on a carrier.
X.P
X\f3Battleships\f1 are similar to destroyers except that they are
Xmuch stronger.
X.P
X\f3Satellites\f1 are only useful for reconaissance.  They can not
Xbe attacked.  They are launched in a random diagonal orbit, and
Xstay up for 50 turns.  They can see one square farther than other
Xobjects.
X.P
XAll ships can move only on sea.  Ships can also dock in a
Xuser-owned city.  Docked ships have damage repaired at the
Xrate of 1 hit per turn.  Ships which have suffered a lot
Xof damage will move more slowly.
X.P
XBecause of their ability
Xto be repaired, ships with lots of hits such as Carriers and
XBattleships have an additional advantage.
XAfter suffering minor damage while destroying enemy shipping,
Xthese ships can sail back to port and be quickly repaired before
Xthe enemy has time to replenish her destroyed shipping.
X.P
XThe following table gives the probability that the piece listed
Xon the side will defeat the piece listed at the top in a battle.
X(The table assumes that both pieces are undamaged.)
X.P
X.TS
Xcenter box tab(;);
Xl | c | c | c | c | c | c | c | c.
X;A;F;P;D;S;T;C;B
X_
XA;50%;50%;50%;25%;33%;50%;13%;10%
XF;50%;50%;50%;25%;33%;50%;13%;10%
XP;50%;50%;50%;25%;33%;50%;13%;10%
XD;75%;75%;75%;50%;33%;75%;27%;17%
XS;67%;67%;67%;67%;50%;67%;40%;20%
XT;50%;50%;50%;25%;33%;50%;13%;10%
XC;88%;88%;88%;73%;60%;88%;50%;29%
XB;90%;90%;90%;84%;80%;90%;71%;50%
X.TE
X.P
XNotice, however, that when a ship has been damaged, the odds of
Xbeing defeated can go up quite a bit.  For example, a healthy
Xsubmarine has a 25% chance of defeating a battleship that has
Xhad one hit of damage done to it, and a healthy submarine has
Xa 50% chance of defeating a carrier which has suffered two hits
Xof damage.
X.SH "MOVEMENT FUNCTIONS"
XThere are a variety of movement functions.  The movement functions
Xof pieces can be specified in user mode and edit mode.
XCities can have movement functions set for each type of piece.
XWhen a movement function for a type of pieces is set for a city,
Xthen every time that type of piece appears in the city, the piece
Xwill acquire that movement function.  Be forewarned that moving
Xloaded transports or loaded carriers into a city can have undesirable
Xside effects.
X.P
XNormally, when a movement
Xfunction has been specified, the piece will continue moving according to
Xthat function until one of the following happen:
X.TP 5
X.B *
XAn enemy piece or unowned city appears next to the piece.  In this case
Xthe piece will be completely awoken, unless its movement function has
Xbeen set to a specific destination.
XArmies on ships and pieces inside
Xcities will not be awoken if the enemy piece is gone by the time it is
Xtheir turn to move.
X.TP
X.B *
XYou explicitly awaken the piece.
X.TP
X.B *
XThe piece can no longer move in accordance with its programmed function.
XIn this case, the piece will awaken \f2temporarily\fP.  You will be asked
Xto move the piece at which time you may awaken it.
X.TP
X.B *
XThe piece is a fighter which has just enough fuel (plus a small reserve)
Xto get to the nearest city.
XIn this case,
Xthe piece will awaken completely, unless its movement function has
Xbeen set to a specific destination, or its movement function has been
Xset to \f2land\fP.
X.P
XThe rationale behind this complexity is that fighters must be awoken
Xcompletely before they are out of range of a city
Xto prevent one from accidentally forgetting to waken the
Xfighter and then watching it fly off to its doom.  However, it is presumed
Xthat when a path is set for the fighter, the fighter is not in danger of
Xrunning out of fuel.
X.P
XPieces do not completely awaken when their function has been set to a
Xdestination because it is slightly time consuming to reset the destination,
Xbut very simple (one keystroke) to wake the piece.
X.P
XThe movement functions are:
X.TP 10
X.B Attack
XThis function applies only to armies.  When this function is set,
Xthe army will move toward the nearest enemy city, unowned city, or
Xenemy army.  This is useful when fighting off an invading enemy or
Xtaking over a new continent.  When an army is set to this mode,
Xit will also explore nearby territory.  This tends to make
Xthe "grope" movement mode pretty useless.
X.TP
X.B Awake
XWhen pieces are awake, you will be asked for the direction in which
Xthe piece should move on each turn.
X.TP
X.B Fill
XThis function applies to carriers and transports.  When this function is
Xspecified, these ships sleep until they have been filled with fighters or
Xarmies respectively.
X.TP
X.B Grope
XThis function causes a piece to explore.  The piece heads toward the nearest
Xunseen square of the map on each of its moves.  Some attempt is made to 
Xexplore in an optimal fashion.
X.TP
X.B Land
XThis function applies to fighters and causes the fighter to head toward
Xthe nearest transport or carrier.
X.TP
X.B Random
XThis movement function causes a piece to move at random to an adjacent
Xempty square.
X.TP
X.B Sentry
XThis movement function puts a piece to sleep.
XThe function of a city cannot be set to 'sleep'.
X.TP
X.B Transport
XThis movement function only works on armies.  The army sleeps until
Xan unfull transport passes by, at which point the army wakes up and
Xboards the transport.
X.TP
X.B Upgrade
XThis movement function only works with ships.  The ship will move
Xto the nearest owned city and remain there until it is repaired.
X.TP
X.B <dir>
XPieces can be set to move in a specified direction.
X.TP
X.B <dest>
XPieces can be set to move toward a specified square.  In this movement
Xmode, pieces take a shortest path toward the destination.  Pieces moving
Xin accordance with this function prefer diagonal moves that explore
Xterritory.  Because of this, the movement of the piece may be
Xnon-intuitive.
X.P
XAs examples of how to use these movement functions, typically
Xwhen I have a new city on a continent, I set the Army function of the
Xcity to \f2attack\f1.  Whenever an army is produced, it merrily goes off
Xon its way exploring the continent and moving towards unowned cities
Xor enemy armies or cities.
X.P
XI frequently set the ship functions for cities that are far from the
Xfront to automatically move ships towards the front.
X.P
XWhen I have armies on a continent, but there is nothing to explore
Xor attack, I move the army to the shore and use the \f2transport\f1
Xfunction to have that army hop aboard the first passing transport.
X.SH COMMANDS
XThere are three command modes.  The first of these is "command mode".
XIn this mode, you give commands that affect the game as a whole.
XIn the second mode, "move mode", you give commands to move your
Xpieces.  The third mode is "edit mode", and in this mode you can
Xedit the functions of your pieces and examine various portions of
Xthe map.
X.P
XAll commands are one character long.  The full mnemonic names are
Xlisted below as a memorization aid.
XThe mnemonics are somewhat contrived because there are so few
Xcharacters in the English language.  Too bad this program isn't
Xwritten in Japanese, neh?
X.P
XIn all command modes, typing "H" will print out a screen of help
Xinformation, and typing <ctrl-L> will redraw the screen.
X.P
X.SH COMMAND MODE
XIn command mode, the computer will prompt you for your orders.
XThe following commands can be given at this time:
X.TP 10
X.B Automove
XEnter automove mode.  This command begins a new round of movement.
XYou will remain in move mode after each of the computer's turns.
X(In move mode, the "O" command will return you to command mode
Xafter the computer finishes its next turn.
X.TP
X.B City
XGive the computer a random unowned city.  This command is useful if you
Xfind that the computer is getting too easy to beat.
X.TP
X.B Date
XThe current round is displayed.
X.TP
X.B Examine
XExamine the enemy's map.  This command is only valid after the computer
Xhas resigned.
X.TP
X.B File
XPrint a copy of the map to the specified file.
X.TP
X.B Give
XThis command gives the computer a free move.
X.TP
X.B J
XEnter edit mode where you can examine and change the functions
Xassociated with your pieces and cities.
X.TP
X.B Move
XEnter move mode for a single round.
X.TP
X.B N
XGive the computer the number of free moves you specify.
X.TP
X.B Print
XDisplay a sector on the screen.
X.TP
X.B Quit
XQuit the game.  (Note that your game will be saved in 'empsave.dat'
Xafter each command you give, and after each move you make.)
X.TP
X.B Restore
XRestore the game from empsave.dat.  (Currently, this command is useless
Xsince the game is restored when the program first starts up, and the
Xgame is saved after each move.)
X.TP
X.B Save
XSave the game in empsave.dat.  (Currently, this command is useless
Xsince the game is saved after each of your commands and after each move.)
X.TP
X.B Trace
XThis command toggles a flag.  When the flag is set,
Xafter each move, either yours or the computer's,
Xa picture of the world is written out to the file
X'empmovie.dat'.  \f3Watch out!  This command produces lots of
Xoutput.\f1
X.TP
X.B Watch
XThis command allows you to watch a saved movie.
XThe movie is displayed in a condensed version so that
Xit will fit on a single screen, so the output may be
Xa little confusing.  This command is only legal if the
Xcomputer resigns.  If you lose the game, you cannot replay
Xa movie to learn the secrets of how the computer beat you.
XNor can you replay a movie to find out the current positions
Xof the computer's pieces.  When replaying a movie, it is
Xrecommended that you use the \f2-d\f1 option to set the delay
Xto around 2000 milliseconds or so.  Otherwise the screen will be
Xupdated too quickly for you to really grasp what is going on.
X.TP
X.B Zoom
XDisplay a condensed version of the map on the screen.  The user map is
Xdivided into small rectangles.  Each rectangle is displayed as one square
Xon the screen.  If there is a city in a rectangle, then it
Xis displayed.  Otherwise enemy pieces are displayed, then user pieces,
Xthen land, then water, and then unexplored territory.  When pieces are
Xdisplayed, ships are preferred to fighters and armies.
X.SH MOVE MODE
XIn move mode, the cursor will appear on the screen at the position
Xof each piece that needs to be moved.  You can then give commands
Xto move the piece.  Directions to move are specified by the
Xfollowing keys:
X.P
X.fp 5 TT
X.ft 5
X.nf
X        QWE
X        A D
X        ZXC
X.fi
X.ft 1
X.P
XThese keys move in the direction of the key from S.  The
Xcharacters are not echoed and only 1 character is
Xaccepted, so there is no need for a <Return>.  Hit the <Space>
Xbar if you want the piece to stay put.
X.P
XOther commands are:
X.TP 10
X.B Build
XChange the production of a city.
X.TP
X.B Fill
XSet the function of a troop transport or aircraft carrier to
X\f2fill\f1.
X.TP
X.B Grope
XSet the function of a piece to \f2grope\f1.
X.TP
X.BI I dir
XSet the direction for a piece to move.
X.TP
X.B J
XEnter edit mode.
X.TP
X.B Kill
XWake up the piece.  If the piece is a transport or carrier,
Xpieces on board will not be awoken.
X.TP
X.B Land
XSet a fighter's function to \f2land\f1.
X.TP
X.B Out
XCancel automove mode.  At the end of the round, you will
Xbe placed in command mode.
X.TP
X.B Print
XRedraw the screen.
X.TP
X.B Random
XSet a piece's function to \f2random\f1.
X.TP
X.B Sentry
XSet a piece's function to \f2sentry\f1.
X.TP
X.B Transport
XSet an army's function to \f2transport\f1.
X.TP
X.B Upgrade
XSet a ship's function to \f2upgrade\f1.
X.TP
X.BI V "piece func"
XSet the city movement function for the specified piece
Xto the specified function.  For example, typing "VAY" would
Xset the city movement function for armies to \f2attack\f1.  Whenever
Xan army is produced in the city (or whenever a loaded transport
Xenters the city), the army's movement function
Xwould be set to \f2attack\f1.
X.TP
X.B Y
XSet an army's function to \f2attack\f1.
X.TP
X.B ?
XDisplay information about the piece.  The
Xfunction, hits left, range, and number of items on board are
Xdisplayed.
X.P
XAttacking something is accomplished by  moving  onto  the
Xsquare of the unit you wish to attack.  Hits are traded
Xoff at 50% probability of a hit landing on one or the
Xother units until one unit is totally destroyed.  There
Xis only 1 possible winner.
X.P
XYou are "allowed" to do \f3fatal\f1 things like attack your
Xown cities or other pieces.  If you try to make a fatal
Xmove, the computer will warn you and give you a chance to
Xchange your mind.
X.P
XYou cannot move onto the edge of the world.
X.SH EDIT MODE
XIn edit mode, you can move around the world and examine pieces
Xor assign them new functions.
XTo move the cursor around, use the standard direction
Xkeys.
XOther commands are:
X.TP 10
X.B Build
XChange the production of the city under the cursor.
XThe program will prompt for the new production, and you
Xshould respond with the key corresponding to the letter of the piece
Xthat you want produced.
X.TP
X.B Fill
XSet a transport's or carrier's function to \f2fill\f1.
X.TP
X.B Grope
XSet a piece's function to \f2grope\f1.
X.TP
X.BI I dir
XSet the function of a piece (or city) to the specified direction.
X.TP
X.B Kill
XWake all pieces at the current location.  If the location is a city,
Xthe fighter path will also be canceled.
X.TP
X.B Mark
XSelect the piece or city at the current location.  This command
Xis used with the "N" command.
X.TP
X.B N
XSet the destination of the piece previously selected with the "M"
Xcommand to the current square.
X.TP
X.B Out
XExit edit mode.
X.TP
X.BI Print sector
XDisplay a new sector of the map.  The map is divided into
Xten sectors of size 20 by 70.  Sector zero is in the upper-left
Xcorner of the map.  Sector four is in the lower-left corner of
Xthe map.  Sector five is in the upper-right corner, and sector
Xnine is in the lower-right corner.
X.TP
X.B Random
XSet a piece to move randomly.
X.TP
X.B Sentry
XPut a piece to sleep.
X.TP
X.B Transport
XSet an army's function to \f2transport\f1.
X.TP
X.B Upgrade
XSet a ship's function to \f2upgrade\f1.
X.TP
X.BI V "piece func"
XSet the city movement function for a piece.
X.TP
X.B Y
XSet an army's function to \f2attack\f1.
X.TP
X.B ?
XDisplay information about a piece or city.
XFor a city, the production, time of completion of
Xthe next piece, movement functions, and the number of fighters and ships
Xin the city are displayed.
X.P
XNote that you cannot directly affect anything inside a city with
Xthe editor.
X.SH HINTS
XAfter you have played this game for a while, you will probably
Xfind that the computer is immensely easy to beat.  Here are some
Xideas you can try that may make the game more interesting.
X.TP 5
X.B *
XGive the computer one or more extra cities before starting the game.
X.TP
X.B *
XTry playing the game with a low smoothness value (try using the
X-s2 or even -s0 option).
X.TP
X.B *
XWhen starting the game, the program will ask you what difficulty
Xlevel you want.  Here "difficulty level" is a misnomer.  To compute
Xa difficulty level, the program looks at each continent and counts
Xthe number of cities on the continents.  A high "difficulty level"
Xgives the computer a large continent with many cities, while the
Xuser gets a small continent with few cities.  A low "difficulty level"
Xhas the opposite effect.  It may be the case that the computer will
Xplay better when the "difficulty level" is low.  The reason for this
Xis that the computer is forced to move armies to multiple continents
Xearly in the game.
X.SH HISTORY
XApparently, this game was originally written outside of Digital,
Xprobably at a university.  The game was ported to DEC's VAX/VMS
Xfrom the TOPS-10/20 FORTRAN sources available around fall 1979.
XThe original authors listed in my old documentation are
XMario DeNobili and Thomas N. Paulson.
XSupport for different terminal types was added by Craig Leres.
X.P
XEd James got hold of the sources at Berkeley and converted
Xportions of the code to C, mostly to use curses for the screen
Xhandling.  He published his modified sources on the net in
XDecember 1986.  Because this game ran on VMS machines for so
Xlong, a previous version is known as VMS Empire.
X.P
XIn 1987 Chuck Simmons at Amdahl
Xreverse engineered the program and wrote a
Xversion completely written in C.  In doing so, he completely
Xmodified the computer strategy, the commands, the piece types,
Xmany of the piece attributes, and the algorithm for creating maps.
X.SH FILES
X.TP 10
X\f2empsave.dat\f1
Xholds a backup of the game.  Whenever empire is run,
Xit will reload any game in this file.
X.TP
X\f2empmovie.dat\f1
Xholds a history of the game so that the game can be replayed as
Xa "movie".
X.SH BUGS
XNo doubt numerous.
X.P
XSatellites are not completely implemented.  You should be able to
Xmove to a square that contains a satellite, but the program won't
Xlet you.  Enemy satellites should not cause your pieces to awaken.
X.SH AUTHORS
X.nf
XOriginal concept by Mario DeNobili and Thomas N. Paulson.
XSupport for different terminal types added by Craig Leres.
XCurses support added by Ed James.
XC/Unix version written by Chuck Simmons
X.fi
X.SH COPYLEFT
X.fn
XCopyright (C) 1987, 1988 Chuck Simmons
X
XSee the file COPYING, distributed with empire, for restriction
Xand warranty information.
X.fi
//E*O*F empire.6//

echo x - util.c
sed -e 's/^X//' > "util.c" << '//E*O*F util.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/*
Xutil.c -- various utility routines.
X*/
X
X#include <curses.h>
X#include <ctype.h>
X#include "empire.h"
X#include "extern.h"
X
X
X/*
XConvert a string to uppercase.
XShirley this is defined elsewhere?
X*/
X
X#include <ctype.h>
X
Xvoid
Xtupper (str)
Xchar	*str;
X{
X	while (*str) {
X		if (islower (*str)) *str = upper (*str);
X		str++;
X	}
X}
X
X/*
XConvert a character to uppercase (if it is lowercase)
X*/
X
Xchar
Xupper (c)
Xchar c;
X{
X	if (islower (c))
X		return toupper (c);
X	else return c;
X}
X
X/*
XClear the end of a specified line starting at the specified column.
X*/
X
Xvoid
Xclreol(linep, colp)
Xint linep, colp;
X{
X	(void) move (linep, colp);
X	(void) clrtoeol();
X}
X
X/*
XInitialize the terminal.
X*/
X
Xvoid
Xttinit()
X{
X	(void) initscr();
X	(void) noecho();
X	(void) crmode();
X	lines = LINES;
X	cols = COLS;
X	if (lines > MAP_HEIGHT + NUMTOPS + 1)
X		lines = MAP_HEIGHT + NUMTOPS + 1;
X	if (cols > MAP_WIDTH + NUMSIDES)
X		cols = MAP_WIDTH + NUMSIDES;
X}
X
X
X/*
XClear the screen.  We must also kill information maintained about the
Xdisplay.
X*/
X
Xvoid
Xclear_screen () {
X	(void) clear ();
X	(void) refresh ();
X	kill_display ();
X}
X
X/*
XRedraw the screen.
X*/
X
Xvoid
Xredraw () {
X	(void) clearok (curscr, TRUE);
X	(void) refresh ();
X}
X
X/*
XWait a little bit to give user a chance to see a message.  We refresh
Xthe screen and pause for a few milliseconds.
X*/
X
Xvoid
Xdelay () {
X	(void) refresh ();
X	(void) napms (delay_time); /* pause a bit */
X}
X
X
X/*
XClean up the display.  This routine gets called as we leave the game.
X*/
X
Xvoid
Xclose_disp()
X{
X	(void) move (LINES - 1, 0);
X	(void) clrtoeol ();
X	(void) refresh ();
X	(void) endwin ();
X}
X
X/*
XPosition the cursor and output a string.
X*/
X
Xvoid
X/* VARARGS3 */
Xpos_str (row, col, str, a, b, c, d, e, f, g, h)
Xint row, col;
Xchar *str;
Xint a, b, c, d, e, f, g, h;
X{
X	(void) move (row, col);
X	addprintf (str, a, b, c, d, e, f, g, h);
X}
X
Xvoid
X/* VARARGS1 */
Xaddprintf (str, a, b, c, d, e, f, g, h)
Xchar *str;
Xint a, b, c, d, e, f, g, h;
X{
X	char junkbuf[STRSIZE];
X	
X	(void) sprintf (junkbuf, str, a, b, c, d, e, f, g, h);
X	(void) addstr (junkbuf);
X}
X
X/*
XReport a bug.
X*/
X
Xvoid
Xassert (expression, file, line)
Xchar *expression;
Xchar *file;
Xint line;
X{
X	char buf[STRSIZE];
X	int a;
X
X	(void) move (lines, 0);
X	close_disp ();
X
X	(void) sprintf (buf, "assert failed: file %s line %d: %s",
X			file, line, expression);
X
X	a = 1; /* keep lint quiet */
X	a /= 0; /* force a core dump */
X	a = a; /* keep lint quiet */
X}
X
X/*
XEnd the game by cleaning up the display.
X*/
X
Xvoid
Xempend ()
X{
X	close_disp ();
X	exit (0);
X}
X
X/*
X * 03a 01Apr88 aml .Hacked movement algorithms for computer.
X * 02b 01Jun87 aml .First round of bug fixes.
X * 02a 01Jan87 aml .Translated to C.
X * 01b 27May85 cal .Fixed round number update bug. Made truename simple.
X * 01a 01Sep83 cal .Taken from a Decus tape
X */
X
Xvoid
Xver ()
X{
X        (void) addstr ("EMPIRE, Version 5.00 site Amdahl 1-Apr-1988");
X}
X
X/*
XHere is a little routine to perform consistency checking on the
Xdatabase.  I'm finding that my database is becoming inconsistent,
Xand I've no idea why.  Possibly this will help.
X
XWe perform the following functions:
X
X1)  Make sure no list contains loops.
X
X2)  Make sure every object is in either the free list with 0 hits,
Xor it is in the correct object list and a location list with non-zero hits,
Xand an appropriate owner.
X
X3)  Make sure every city is on the map.
X
X4)  Make sure every object is in the correct location and that
Xobjects on the map have non-zero hits.
X
X5)  Make sure every object in a cargo list has a ship pointer.
X
X6)  Make sure every object with a ship pointer is in that ship's
Xcargo list.
X*/
X
Xstatic int in_free[LIST_SIZE]; /* TRUE if object in free list */
Xstatic int in_obj[LIST_SIZE]; /* TRUE if object in obj list */
Xstatic int in_loc[LIST_SIZE]; /* TRUE if object in a loc list */
Xstatic int in_cargo[LIST_SIZE]; /* TRUE if object in a cargo list */
X
Xvoid
Xcheck () {
X	void check_cargo(), check_obj(), check_obj_cargo();
X	
X	long i, j;
X	piece_info_t *p;
X	
X	/* nothing in any list yet */
X	for (i = 0; i < LIST_SIZE; i++) {
X		in_free[i] = 0;
X		in_obj[i] = 0;
X		in_loc[i] = 0;
X		in_cargo[i] = 0;
X	}
X		
X	/* Mark all objects in free list.  Make sure objects in free list
X	have zero hits. */
X	
X	for (p = free_list; p != NULL; p = p->piece_link.next) {
X		i = p - object;
X		ASSERT (!in_free[i]);
X		in_free[i] = 1;
X		ASSERT (p->hits == 0);
X		if (p->piece_link.prev)
X			ASSERT (p->piece_link.prev->piece_link.next == p);
X	}
X	
X	/* Mark all objects in the map.
X	Check that cities are in corect location.
X	Check that objects are in correct location,
X	have a good owner, and good hits. */
X	
X	for (i = 0; i < MAP_SIZE; i++) {
X		if (map[i].cityp) ASSERT (map[i].cityp->loc == i);
X		
X		for (p = map[i].objp; p != NULL; p = p->loc_link.next) {
X			ASSERT (p->loc == i);
X			ASSERT (p->hits > 0);
X			ASSERT (p->owner == USER || p->owner == COMP);
X				
X			j = p - object;
X			ASSERT (!in_loc[j]);
X			in_loc[j] = 1;
X			
X			if (p->loc_link.prev)
X				ASSERT (p->loc_link.prev->loc_link.next == p);
X		}
X	}
X
X	/* make sure all cities are on map */
X
X	for (i = 0; i < NUM_CITY; i++)
X		ASSERT (map[city[i].loc].cityp == &(city[i]));
X
X	/* Scan object lists. */
X	
X	check_obj (comp_obj, COMP);
X	check_obj (user_obj, USER);
X	
X	/* Scan cargo lists. */
X	
X	check_cargo (user_obj[TRANSPORT], ARMY);
X	check_cargo (comp_obj[TRANSPORT], ARMY);
X	check_cargo (user_obj[CARRIER], FIGHTER);
X	check_cargo (comp_obj[CARRIER], FIGHTER);
X	
X	/* Make sure all objects with ship pointers are in cargo. */
X
X	check_obj_cargo (comp_obj);
X	check_obj_cargo (user_obj);
X	
X	/* Make sure every object is either free or in loc and obj list. */
X
X	for (i = 0; i < LIST_SIZE; i++)
X		ASSERT (in_free[i] != (in_loc[i] && in_obj[i]));
X}
X
X/*
XCheck object lists.  We look for:
X
X1)  Loops and bad prev pointers.
X
X2)  Dead objects.
X
X3)  Invalid types.
X
X4)  Invalid owners.
X*/
X
Xvoid
Xcheck_obj (list, owner)
Xpiece_info_t **list;
Xint owner;
X{
X	long i, j;
X	piece_info_t *p;
X	
X	for (i = 0; i < NUM_OBJECTS; i++)
X	for (p = list[i]; p != NULL; p = p->piece_link.next) {
X		ASSERT (p->owner == owner);
X		ASSERT (p->type == i);
X		ASSERT (p->hits > 0);
X		
X		j = p - object;
X		ASSERT (!in_obj[j]);
X		in_obj[j] = 1;
X	
X		if (p->piece_link.prev)
X			ASSERT (p->piece_link.prev->piece_link.next == p);
X	}
X}
X
X/*
XCheck cargo lists.  We assume object lists are valid.
Xas we will place bits in the 'in_cargo' array that are used by
X'check_obj'.
X
XCheck for:
X
X1)  Number of items in list is same as cargo count.
X
X2)  Type of cargo is correct.
X
X3)  Location of cargo matches location of ship.
X
X4)  Ship pointer of cargo points to correct ship.
X
X5)  There are no loops in cargo list and prev ptrs are correct.
X
X6)  All cargo is alive.
X*/
X
Xvoid
Xcheck_cargo (list, cargo_type)
Xpiece_info_t *list;
Xint cargo_type;
X{
X	piece_info_t *p, *q;
X	long j, count;
X	
X	for (p = list; p != NULL; p = p->piece_link.next) {
X		count = 0;
X		for (q = p->cargo; q != NULL; q = q->cargo_link.next) {
X			count += 1; /* count items in list */
X			ASSERT (q->type == cargo_type);
X			ASSERT (q->owner == p->owner);
X			ASSERT (q->hits > 0);
X			ASSERT (q->ship == p);
X			ASSERT (q->loc == p->loc);
X			
X			j = q - object;
X			ASSERT (!in_cargo[j]);
X			in_cargo[j] = 1;
X
X			if (p->cargo_link.prev)
X				ASSERT (p->cargo_link.prev->cargo_link.next == p);
X                }
X		ASSERT (count == p->count);
X        }
X}
X
X/*
XScan through object lists making sure every object with a ship
Xpointer appears in a cargo list.  We assume object and cargo
Xlists are valid.
X*/
X
Xvoid
Xcheck_obj_cargo (list)
Xpiece_info_t **list;
X{
X	piece_info_t *p;
X	long i;
X
X	for (i = 0; i < NUM_OBJECTS; i++)
X	for (p = list[i]; p != NULL; p = p->piece_link.next) {
X		if (p->ship) ASSERT (in_cargo[p-object]);
X	}
X}
//E*O*F util.c//

echo x - data.c
sed -e 's/^X//' > "data.c" << '//E*O*F data.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/*
XStatic data.
X
XOne of our hopes is that changing the types of pieces that
Xexist in the game is mostly a matter of modifying this file.
XHowever, see also the help routine, empire.h, and empire.doc.
X*/
X
X#include "empire.h"
X
X/*
XPiece attributes.  Notice that the Transport is allowed only one hit.
XIn the previous version of this game, the user could easily win simply
Xby building armies and troop transports.  We attempt to alleviate this
Xproblem by making transports far more fragile.  We have also increased
Xthe range of a fighter from 20 to 30 so that fighters will be somewhat
Xmore useful.
X*/
X
Xpiece_attr_t piece_attr[] = {
X	{'A', /* character for printing piece */
X	 "army", /* name of piece */ 
X	 "army", /* nickname */
X	 "an army", /* name with preceding article */
X	 "armies", /* plural */
X	 "+", /* terrain */
X	 5, /* units to build */
X	 1, /* strength */
X	 1, /* max hits */
X	 1, /* movement */
X	 0, /* capacity */
X	 INFINITY}, /* range */
X
X	/*
X	 For fighters, the range is set to an even multiple of the speed.
X	 This allows user to move fighter, say, two turns out and two
X	 turns back.
X	*/
X	 
X	{'F', "fighter", "fighter", "a fighter", "fighters",
X		".+", 10, 1,  1, 8, 0, 32},
X
X	{'P', "patrol boat", "patrol", "a patrol boat", "patrol boats",
X		".",  15, 1,  1, 4, 0, INFINITY},
X		
X	{'D', "destroyer", "destroyer", "a destroyer", "destroyers",
X		".",  20, 1,  3, 2, 0, INFINITY},
X
X	{'S', "submarine", "submarine", "a submarine", "submarines",
X		".",  20, 3,  2, 2, 0, INFINITY},
X
X	{'T', "troop transport", "transport", "a troop transport", "troop transports",
X		".",  30, 1,  1, 2, 6, INFINITY},
X
X	{'C', "aircraft carrier", "carrier", "an aircraft carrier", "aircraft carriers",
X		".",  30, 1,  8, 2, 8, INFINITY},
X
X	{'B', "battleship", "battleship", "a battleship", "battleships",
X		".",  40, 2, 10, 2, 0, INFINITY},
X		
X	{'Z', "satellite", "satellite", "a satellite", "satellites",
X		".+", 50, 0, 1, 10, 0, 500}
X};
X
X/* Direction offsets. */
X
Xint dir_offset [] = {-MAP_WIDTH, /* north */
X		     -MAP_WIDTH+1, /* northeast */
X		     1, /* east */
X		     MAP_WIDTH+1, /* southeast */
X		     MAP_WIDTH, /* south */
X		     MAP_WIDTH-1, /* southwest */
X		     -1, /* west */
X		     -MAP_WIDTH-1}; /* northwest */
X
X/* Names of movement functions. */
X
Xchar *func_name[] = {"none", "random", "sentry", "fill", "land",
X			"explore", "load", "attack", "load", "repair",
X			"transport",
X			"W", "E", "D", "C", "X", "Z", "A", "Q"};
X
X/* The order in which pieces should be moved. */
Xint move_order[] = {SATELLITE, TRANSPORT, CARRIER, BATTLESHIP, 
X		    PATROL, SUBMARINE, DESTROYER, ARMY, FIGHTER};
X
X/* types of pieces, in declared order */
Xchar type_chars[] = "AFPDSTCBZ";
X
X/* Lists of attackable objects if object is adjacent to moving piece. */
X
Xchar tt_attack[] = "T";
Xchar army_attack[] = "O*TACFBSDP";
Xchar fighter_attack[] = "TCFBSDPA";
Xchar ship_attack[] = "TCBSDP";
X
X/* Define various types of objectives */
X
Xmove_info_t tt_explore = { /* water objectives */
X	COMP, /* home city */
X	" ", /* objectives */
X	{1} /* weights */
X};
Xmove_info_t tt_load = { /* land objectives */
X	COMP, "$",           {1}
X};
X
X/*
XRationale for 'tt_unload':
X
X     Any continent with four or more cities is extremely attractive,
Xand we should grab it quickly.  A continent with three cities is
Xfairly attractive, but we are willing to go slightly out of our
Xway to find a better continent.  Similarily for two cities on a
Xcontinent.  At one city on a continent, things are looking fairly
Xunattractive, and we are willing to go quite a bit out of our way
Xto find a better continent.
X
X     Cities marked with a '0' are on continents where we already
Xhave cities, and these cities will likely fall to our armies anyway,
Xso we don't need to dump armies on them unless everything else is
Xreal far away.  We would prefer to use unloading transports for
Xtaking cities instead of exploring, but we are willing to explore
Xif interesting cities are too far away.
X
X     It has been suggested that continents containing one city
Xare not interesting.  Unfortunately, most of the time what the
Xcomputer sees is a single city on a continent next to lots of
Xunexplored territory.  So it dumps an army on the continent to
Xexplore the continent and the army ends up attacking the city
Xanyway.  So, we have decided we might as well send the tt to
Xthe city in the first place and increase the speed with which
Xthe computer unloads its tts.
X*/
X
Xmove_info_t tt_unload     = {
X	COMP, "9876543210 ", {1, 1, 1, 1, 1, 1, 11, 21, 41, 101, 61}
X};
X
X/*
X '$' represents loading tt must be first
X 'x' represents tt producing city
X '0' represnets explorable territory
X*/
Xmove_info_t army_fight    = { /* land objectives */
X	COMP, "O*TA ",       {1, 1, 1, 1, 11}
X};
Xmove_info_t army_load     = { /* water objectives */
X	COMP, "$x",          {1, W_TT_BUILD}
X};
X
Xmove_info_t fighter_fight = {
X	COMP, "TCFBSDPA ",   {1, 1, 5, 5, 5, 5, 5, 5, 9}
X};
Xmove_info_t ship_fight    = {
X	COMP, "TCBSDP ",     {1, 1, 3, 3, 3, 3, 21}
X};
Xmove_info_t ship_repair   = {
X	COMP, "X",           {1}
X};
X
Xmove_info_t user_army        = {
X	USER, " ",   {1}
X};
Xmove_info_t user_army_attack = {
X	USER, "*Xa ", {1, 1, 1, 12}
X};
Xmove_info_t user_fighter     = {
X	USER, " ",   {1}
X};
Xmove_info_t user_ship        = {
X	USER, " ",   {1}
X};
Xmove_info_t user_ship_repair = {
X	USER, "O",   {1}
X};
X
X/*
XVarious help texts.
X*/
X
Xchar *help_cmd[] = {
X	"COMMAND MODE",
X	"Auto:     enter automove mode",
X	"City:     give city to computer",
X	"Date:     print round",
X	"Examine:  examine enemy map",
X	"File:     print map to file",
X	"Give:     give move to computer",
X	"Help:     display this text",
X	"J:        enter edit mode",
X	"Move:     make a move",
X	"N:        give N moves to computer",
X	"Print:    print a sector",
X	"Quit:     quit game",
X	"Restore:  restore game",
X	"Save:     save game",
X	"Trace:    save movie in empmovie.dat",
X	"Watch:    watch movie",
X	"Zoom:     display compressed map",
X	"<ctrl-L>: redraw screen"
X};
Xint cmd_lines = 19;
X
Xchar *help_user[] = {
X	"USER MODE",
X	"QWE",
X	"A D       movement directions",
X	"ZXC",
X	"<space>:  sit",
X	"Build:    change city production",
X	"Fill:     set func to fill",
X	"Grope:    set func to explore",
X	"Help:     display this text",
X	"I <dir>:  set func to dir",
X	"J:        enter edit mode",
X	"Kill:     set func to awake",
X	"Land:     set func to land",
X	"Out:      leave automove mode",
X	"Print:    redraw screen",
X	"Random:   set func to random",
X	"Sentry:   set func to sentry",
X	"Upgrade:  set func to repair",
X	"V <piece> <func>:  set city func",
X	"Y:        set func to attack",
X	"<ctrl-L>: redraw screen",
X	"?:        describe piece"
X};
Xint user_lines = 22;
X	
Xchar *help_edit[] = {
X	"EDIT MODE",
X	"QWE",
X	"A D       movement directions",
X	"ZXC",
X	"Build:    change city production",
X	"Fill:     set func to fill",
X	"Grope:    set func to explore",
X	"Help:     display this text",
X	"I <dir>:  set func to dir",
X	"Kill:     set func to awake",
X	"Land:     set func to land",
X	"Mark:     mark piece",
X	"N:        set dest for marked piece",
X	"Out:      exit edit mode",
X	"Print:    print sector",
X	"Random:   set func to random",
X	"Sentry:   set func to sentry",
X	"Upgrade:  set func to repair",
X	"V <piece> <func>:  set city func",
X	"Y:        set func to attack",
X	"<ctrl-L>: redraw screen",
X	"?:        describe piece"
X};
Xint edit_lines = 22;
//E*O*F data.c//

echo x - COPYING
sed -e 's/^X//' > "COPYING" << '//E*O*F COPYING//'
X
X		    EMPIRE GENERAL PUBLIC LICENSE
X
X Copyright (C)  1987, 1988 Chuck Simmons
X Copyleft (GNU) 1987, 1988 Chuck Simmons
X
X Everyone is permitted to copy and distribute verbatim copies
X of this license, but changing it is not allowed.  You can also
X use this wording to make the terms for other programs.
X
X  The license agreements of most software companies keep you at the
Xmercy of those companies.  By contrast, our general public license is
Xintended to give everyone the right to share EMPIRE.  To make sure that
Xyou get the rights we want you to have, we need to make restrictions
Xthat forbid anyone to deny you these rights or to ask you to surrender
Xthe rights.  Hence this license agreement.
X
X  Specifically, we want to make sure that you have the right to give
Xaway copies of EMPIRE, that you receive source code or else can get it
Xif you want it, that you can change EMPIRE or use pieces of it in new
Xfree programs, and that you know you can do these things.
X
X  To make sure that everyone has such rights, we have to forbid you to
Xdeprive anyone else of these rights.  For example, if you distribute
Xcopies of EMPIRE, you must give the recipients all the rights that you
Xhave.  You must make sure that they, too, receive or can get the
Xsource code.  And you must tell them their rights.
X
X  Also, for our own protection, we must make certain that everyone
Xfinds out that there is no warranty for EMPIRE.  If EMPIRE is modified by
Xsomeone else and passed on, we want its recipients to know that what
Xthey have is not what we distributed, so that any problems introduced
Xby others will not reflect on our reputation.
X
X  Therefore we (Chuck Simmons) make the following terms which say what
Xyou must do to be allowed to distribute or change EMPIRE.
X
X
X			COPYING POLICIES
X
X  1. You may copy and distribute verbatim copies of EMPIRE source code
Xas you receive it, in any medium, provided that you conspicuously and
Xappropriately publish on each copy a valid copyright notice "Copyright
X(C) 1988 Chuck Simmons" (or with whatever year is appropriate); keep
Xintact the notices on all files that refer to this License Agreement
Xand to the absence of any warranty; and give any other recipients of
Xthe EMPIRE program a copy of this License Agreement along with the
Xprogram.  You may charge a distribution fee for the physical act of
Xtransferring a copy.
X
X  2. You may modify your copy or copies of EMPIRE or any portion of it,
Xand copy and distribute such modifications under the terms of
XParagraph 1 above, provided that you also do the following:
X
X    a) cause the modified files to carry prominent notices stating
X    that you changed the files and the date of any change; and
X
X    b) cause the whole of any work that you distribute or publish,
X    that in whole or in part contains or is a derivative of EMPIRE or
X    any part thereof, to be licensed at no charge to all third
X    parties on terms identical to those contained in this License
X    Agreement (except that you may choose to grant more extensive
X    warranty protection to some or all third parties, at your option).
X
X    c) You may charge a distribution fee for the physical act of
X    transferring a copy, and you may at your option offer warranty
X    protection in exchange for a fee.
X
XMere aggregation of another unrelated program with this program (or its
Xderivative) on a volume of a storage or distribution medium does not bring
Xthe other program under the scope of these terms.
X
X  3. You may copy and distribute EMPIRE (or a portion or derivative of it,
Xunder Paragraph 2) in object code or executable form under the terms of
XParagraphs 1 and 2 above provided that you also do one of the following:
X
X    a) accompany it with the complete corresponding machine-readable
X    source code, which must be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    b) accompany it with a written offer, valid for at least three
X    years, to give any third party free (except for a nominal
X    shipping charge) a complete machine-readable copy of the
X    corresponding source code, to be distributed under the terms of
X    Paragraphs 1 and 2 above; or,
X
X    c) accompany it with the information you received as to where the
X    corresponding source code may be obtained.  (This alternative is
X    allowed only for noncommercial distribution and only if you
X    received the program in object code or executable form alone.)
X
XFor an executable file, complete source code means all the source code for
Xall modules it contains; but, as a special exception, it need not include
Xsource code for modules which are standard libraries that accompany the
Xoperating system on which the executable file runs.
X
X  4. You may not copy, sublicense, distribute or transfer EMPIRE
Xexcept as expressly provided under this License Agreement.  Any attempt
Xotherwise to copy, sublicense, distribute or transfer EMPIRE is void and
Xyour rights to use the program under this License agreement shall be
Xautomatically terminated.  However, parties who have received computer
Xsoftware programs from you with this License Agreement will not have
Xtheir licenses terminated so long as such parties remain in full compliance.
X
X  5. If you wish to incorporate parts of EMPIRE into other free programs
Xwhose distribution conditions are different, write to Chuck Simmons
Xat 1250 E. Arques Ave (MS 269), Box 3470, Sunnyvale, CA  94088-3470,
XUSA.  We have not yet worked out a simple rule that can be stated here,
Xbut we will often permit this.  We will be guided by the two goals of
Xpreserving the free status of all derivatives of our free software and
Xof promoting the sharing and reuse of software.
X
XYour comments and suggestions about our licensing policies and our
Xsoftware are welcome!  This contract was based on the contract made by
Xthe Free Software Foundation.  Please contact the Free Software
XFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or call (617)
X876-3296 for details on copylefted materal in general, or contact us
Xvia electronic mail at amdahl!chuck.
X
X(Please note that the Free Software Foundation is in no way associated
Xwith EMPIRE, and any lack of quality inherent in EMPIRE should not
Xreflect on the reputation of that noble enterprise.)
X
X
X		       NO WARRANTY
X
X  BECAUSE EMPIRE IS LICENSED FREE OF CHARGE, WE PROVIDE ABSOLUTELY NO
XWARRANTY, TO THE EXTENT PERMITTED BY APPLICABLE STATE LAW.  EXCEPT WHEN
XOTHERWISE STATED IN WRITING, CHUCK SIMMONS AND/OR OTHER PARTIES PROVIDE
XEMPIRE "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
XINCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
XAND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY
XAND PERFORMANCE OF EMPIRE IS WITH YOU.  SHOULD EMPIRE PROVE DEFECTIVE,
XYOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
X
X  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW WILL CHUCK SIMMONS
XAND/OR ANY OTHER PARTY WHO MAY MODIFY AND REDISTRIBUTE EMPIRE AS PERMITTED
XABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY LOST PROFITS, LOST
XMONIES, OR OTHER SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
XOUT OF THE USE OR INABILITY TO USE (INCLUDING BUT NOT LIMITED TO LOSS
XOF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY THIRD
XPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS)
XEMPIRE, EVEN IF YOU HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES,
XOR FOR ANY CLAIM BY ANY OTHER PARTY.
//E*O*F COPYING//

echo shar: End of archive 5 \(of 6\).
cp /dev/null ark5isdone
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