/* Implementation of the "mplayer" AI in Xconq.
   Copyright (C) 1987-1989, 1991-1999 Stanley T. Shebs.

Xconq is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.  See the file COPYING.  */

#include "conq.h"
extern int select_by_weight(int *arr, int numvals);
extern int carryable(int u);
extern int accelerable(int u);
extern int accelerator(int u1, int u2);
#include "kpublic.h"
extern void try_to_draw(Side *side, int flag, char *ainame);
extern void give_up(Side *side, char *ainame);
#include "ai.h"

extern int bhw_max;

/* Limit on the number of goals that a side may have. */

#define MAXGOALS 10

/* Limit on the number of theaters a single side may have. */

#define MAXTHEATERS 98

/* what does the game look like? */
typedef enum a_game_class {
    gc_none,
    gc_standard,
    gc_time,
    gc_advanced
} GameClass;

static GameClass game_class = gc_none;

/* Strategy is what a side uses to make decisions. */

typedef struct a_strategy {
    int type;			/* placeholder */
    int trytowin;
    int report_not_understood;
    int creationdate;
    short strengths[MAXSIDES][MAXUTYPES];  /* estimated numbers of units */
    short points[MAXSIDES];	/* estimated point value */
    short alstrengths[MAXSIDES][MAXUTYPES];  /* numbers in alliances */
    short alpoints[MAXSIDES];	/* points in alliances */
    short initial_strengths_computed;
    short strengths0[MAXSIDES][MAXUTYPES];  /* initial estimated numbers of units */
    short points0[MAXSIDES];	/* initial estimated point value */
    short alstrengths0[MAXSIDES][MAXUTYPES];  /* initial numbers in alliances */
    short alpoints0[MAXSIDES];	/* initial points in alliances */
    short contacted[MAXSIDES+1];
    short homefound[MAXSIDES+1];
    int analyzegame;
    struct a_theater *theaters;
    struct a_theater **theatertable;
    short numtheaters;
    char *areatheaters;
    struct a_theater *homefront;
    struct a_theater *perimeters[NUMDIRS];
    struct a_theater *midranges[NUMDIRS];
    struct a_theater *remotes[NUMDIRS];
    int numgoals;
    struct a_goal *goals[MAXGOALS];
    /* Exploration and search slots. */
    int zonewidth, zoneheight;
    int numzonex, numzoney;     /* dimensions of search zone array */
    int numzones;
    struct a_searchzone *searchzones;
    short *explorertypes;
    short explorersneeded;
    short *terrainguess;
    short cx, cy;               /* "centroid" of all our units */
    short *demand;              /* worth of each utype w.r.t. strategy */
    int explore_priority;
    int defend_priority;
    int attack_priority;
    struct a_unit *unitlist[MAXUTYPES];   /* lists to help mplay efficiency */
    short unitlistcount[MAXUTYPES];  /* counts of above lists */
    short *actualmix;
    short *expectedmix;
    short *idealmix;
    short *develop_status;  /* specific to the "time" game */
    short *develop_on;      /* specific to the "time" game */
    Obj *writable_state;
} Strategy;

/* utype-specific develop status codes for the "time" game */
#define RS_DEVELOP_NEEDED 4
#define RS_DEVELOP_ASSIGNED 3
#define RS_UPGRADE_NEEDED 1

#define mplayer(s) ((Strategy *) (s)->ai)

/* A Theater is a sub-area that can be planned for all at once. */

/* To save space in theater layer, no more than 127 theaters may exist at once.
   This should be sufficient, even a Napoleon would have trouble keeping track
   of that much activity. */

typedef struct a_theater {
    short id;
    char *name;			/* an informative name for this theater */
    short x, y;			/* center of the theater */
    short xmin, ymin;		/*  */
    short xmax, ymax;		/*  */
    int size;			/* number of cells in the theater */
    short importance;		/* 0 = shrug, 100 = critical */
    Goal *maingoal;
    short allied_units;		/* How many units on our side here. */
    short makers;		/* Total number of makers */
    short unexplored;		/* number of unseen cells in theater */
    short allied_bases;		/* total number of our bases, includes towns */
    short border;		/* true if this is a border theater. */
    short reinforce;		/* priority on request for units. */
    short numassigned[MAXUTYPES];  /* num of each type assigned to theater */
    short numneeded[MAXUTYPES];  /* units we should move to theater. */
    short numtotransport[MAXUTYPES];  /* types needing transportation. */
    short numenemies[MAXUTYPES];
    short numsuspected[MAXUTYPES];
    short numsuspectedmax[MAXUTYPES];
    int *people;		/* number of populated cells seen */
    int enemystrengthmin;	/* estimate of enemy unit strength */
    int enemystrengthmax;	/* estimate of enemy unit strength */
    short units_lost;		/* How many units have we lost here. */
    struct a_theater *next;
} Theater;

#define for_all_theaters(s,th) \
  	for ((th) = mplayer(s)->theaters; (th) != NULL; (th) = (th)->next) \

#define theater_at(s,x,y)  \
  (mplayer(s)->theatertable[((int) mplayer(s)->areatheaters[(x)+area.width*(y)])])

#define set_theater_at(s,x,y,th)  \
  ((mplayer(s)->areatheaters[(x)+area.width*(y)]) = (th)->id)

#define for_all_cells_in_theater(s,x,y,th) { \
  for ((x) = theater->xmin; (x) < theater->xmax; ++(x))  \
    for ((y) = theater->ymin; (y) < theater->ymax; ++(y))  \
      if (theater_at((s), (x), (y)) == (th))  }

#define unit_theater(unit) ((Theater *) (unit)->aihook)

#define set_unit_theater(unit,theater) ((unit)->aihook = (char *) (theater))

#define can_see_actual_units(side, x, y) (all_see_all || cover((side), (x), (y)) > 0)

/* Local function declarations. */

static void mplayer_init(Side *side);
static void mplayer_init_turn(Side *side);
static void create_strategy(Side *side);
static void reset_strategy(Side *side);
static void analyze_the_game(Side *side);
static void determine_subgoals(Side *side);
static void review_theaters(Side *side);
static void create_initial_theaters(Side *side);
static Theater *create_theater(Side *side);
static void remove_theater(Side *side, Theater *theater);
static void remove_small_theaters(Side *side);
static void compute_theater_bounds(Side *side);
static void review_goals(Side *side);
static void review_units(Side *side);
static void update_side_strategy(Side *side);
static void decide_theater_needs(Side *side, Theater *theater);
static void update_unit_plans(Side *side);
static void update_unit_plans_randomly(Side *side);
static void estimate_strengths(Side *side);
static void decide_resignation(Side *side);
static void add_goal(Side *side, Goal *goal);
static Goal *has_goal(Side *side, GoalType goaltype);
static Goal *has_unsatisfied_goal(Side *side, GoalType goaltype);
static void mplayer_decide_plan(Side *side, Unit *unit);
static int need_this_type_to_explore(Side *side, int u);
/* static int compare_weights(struct weightelt *w1, struct weightelt *w2); */
static void assign_to_exploration(Side *side, Unit *unit);
static void assign_explorer_to_theater(Side *side, Unit *unit, Theater *theater);
static int need_this_type_to_build_explorers(Side *side, int u);
static void assign_to_explorer_construction(Side *side, Unit *unit);
static void assign_to_offense(Side *side, Unit *unit);
static void assign_to_offense_support(Side *side, Unit *unit);
static int mplayer_adjust_plan(Side *side, Unit *unit);
static int type_can_build_attackers(Side *side, int u);
static int preferred_build_type(Side *side, Unit *unit, int plantype);
static int need_more_transportation(Side *side);
static void assign_to_defense(Side *side, Unit *unit);
static void assign_to_defense_support(Side *side, Unit *unit);
static int assign_to_develop_on(Side *side, Unit *unit, int u2);
static int can_develop_on(int u, int u2);
static int needs_develop(Side *side, int u);
#if 0
static int mplayer_guide_explorer(Side *side, Unit *unit);
#endif
static int probably_explorable(Side *side, int x, int y, int u);
#if 0
static int build_base_for_self(Side *side, Unit *unit);
static int build_base_for_others(Side *side, Unit *unit);
#endif
static int build_depot_for_self(Side *side, Unit *unit);
static void assign_to_colonize(Side *side, Unit *unit);
static void assign_to_improve(Side *side, Unit *unit);
#if 0
static int type_can_build_colonizers(Side *side, int u);
#endif
static void mplayer_react_to_action_result(Side *side, Unit *unit, int rslt);
static void mplayer_react_to_task_result(Side *side, Unit *unit, Task *task, TaskOutcome rslt);
static void change_to_adjacent_theater(Side *side, Unit *unit);
static int desired_direction_impassable(Unit *unit, int x, int y);
static int could_be_ferried(Unit *unit, int x, int y);
static int blocked_by_enemy(Unit *unit, int x, int y, int shortest);
static int attack_blockage(Side *side, Unit *unit, int x, int y, int shortest);
static void mplayer_react_to_new_side(Side *side, Side *side2);
static void mplayer_finish_movement(Side *side);
static Unit *search_for_available_transport(Unit *unit, int purpose);
static int defended_by_occupants(Unit *unit);
static int mplayer_update_plan(Unit *unit);
static int mplayer_handle_emergency(Unit *unit);
static void assign_to_defend_unit(Unit *unit, Unit *unit2);
#if 0
static void assign_to_defend_cell(Unit *unit, int x, int y);
static void assign_to_defend_vicinity(Unit *unit, int x, int y, int w, int h);
#endif
static void rethink_plan(Unit *unit);
static int enemy_close_by(Side *side, Unit *unit, int dist, int *xp, int *yp);
static void mplayer_receive_message(Side *side, Side *sender, char *str);
static char *mplayer_at_desig(Side *side, int x, int y);
static int mplayer_theater_at(Side *side, int x, int y);
static int mplayer_read_strengths(Side *side);
static Obj *mplayer_save_state(Side *side);

#if 0
static void mplayer_react_to_unit_loss(Side *side, Unit *unit);
#endif

static int compare_weights(const void *w1, const void *w2);

/* This is the set of operations that generic code will use. */

AI_ops mplayer_ops = {
    "mplayer",			/* name */
    NULL,			/* to_test_compat */		
    mplayer_init,		/* to_init */
    mplayer_init_turn,		/* to_init_turn */
    mplayer_decide_plan,	/* to_decide_plan */
    mplayer_react_to_action_result,	/* to_react_to_action_result */
    mplayer_react_to_task_result,	/* to_react_to_task_result */
    mplayer_react_to_new_side,	/* to_react_to_new_side */
    mplayer_adjust_plan,	/* to_adjust_plan */
    mplayer_update_plan,	/* to_update_plan */
    mplayer_handle_emergency,	/* to_handle_emergency */
    mplayer_finish_movement,	/* to_finish_movement */
    mplayer_receive_message,	/* to_receive_message */
    mplayer_save_state,		/* to_save_state */
    mplayer_theater_at,		/* region_at */
    mplayer_at_desig,		/* at_desig */
    -1				/* dummy */
};

/* Flag to detect when shared mplayer init has been done. */

static int mplayerinited = FALSE;

static Theater *tmptheater;

static Side *anewside;

/* Determine game type from name of included modules. */

static GameClass find_game_class(void);

static GameClass
find_game_class(void)
{
    Module *m;

    for_all_modules(m) {
	if (strcmp(m->name, "time") == 0
	    || (m->origmodulename && strcmp(m->origmodulename, "time") == 0))
	  return gc_time;
    	else if (strcmp(m->name, "advanced") == 0
		 || (m->origmodulename
		     && strcmp(m->origmodulename, "advanced") == 0))
	  return gc_advanced;
    }
    return gc_standard;
}

static void
mplayer_init(side)
Side *side;
{
    Unit *unit;

    if (game_class == gc_none) {
	game_class = find_game_class();
    }

    /* (should do this only when absolutely needed - mplayer might
       never actually be used) */
    if (!mplayerinited) {
	mplayerinited = TRUE;
	Dprintf("One mplayer AI is %d bytes.\n", sizeof(Strategy));
    }
    /* Make sure a strategy object exists. */
    if (mplayer(side) == NULL)
      create_strategy(side);
    /* If the side has no units at the moment, it doesn't really need to
       plan. */
    if (!side_has_units(side))
      return;
    /* Compute an initial estimation of units on each side. */
    /* (Needed for save/restore consistency, otherwise not
       critical to do here.) */
    estimate_strengths(side);
    /* Study the scorekeepers and such, decide how to play the game. */
    analyze_the_game(side);
    /* Reset plans of any units that were not doing anything. */
    for_all_side_units(side, unit) {
    	if (in_play(unit) && unit->plan && unit->plan->aicontrol) {
	    net_force_replan(side, unit, TRUE);
    	}
    }
}

/* At the beginning of each turn, make plans and review the situation. */

static void
mplayer_init_turn(side)
Side *side;
{
    int u, u2;

    /* Cases where we no longer need to run. */
    if (!side->ingame)
      return;
    /* A side without units hasn't got anything to do but wait. */
    /* (should account for possible units on controlled sides) */
    if (!side_has_units(side))
      return;
    /* Mplayers in a hacked game will not play,
       unless they're being debugged. */
    if (compromised && !DebugM)
      return;
    update_all_progress_displays("ai turn init start", side->id);
    DMprintf("%s mplayer init turn\n", side_desig(side));
    /* Make sure a strategy object exists. */
    if (mplayer(side) == NULL)
      create_strategy(side);
    /* Look over the game design we're playing with. */
    analyze_the_game(side);

    if (mplayer(side)->report_not_understood) {
	notify_all("%s AI doesn't understand scoring in this game!", short_side_title(side));
	mplayer(side)->report_not_understood = FALSE;
    }

    /* code specific to the "time" game */
    if (game_class == gc_time) {
	for_all_unit_types(u) {
	    if (mplayer(side)->develop_status[u] == RS_DEVELOP_ASSIGNED) {
		u2 = mplayer(side)->develop_on[u];
		if (!needs_develop (side, u2)) {
		    /* develop done, start upgrading */
		    DMprintf("%s has completed develop on %s\n",
			     side_desig(side), u_type_name(u2));
		    mplayer(side)->develop_status[u] = RS_UPGRADE_NEEDED;
		}
	    }
	}
    }

    /* If this game is one that can be won, as opposed to
       just dinking around, figure how to win it. */
    if (mplayer(side)->trytowin) {
	/* Check out the current goal tree first. */
	review_goals(side);
	/* Goal analysis might have triggered resignation. */
	if (!side->ingame)
	  goto done;
	/* Check out all the theaters. */
	review_theaters(side);
	/* Check out all of our units. */
	review_units(side);
	/* (should be integrated better) */
	mplayer_finish_movement(side);
	/* Decide on the new current plan. */
	update_side_strategy(side);
	/* Propagate this to individual unit plans. */
	update_unit_plans(side);
    } else {
	update_unit_plans_randomly(side);
    }
  done:
    update_all_progress_displays("", side->id);
    DMprintf("%s mplayer init turn done\n", side_desig(side));
}

/* Create and install an entirely new strategy object for the side. */

static void
create_strategy(side)
Side *side;
{
    Strategy *strategy = (Strategy *) xmalloc(sizeof(Strategy));

    /* Put the specific structure into a generic slot. */
    side->ai = (struct a_ai *) strategy;
    strategy->type = mplayertype;
    /* Allocate a table of pointers to theaters, for access via small numbers
       rather than full pointers. */
    strategy->theatertable = (Theater **) xmalloc(127 * sizeof(Theater *));
    /* Allocate a layer of indexes into the theater table. */
    strategy->areatheaters = malloc_area_layer(char);
    /* Allocate random things. */
    /* Arrays for unit types. */
    strategy->actualmix = (short *) xmalloc(numutypes * sizeof(short));
    strategy->expectedmix = (short *) xmalloc(numutypes * sizeof(short));
    strategy->idealmix = (short *) xmalloc(numutypes * sizeof(short));
    strategy->develop_status = (short *) xmalloc(numutypes * sizeof(short));
    strategy->develop_on     = (short *) xmalloc(numutypes * sizeof(short));
    /* Arrays for terrain types. */
    strategy->terrainguess = (short *) xmalloc(numttypes * sizeof(short));
    strategy->writable_state = lispnil;
    /* Set everything to correct initial values. */
    reset_strategy(side);
}

/* Put all the right initial values into the strategy, but don't allocate anything. */

static void
reset_strategy(side)
Side *side;
{
    int u, u2, t, dir;
    Strategy *strategy = (Strategy *) side->ai;

    /* Remember when we did this. */
    strategy->creationdate = g_turn();
    /* Null out various stuff. */
    strategy->numgoals = 0;
    strategy->theaters = NULL;
    /* Actually we start with no theaters, but it's convenient to leave entry 0
       in the theater table pointing to NULL. */
    strategy->numtheaters = 1;
    /* Clear pointers to special-purpose theaters. */
    strategy->homefront = NULL;
    for_all_directions(dir) {
    	strategy->perimeters[dir] = NULL;
    	strategy->midranges[dir] = NULL;
    	strategy->remotes[dir] = NULL;
    }
    strategy->explorersneeded = 0;
    /* Reset the summation of our exploration needs. */
    for_all_unit_types(u) {
	strategy->actualmix[u] = 0;
	strategy->expectedmix[u] = 0;
	strategy->idealmix[u] = 0;
	strategy->develop_status[u] = 0;
	strategy->develop_on[u] = 0;

	/* code specific to the "time" game */
	if (game_class == gc_time) {
	    for_all_unit_types(u2) {
		if (needs_develop (side, u2) && can_develop_on(u, u2)) {
		    strategy->develop_status[u] = RS_DEVELOP_NEEDED;
		    strategy->develop_on[u] = u2;
		    DMprintf("%s can develop on %s (to level %d)\n",
			     u_type_name(u), u_type_name(u2),
			     u_tech_to_build(u2));
		}
	    }
	}
    }
    for_all_terrain_types(t) {
	strategy->terrainguess[t] = 0;
    }
    strategy->analyzegame = TRUE;
    /* Analyze the game and decide our basic goals. */
    analyze_the_game(side);
}

/* Look over the game design and decide what we're supposed to be doing,
   if anything at all.  This just sets up toplevel goals based on the
   game design, does not evaluate goals or any such. */

static void
analyze_the_game(side)
Side *side;
{
    int maybedraw, i;
    Goal *goal;

    if (mplayer(side)->analyzegame) {
	if (should_try_to_win(side)) {
	    mplayer(side)->trytowin = TRUE;
	    /* This is our whole purpose in the game. */
	    goal = create_goal(GOAL_WON_GAME, side, TRUE);
	    add_goal(side, goal);
	    /* Now figure what exactly we have to do in order to win. */
	    determine_subgoals(side);
	    /* Machine will want to keep playing as long as it thinks
	       it has a chance to win. */
	    maybedraw = FALSE;
	} else {
	    mplayer(side)->trytowin = FALSE;
	    /* Since the side is not trying to win anything, it will be
	       pretty laidback about whether to keep the game going. */
	    maybedraw = TRUE;
	}
	/* Be trusting about game saves, at least for now. (The problem
	   is that a human player could escape fate by saving the game
	   and then either editing the saved game or just throwing it
	   away.) */
	if (TRUE != side->willingtosave /* (should) and decision delegated to AI */)
	  net_set_willing_to_save(side, TRUE);
	if (maybedraw != side->willingtodraw /* (should) and decision delegated to AI */)
	  try_to_draw(side, maybedraw, "mplayer");
	mplayer(side)->analyzegame = FALSE;
	/* Summarize our analysis of this game. */
	DMprintf("%s will try to %s this game\n",
		 side_desig(side),
		 mplayer(side)->trytowin ? "win" : "have fun in");
	for (i = 0; i < mplayer(side)->numgoals; ++i) {
	    goal = mplayer(side)->goals[i];
	    DMprintf("%s has %s\n", side_desig(side), goal_desig(goal));
	}
    }
}

static void
determine_subgoals(side)
Side *side;
{
    int numvicgoals, understood;
    Unit *unit;
    Side *side2;
    Scorekeeper *sk;
    Goal *goal;

    understood = TRUE;
    /* Look at each scorekeeper and decide on appropriate goals. */
    for_all_scorekeepers(sk) {
	/* (should test who scorekeeper applies to) */
	if (match_keyword(sk->body, K_LAST_SIDE_WINS)
	    || match_keyword(sk->body, K_LAST_ALLIANCE_WINS)) {
	    /* We want to "kick butt" - *everybody* else's butt. */
	    for_all_sides(side2) {
		if (!trusted_side(side, side2) && side2->ingame) {
		    /* Our goals include preventing other sides from accomplishing
		       theirs. */
		    goal = create_goal(GOAL_WON_GAME, side2, FALSE);
		    add_goal(side, goal);
		    /* (should add "search-and-destroy" as corollaries) */
		}
	    }
	    /* Add goals to protect our own units. */
	    numvicgoals = 0;
	    for_all_side_units(side, unit) {
		if (point_value(unit) > 0  /* (should be "n most valuable") */
		    && in_play(unit)
		    && numvicgoals < 10) {
		    goal = create_goal(GOAL_VICINITY_HELD, side, TRUE);
		    goal->args[0] = unit->x;  goal->args[1] = unit->y;
		    goal->args[2] = goal->args[3] = 2;
		    add_goal(side, goal);
		    ++numvicgoals;
		}
	    }
	} else if (sk->initial != -10001) {
	    /* This is a numerical scorekeeper whose value we want to maximize. */
	    if (consp(sk->body) && match_keyword(car(sk->body), K_SET)) {
		if (consp(cadr(sk->body)) && match_keyword(car(cadr(sk->body)), K_SUM)) {
		    understood = FALSE;
		} else {
		    understood = FALSE;
		}
	    } else {
		understood = FALSE;
	    }
	} else {
	    understood = FALSE;
	}
    }
    
    if (!understood) {
	/* Can't notify anybody yet, no windows up, so record */
	mplayer(side)->report_not_understood = TRUE;
	DMprintf("%s AI doesn't understand scoring in this game!", short_side_title(side));
    }
    /* We might develop a sudden interest in exploration. */
    /* (but should only be if information is really important to winning) */
    if (!all_see_all) {
	if (!g_terrain_seen()) {
	    add_goal(side, create_goal(GOAL_WORLD_KNOWN, side, TRUE));
	}
	/* It will be important to keep track of other sides' units
	   as much as possible. */
	for_all_sides(side2) {
	    if (side != side2) {
		goal = create_goal(GOAL_POSITIONS_KNOWN, side, TRUE);
		goal->args[0] = (long) side2;
		add_goal(side, goal);
	    }
	}
	/* Also add the general goal of knowing where indeps are. */
	goal = create_goal(GOAL_POSITIONS_KNOWN, side, TRUE);
	goal->args[0] = (long) NULL;
	add_goal(side, goal);
    }
}

/* Do a combination of analyzing existing theaters and creating new ones. */

static void
review_theaters(side)
Side *side;
{
    int x, y, u, s, pop, totnumunits;
    int firstcontact = FALSE;
    int homefound = FALSE;
    short view;
    Unit *unit;
    Side *firstcontactside, *homefoundside, *otherside, *side2;
    Theater *theater;

    /* Create some theaters if none exist. */
    if (mplayer(side)->theaters == NULL) {
	create_initial_theaters(side);
    }
    for_all_theaters(side, theater) {
	theater->allied_units = 0;
	theater->makers = 0;
	theater->unexplored = 0;
	theater->border = FALSE;
	theater->allied_bases = 0;
	for_all_unit_types(u) {
	    theater->numassigned[u] = 0;
	    theater->numneeded[u] = 0;
	    theater->numenemies[u] = 0;
	    theater->numsuspected[u] = theater->numsuspectedmax[u] = 0;
	    theater->numtotransport[u] = 0;
	}
	if (people_sides_defined()) {
	    for (s = 0; s <= numsides; ++s)
	      theater->people[s] = 0;
	}
	theater->units_lost /= 2;
    }
    compute_theater_bounds(side);
    /* Now look at all the units that we can. */
    for_all_side_units(side, unit) {
    	if (in_play(unit)) {
	    theater = unit_theater(unit);
	    if (theater != NULL) {
		++(theater->allied_units);
		++(theater->numassigned[unit->type]);
		if (isbase(unit))
		  ++(theater->allied_bases);
		if (unit->plan
		    && unit->plan->waitingfortransport)
		  ++(theater->numtotransport[unit->type]);
	    }
	}
    }
    /* (should also analyze allies etc) */
    /* Now look at the whole world. */
    for_all_interior_cells(x, y) {
	theater = theater_at(side, x, y);
	if (theater != NULL) {
	    if (can_see_actual_units(side, x, y)) {
	    	for_all_stack(x, y, unit) {
	    	    /* what about occupants? */
	    	    if (in_play(unit)
	    	    	&& !trusted_side(side, unit->side)
	    	    	&& (!indep(unit)
	    	    	    || u_point_value(unit->type) > 0)) {
			if (enemy_side(side, unit->side))
			  ++(theater->numenemies[unit->type]);
	    	    	if (mplayer(side)->contacted[side_number(unit->side)] == 0) {
			    mplayer(side)->contacted[side_number(unit->side)] = 1;
			    if (!indep(unit)) {
				firstcontact = TRUE;
				firstcontactside = unit->side;
			    }
	    	    	}
	    	    	if (mplayer(side)->homefound[side_number(unit->side)] == 0
	    	    	    && !mobile(unit->type)) {
			    mplayer(side)->homefound[side_number(unit->side)] = 1;
			    if (!indep(unit)) {
				homefound = TRUE;
				homefoundside = unit->side;
			    }
	    	    	}
	    	    }
	    	}
		if (people_sides_defined()) {
		    pop = people_side_at(x, y);
		    if (pop != NOBODY) {
			++(theater->people[pop]);
	    	    	if (mplayer(side)->homefound[pop] == 0) {
			    mplayer(side)->homefound[pop] = 1;
			    if (pop != 0) {
				homefound = TRUE;
				homefoundside = side_n(pop);
			    }
	    	    	}
		    }
		}
	    } else {
		if (terrain_view(side, x, y) == UNSEEN) {
		    ++(theater->unexplored);
		} else {
		    view = unit_view(side, x, y);
		    if (view != EMPTY) {
			side2 = side_n(vside(view));
			if (side2 == NULL) {
			    u = vtype(view);
			    /* (should rate by value of capture) */
			    if (u_point_value(u) > 0) {
				++(theater->numsuspected[u]);
				++(theater->numsuspectedmax[u]);
			    }
			} else if (enemy_side(side, side2)) {
			    u = vtype(view);
			    if (u_point_value(u) > 0) {
				++(theater->numsuspected[u]);
				++(theater->numsuspectedmax[u]);
			    }
			}
		    }
		    if (people_sides_defined()) {
			pop = people_side_at(x, y);
			if (pop != NOBODY) {
			    ++(theater->people[pop]);
			}
		    }
		}
	    }
	}
    }
    for_all_theaters(side, theater) {
    	theater->x = (theater->xmin + theater->xmax) / 2;
    	theater->y = (theater->ymin + theater->ymax) / 2;
    	theater->enemystrengthmin = theater->enemystrengthmax = 0;
    	for_all_unit_types(u) {
	    theater->enemystrengthmin +=
	      theater->numenemies[u] + theater->numsuspected[u];
	}
	theater->enemystrengthmax = theater->enemystrengthmin;
    }
    if (firstcontact || homefound) {
    	for_all_side_units(side, unit) {
	    if (unit->plan && unit->plan->aicontrol) {
		net_force_replan(side, unit, FALSE);
		set_unit_theater(unit, NULL);
		update_unit_display(side, unit, TRUE);
	    }
	}
    }
    for_all_theaters(side, theater) {
	DMprintf("%s theater \"%s\" at %d,%d from %d,%d to %d,%d (size %d)\n",
		 side_desig(side), theater->name, theater->x, theater->y,
		 theater->xmin, theater->ymin, theater->xmax, theater->ymax,
		 theater->size);
	/* Summarize what we know about the theater. */
	DMprintf("%s theater \"%s\"", side_desig(side), theater->name);
	if (!all_see_all && theater->unexplored > 0) {
	    DMprintf(" unexplored %d", theater->unexplored);
	}
	DMprintf(" enemy %d", theater->enemystrengthmin);
	if (theater->enemystrengthmin != theater->enemystrengthmax) {
	    DMprintf("-%d", theater->enemystrengthmax);
	}
	for_all_unit_types(u) {
	    if (theater->numenemies[u] + theater->numsuspected[u] > 0) {
	    	DMprintf(" %3s %d", u_type_name(u), theater->numenemies[u]);
	    	if (theater->numsuspected[u] > 0) {
		    DMprintf("+%d", theater->numsuspected[u]);
		}
	    }
	}
	if (people_sides_defined()) {
	    DMprintf(" people");
	    for (s = 0; s <= numsides; ++s) {
		if (theater->people[s] > 0) {
		    DMprintf(" s%d %d", s, theater->people[s]);
		}
	    }
	}
	DMprintf("\n");
	totnumunits = 0;
	for_all_unit_types(u) {
	    totnumunits +=
	      (theater->numassigned[u] + theater->numneeded[u] + theater->numtotransport[u]);
	}
	if (totnumunits > 0) {
	    /* Summarize the status of our own units in this theater. */
	    DMprintf("%s theater \"%s\" has ", side_desig(side), theater->name);
	    for_all_unit_types(u) {
		if (theater->numassigned[u] + theater->numneeded[u] + theater->numtotransport[u] > 0) {
		    DMprintf(" %d %3s", theater->numassigned[u], u_type_name(u));
			if (theater->numneeded[u] > 0) {
			    DMprintf(" (of %d needed)", theater->numneeded[u]);
			}
			if (theater->numtotransport[u] > 0) {
			    DMprintf(" (%d awaiting transport)", theater->numtotransport[u]);
			}
		}
	    }
	    DMprintf("\n");
	}
    }
    /* Also summarize contacts. */
    for_all_sides(otherside) {
    	if (otherside != side) {
	    if (mplayer(side)->contacted[otherside->id]) {
		DMprintf("%s contacted s%d", side_desig(side), otherside->id);
		if (mplayer(side)->homefound[otherside->id]) {
		    DMprintf(", home found");
		}
		DMprintf("\n");
	    }
    	}
    }
}

/* Set up the initial set of theaters. */

static void
create_initial_theaters(side)
Side *side;
{
    int x, y, dir, dist, i, j;
    int xmin, ymin, xmax, ymax;
    int homeradius, perimradius, midradius, xxx;
    int numthx, numthy, thwid, thhgt;
    Unit *unit;
    Theater *homefront, *enemyarea, *theater;
    Theater *gridtheaters[8][8];
    Strategy *strategy = mplayer(side);
    
    for (i = 0; i < 8; ++i) {
	for (j = 0; j < 8; ++j) {
	    gridtheaters[i][j] = NULL;
	}
    }
    /* Compute bbox of initial (should also do enemy?) units. */
    xmin = area.width;  ymin = area.height;  xmax = ymax = 0;
    for_all_side_units(side, unit) {
	if (alive(unit) /* and other preconditions? */) {
	    if (unit->x < xmin)
	      xmin = unit->x;
	    if (unit->y < ymin)
	      ymin = unit->y;
	    if (unit->x > xmax)
	      xmax = unit->x;
	    if (unit->y > ymax)
	      ymax = unit->y;
	}
    }
    /* Most games start with each side's units grouped closely together.
       If this is not the case, do something else. */
    if (xmax - xmin > area.width / 4 && ymax - ymin > area.height / 4) {
	/* (should do some sort of clustering of units) */
	if (0 /*people_sides_defined()*/) {
	    homefront = create_theater(side);
	    homefront->name = "Home Front";
	    enemyarea = create_theater(side);
	    enemyarea->name = "Enemy Area";
	    for_all_interior_cells(x, y) {
	        if (people_side_at(x, y) == side->id) {
		    set_theater_at(side, x, y, homefront);
	        } else {
		    set_theater_at(side, x, y, enemyarea);
	        }
	    }
	} else {
	    /* Divide the world up along a grid. */
	    numthx = (area.width  > 60 ? (area.width  > 120 ? 7 : 5) : 3);
	    numthy = (area.height > 60 ? (area.height > 120 ? 7 : 5) : 3);
	    thwid = max(8, area.width / numthx);
	    thhgt = max(8, area.height / numthy);
	    for_all_interior_cells(x, y) {
		i = x / thwid;  j = y / thhgt;
		if (gridtheaters[i][j] == NULL) {
		    theater = create_theater(side);
		    sprintf(spbuf, "Grid %d,%d", i, j);
		    theater->name = copy_string(spbuf);
		    theater->x = x;  theater->y = y;
		    gridtheaters[i][j] = theater;
		} else {
		    theater = gridtheaters[i][j];
		}
		set_theater_at(side, x, y, theater);
	    }
	}
	return;
    } else {
	/* Always create a first theater that covers the starting area. */
	homefront = create_theater(side);
	homefront->name = "Home Front";
	/* Calculate startxy if not already available. */
	if (side->startx < 0 && side->starty < 0)
	  calc_start_xy(side);
	homefront->x = side->startx;  homefront->y = side->starty;
	strategy->homefront = homefront;
	homeradius = max(5, g_radius_min());
	perimradius = max(homeradius + 5, g_separation_min() - homeradius);
	midradius = max(perimradius + 10, g_separation_min() * 2);
	xxx = max((side->startx - perimradius), (area.width - side->startx - perimradius));
	xxx /= 2;
	midradius = min(midradius, perimradius + xxx);
	for_all_interior_cells(x, y) {
	    dist = distance(x, y, side->startx, side->starty);
	    if (people_sides_defined()
		&& people_side_at(x, y) == side->id
		&& dist < (perimradius - 3)) {
		set_theater_at(side, x, y, homefront);
	    } else {
		if (dist < homeradius) {
		    set_theater_at(side, x, y, homefront);
		} else {
		    dir = approx_dir(x - side->startx, y - side->starty);
		    if (dist < perimradius) {
			if (strategy->perimeters[dir] == NULL) {
			    theater = create_theater(side);
			    sprintf(spbuf, "Perimeter %s", dirnames[dir]);
			    theater->name = copy_string(spbuf);
			    theater->x = x;  theater->y = y;
			    strategy->perimeters[dir] = theater;
			} else {
			    theater = strategy->perimeters[dir];
			}
		    } else if (dist < midradius) {
			if (strategy->midranges[dir] == NULL) {
			    theater = create_theater(side);
			    sprintf(spbuf, "Midrange %s", dirnames[dir]);
			    theater->name = copy_string(spbuf);
			    theater->x = x;  theater->y = y;
			    strategy->midranges[dir] = theater;
			} else {
			    theater = strategy->midranges[dir];
			}
		    } else {
			if (strategy->remotes[dir] == NULL) {
			    theater = create_theater(side);
			    sprintf(spbuf, "Remote %s", dirnames[dir]);
			    theater->name = copy_string(spbuf);
			    theater->x = x;  theater->y = y;
			    strategy->remotes[dir] = theater;
			} else {
			    theater = strategy->remotes[dir];
			}
		    }
		    set_theater_at(side, x, y, theater);
	    	}
	    }
	}  
    }
    remove_small_theaters(side);
    /* Assign all units to the theater they're currently in. */
    /* (how do reinforcements get handled? mplayer should get hold of perhaps) */
    for_all_side_units(side, unit) {
	if (in_play(unit) /* and other preconditions? */) {
	    set_unit_theater(unit, theater_at(side, unit->x, unit->y));
	}
    }
}

/* Create a single theater object and link it into the list of
   theaters. */

/* (should be able to re-use theaters in already in theater table) */

static Theater *
create_theater(side)
Side *side;
{
    Theater *theater = (Theater *) xmalloc(sizeof(Theater));
    
    if (mplayer(side)->numtheaters > MAXTHEATERS)
      return NULL;
    theater->id = (mplayer(side)->numtheaters)++;
    theater->name = "?";
    theater->maingoal = NULL;
    theater->people = (int *) xmalloc ((numsides + 1) * sizeof(int));
    /* (should alloc other array slots too) */
    /* Connect theater into a linked list. */
    theater->next = mplayer(side)->theaters;
    mplayer(side)->theaters = theater;
    /* Install it into the theater table also. */
    mplayer(side)->theatertable[theater->id] = theater;
    return theater;
}

/* Clear all references to the theater and remove it from the list.
   Note that the theater size must already be zero. */

static void
remove_theater(side, theater)
Side *side;
Theater *theater;
{
    int dir;
    Theater *prev;

    if (mplayer(side)->homefront == theater)
      mplayer(side)->homefront = NULL;
    for_all_directions(dir) {
	if (mplayer(side)->perimeters[dir] == theater)
	  mplayer(side)->perimeters[dir] = NULL;
	if (mplayer(side)->midranges[dir] == theater)
	  mplayer(side)->midranges[dir] = NULL;
	if (mplayer(side)->remotes[dir] == theater)
	  mplayer(side)->remotes[dir] = NULL;
    }
    if (mplayer(side)->theaters == theater)
      mplayer(side)->theaters = theater->next;
    else {
	prev = NULL;
	for_all_theaters(side, prev) {
	    if (prev->next == theater) {
		prev->next = theater->next;
		break;
	    }
	}
	/* If prev still null, badness */
    }
    --(mplayer(side)->numtheaters);
}

static void move_theater_cell(int x, int y);

static void
move_theater_cell(int x, int y)
{
    int dir, x1, y1;
    Theater *theater2;

    if (theater_at(tmpside, x, y) == tmptheater) {
	for_all_directions(dir) {
	    if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
		theater2 = theater_at(tmpside, x1, y1);
		if (theater2 != NULL && theater2 != tmptheater) {
		    set_theater_at(tmpside, x, y, theater2);
		    ++(theater2->size);
		    /* (should recompute bbox too) */
		    --(tmptheater->size);
		}
	    }
	}
    }
}

static void
remove_small_theaters(side)
Side *side;
{
    int domore;
    Theater *theater;

    compute_theater_bounds(side);
    domore = TRUE;
    while (domore) {
	domore = FALSE;
	for_all_theaters(side, theater) {
	    if (between(1, theater->size, 5)) {
		tmpside = side;
		tmptheater = theater;
		apply_to_area(theater->x, theater->y, 6, move_theater_cell);
		if (theater->size == 0) {
		    remove_theater(side, theater);
		    /* Have to start over now. */
		    domore = TRUE;
		    break;
		}
	    }
	}
    }
    /* Redo, many random changes to bounds. */
    compute_theater_bounds(side);
}

/* Compute the size and bounding box of each theater.  This should be run
   each time theaters change in size or shape. */

static void
compute_theater_bounds(side)
Side *side;
{
    int x, y;
    Theater *theater;

    for_all_theaters(side, theater) {
	theater->size = 0;
	theater->xmin = theater->ymin = -1;
	theater->xmax = theater->ymax = -1;
    }
    for_all_interior_cells(x, y) {
	theater = theater_at(side, x, y);
	if (theater != NULL) {
	    ++(theater->size);
	    /* Compute bounding box of theater if not already done. */
	    if (theater->xmin < 0 || x < theater->xmin)
	      theater->xmin = x;
	    if (theater->ymin < 0 || y < theater->ymin)
	      theater->ymin = y;
	    if (theater->xmax < 0 || x > theater->xmax)
	      theater->xmax = x;
	    if (theater->ymax < 0 || y > theater->ymax)
	      theater->ymax = y;
	}
    }
}

/* Examine the goals to see what has been accomplished and what still needs
   to be done. */

static void
review_goals(side)
Side *side;
{
    int i;
    Scorekeeper *sk;
    Goal *goal;
    Side *side2;
    Strategy *strategy = mplayer(side);

    /* First check on our friends and enemies. */
    for_all_sides(side2) {
	/* If they're not trusting us, we don't want to trust them. */
	/* (should be able to update this immediately after other side changes trust) */
	if (!trusted_side(side2, side) && trusted_side(side, side2))
	  net_set_trust(side, side2, FALSE);
    }
    for (i = 0; i < strategy->numgoals; ++i) {
	goal = strategy->goals[i];
	DMprintf("%s has %s\n", side_desig(side), goal_desig(goal));
    }
    /* Should look at certainty of each goal and decide whether to keep or
       drop it, and mention in debug output also. */
    /* Also think about resigning. */
    if (keeping_score()) {
	for_all_scorekeepers(sk) {
	    if (symbolp(sk->body)
		&& (match_keyword(sk->body, K_LAST_SIDE_WINS)
		    || match_keyword(sk->body, K_LAST_ALLIANCE_WINS))) {
		decide_resignation(side);
	    }
	}
    }
}

static void
estimate_strengths(side)
Side *side;
{
    int u, sn1, x, y, uview;
    Side *side1, *side2;
    Strategy *strategy = mplayer(side);
    Unit *unit;
 
    for_all_sides(side1) {
	sn1 = side_number(side1);
	for_all_unit_types(u) {
	    strategy->strengths[sn1][u] = 0;
	}
	/* this lets us count even semi-trusted allies' units accurately... */
	if (side1 == side || allied_side(side, side1)) {
	    for_all_side_units(side1, unit) {
		/* Note that we count off-area units, since they are reinforcements
		   usually. */
		if (alive(unit) && completed(unit)) {
		    ++(strategy->strengths[sn1][unit->type]);
		}
	    }
	}
    }
    if (all_see_all
	|| (!strategy->initial_strengths_computed 
	    && !mplayer_read_strengths(side))) {
	/* If we can see everything, we can add up units accurately.  We also allow
	   initial strengths to be known accurately; this prevents the AI from
	   resigning just because an important unit appears for the first time,
	   even though all players know that it was one of the starting units. */ 
	for_all_cells(x, y) {
	    for_all_stack(x, y, unit) {
		side2 = unit->side;
		if (side2 != NULL && !(side2 == side || allied_side(side, side2))) {
		    if (completed(unit)) {
		        ++(strategy->strengths[side2->id][unit->type]);
		    }
		}
	    }
	}
    } else {
	/* Look at the current view to get enemy strength. */
	/* This is too easily faked, and doesn't know about hiding units... */
	/* Should also discount old data. */
	for_all_cells(x, y) {
	    uview = unit_view(side, x, y);
	    if (uview != UNSEEN && uview != EMPTY) {
		side2 = side_n(vside(uview));
		/* Count only units on other sides. */
		if (side2 != NULL && !(side2 == side || allied_side(side, side2))) {
		    ++(strategy->strengths[side2->id][vtype(uview)]);
		}
	    }
	}
    }
    /* Estimate point values. */
    /* (should try to account for individual units with special point values) */
    for_all_sides(side1) {
	sn1 = side1->id;
	strategy->points[sn1] = 0;
	for_all_unit_types(u) {
	    strategy->points[sn1] +=
	      strategy->strengths[sn1][u] * u_point_value(u);
	}
    }
    /* Estimate how many of each type in allied group. */
    for_all_sides(side1) {
	sn1 = side1->id;
	for_all_unit_types(u) {
	    strategy->alstrengths[sn1][u] = strategy->strengths[sn1][u];
	    for_all_sides(side2) {
		if (side1 != side2 && allied_side(side1, side2)) {
		    strategy->alstrengths[sn1][u] +=
		      strategy->strengths[side2->id][u];
		}
	    }
	}
	strategy->alpoints[sn1] = strategy->points[sn1];
	for_all_sides(side2) {
	    if (side1 != side2 && allied_side(side1, side2)) {
		strategy->alpoints[sn1] += strategy->points[side2->id];
	    }
	}
    }
    /* The first time we estimate strength, record it specially.  Later we will
       use this to compute which sides are getting stronger/weaker as time
       goes on. */
    if (!strategy->initial_strengths_computed) {
	if (!mplayer_read_strengths(side)) {
	    for_all_sides(side1) {
		sn1 = side1->id;
		strategy->points0[sn1] = strategy->points[sn1];
		strategy->alpoints0[sn1] = strategy->alpoints[sn1];
		for_all_unit_types(u) {
		    strategy->strengths0[sn1][u] =
		      strategy->strengths[sn1][u];
		    strategy->alstrengths0[sn1][u] =
		      strategy->alstrengths[sn1][u];
		}
	    }
	}
	ai_save_state(side);
	strategy->initial_strengths_computed = TRUE;
    }
    /* If we're calling strength estimation because a new side has
       come into existence, use the current strengths as the new
       side's initial strengths. */
    if (anewside != NULL) {
	sn1 = anewside->id;
	strategy->points0[sn1] = strategy->points[sn1];
	strategy->alpoints0[sn1] = strategy->alpoints[sn1];
	for_all_unit_types(u) {
	    strategy->strengths0[sn1][u] =
	      strategy->strengths[sn1][u];
	    strategy->alstrengths0[sn1][u] =
	      strategy->alstrengths[sn1][u];
	}
	/* Have to redo the saveable state also; force this by blasting any
	   existing recordable state (it should all be re-creatable from
	   mplayer's internal state). */
	strategy->writable_state = lispnil;
	ai_save_state(side);
    }
    /* Dump out a detailed listing of our estimates. */
    if (DebugM) {
	for_all_sides(side1) {
	    sn1 = side1->id;
	    DMprintf("%s ", side_desig(side));
	    DMprintf("est init streng of %s: ", side_desig(side1));
	    for_all_unit_types(u) {
		DMprintf(" %d", strategy->strengths0[sn1][u]);
	    }
	    DMprintf(" (%d points)\n", strategy->points0[sn1]);
	    DMprintf("%s ", side_desig(side));
	    DMprintf("est curr streng of %s: ", side_desig(side1));
	    for_all_unit_types(u) {
		DMprintf(" %d", strategy->strengths[sn1][u]);
	    }
	    DMprintf(" (%d points)\n", strategy->points[sn1]);
	    DMprintf("%s ", side_desig(side));
	    DMprintf("est init allied of %s: ", side_desig(side1));
	    for_all_unit_types(u) {
		DMprintf(" %d", strategy->alstrengths0[sn1][u]);
	    }
	    DMprintf(" (%d points)\n", strategy->alpoints0[sn1]);
	    DMprintf("%s ", side_desig(side));
	    DMprintf("est curr allied of %s: ", side_desig(side1));
	    for_all_unit_types(u) {
		DMprintf(" %d", strategy->alstrengths[sn1][u]);
	    }
	    DMprintf(" (%d points)\n", strategy->alpoints[sn1]);
	}
    }
}

/* Sometimes there is no point in going on, but be careful not to be too
   pessimistic.  Right now we only give up if no hope at all.  Currently
   this is only used if there is a last-side-wins scorekeeper; it would
   need to be modified considerably to be useful with scorekeepers in
   general. */

static void
decide_resignation(side)
Side *side;
{
    int ratio, ratio0, chance = 0, sn1;
    Side *side1;
    Strategy *strategy = mplayer(side);

    estimate_strengths(side);
    /* If our estimate of our own points is zero, then we're about to lose the
       game anyway, so just return and avoid screwing up ratio calcs below. */
    if (strategy->alpoints[side->id] <= 0)
      return;
    for_all_sides(side1) {
	if (side != side1 && side1->ingame && !allied_side(side, side1)) {
	    sn1 = side1->id;
	    /* Note that ratio calculations always scale up by 100, so that we
	       can do finer comparisons without needing floating point. */
	    ratio = (strategy->alpoints[sn1] * 100) / strategy->alpoints[side->id];
	    /* code specific to the "time" game */
	    /* the mplayer can severely underestimates its own strength */
	    if (game_class == gc_time) {
		ratio /= 3;
	    }

	    if (strategy->alpoints0[side->id] > 0) {
		ratio0 = (strategy->alpoints0[sn1] * 100) / strategy->alpoints0[side->id];
		/* If we estimated 0 points for some side's initial strength,
		   then our estimate is bad; assume parity. */
		if (ratio0 <= 0)
		  ratio0 = 100;
		/* This formula basically calls for no resignation if ratio is no more
		   than twice what it was initially, 50% chance if ratio is four times
		   what it was (if we started out even, then we're outnumbered 4 to 1),
		   and interpolates for ratios in between. */
		chance = (((ratio * 100) / ratio0) - 200) / 5;
		chance = max(chance, 0);
		chance = min(chance, 90);
	    } else {
		/* work by absolute ratios */
		if (ratio > 400) {
		    chance = ratio / 10;
		}
		chance = min(chance, 90);
	    }
	}
    }

    /* Whether or not we actually resign, we may be willing to
       go for a draw if other players want to. */
    /* (Note that toggling this flag is not exactly poker-faced
       behavior, but I doubt human players will be able to derive
       much advantage, since they'll already have a pretty good
       idea if the AI is in trouble or not.) */
    try_to_draw(side, (chance > 0), "mplayer");
    /* Maybe resign. */
    if (chance > 0) {
	if (probability(chance)) {
	    give_up(side, "mplayer");
	}
    }
}

/* Go through all our units (and allied ones?). */

static void
review_units(side)
Side *side;
{
    int u, u2, cp, cpmin, any;
    int numoffensive[MAXUTYPES], numdefensive[MAXUTYPES];
    Unit *unit, *occ, *unit2;
    Plan *plan;
    Theater *oldtheater, *theater;

    /* This code is specific to the "time" game. */
    if (game_class == gc_time) {
	for_all_unit_types(u) {
	    u2 = mplayer(side)->develop_on[u];
	    if (mplayer(side)->develop_status[u] == RS_DEVELOP_ASSIGNED) {
		/* is anyone developing? */
		unit2 = NULL;
		for_all_side_units(side, unit) {
		    if (unit->type==u && in_play(unit) &&
			unit->plan && unit->plan->aicontrol) {
			if (unit->plan->tasks &&
			    unit->plan->tasks->type == TASK_DEVELOP &&
			    unit->plan->tasks->args[0] == u2) 
			  unit2 = unit;
		    }
		}
		if (unit2 != NULL) {
		    DMprintf("%s is developing for %s on %s (level %d/%d)\n",
			     unit_desig(unit2), side_desig(side),
			     u_type_name(u2),
			     side->tech[u2], u_tech_to_build(u2));
		} else {
		    DMprintf("no %s is developing for %s on %s!\n",
			     u_type_name(u), side_desig(side),
			     u_type_name(u2));
		    mplayer(side)->develop_status[u] = RS_DEVELOP_NEEDED;
		}
	    }
	    if (mplayer(side)->develop_status[u] == RS_DEVELOP_NEEDED
		&& needs_develop (side, u2)) {
		/* pick for develop a unit not building; 
		   if all are building, choose the one which started last */
		unit2 = NULL;
		cpmin = 9999;
		any = 0;
		for_all_side_units(side, unit) {
		    if (unit->type == u
			&& in_play(unit)
			&& unit->plan
			&& unit->plan->aicontrol) {
			any = 1;
			cp = 0;
			occ = NULL;
			if ((unit->plan->tasks != NULL
			     && unit->plan->tasks->type == TASK_BUILD))
			  occ = find_unit_to_complete(unit, unit->plan->tasks);
			if (occ != NULL) {
			    cp = occ->cp - uu_creation_cp(u,occ->type);
			    if (uu_cp_per_build(u,u2) > 0)
			      cp /= uu_cp_per_build(u,u2);
			}
			if (cp < cpmin) {
			    unit2 = unit;
			    cpmin = cp;
			}
		    }
		}
		if (unit2 == NULL) {
		    if (any)
		      DMprintf("no %s is available to develop for %s on %s!\n",
			       u_type_name(u), side_desig(side),
			       u_type_name(u2));
		} else {
		    if (assign_to_develop_on(side, unit2, u2)) {
			mplayer(side)->develop_status[u] =
			  RS_DEVELOP_ASSIGNED;
		    }
		}
	    }
	}
    }

    for_all_unit_types(u) {
	numoffensive[u] = numdefensive[u] = 0;
    }
    for_all_side_units(side, unit) {
	if (in_play(unit) && unit->plan && unit->plan->aicontrol) {
	    /* Count plan types. */
	    switch (unit->plan->type) {
	      case PLAN_OFFENSIVE:
	      case PLAN_EXPLORATORY:
		++numoffensive[unit->type];
		break;
	      case PLAN_DEFENSIVE:
		++numdefensive[unit->type];
		break;
	      default:
	        break;
	    }
	}
    }

    for_all_side_units(side, unit) {
	if (in_play(unit) && unit->plan && unit->plan->aicontrol) {
	    /* code specific to the "time" game */
	    if (game_class == gc_time) {
		u = unit->type;
		u2 = mplayer(side)->develop_on[u];

		/* should we upgrade? */
		if (mplayer(side)->develop_status[u] == RS_UPGRADE_NEEDED) {
		    cp = 0;
		    occ = NULL;
		    if ((unit->plan->tasks != NULL &&
			 unit->plan->tasks->type == TASK_BUILD))
		      occ = find_unit_to_complete(unit, unit->plan->tasks);
		    if (occ != NULL) {
			cp = occ->cp - uu_creation_cp(u,occ->type);
			if (uu_cp_per_build(u,u2)>0)
			  cp /= uu_cp_per_build(u,u2);
		    }
		    if (occ != NULL && occ->type==u2) {
			/* already upgrading */
			DMprintf("%s is upgrading to %s (%d/%d cp)\n",
				 unit_desig(unit), u_type_name(u2),
				 occ->cp, u_cp(occ->type));
		    } else if (cp >= u_cp(u2)/4) { /* rule-of-thumb... */
			/* complete unit under construction */
			DMprintf("%s will complete %s (now %d/%d cp) before upgrading to %s\n",
				 unit_desig(unit), u_type_name(occ->type),
				 occ->cp, u_cp(occ->type), u_type_name(u2));
		    } else {
			/* start upgrading */
			if (occ != NULL && !fullsized(occ)) {
			    DMprintf("%s will drop work on %s (%d/%d cp) and immediately start upgrading to %s\n",
				 unit_desig(unit), u_type_name(occ->type),
				 occ->cp, u_cp(occ->type), u_type_name(u2));
			} else {
			    DMprintf("%s will start upgrading to %s\n",
				     unit_desig(unit), u_type_name(u2));
			}
			net_set_unit_plan_type(side, unit, PLAN_PASSIVE);
			net_clear_task_agenda(side, unit);
			net_set_build_task(unit, u2, 1);
		    }
		}
	    }

	    plan = unit->plan;
	    oldtheater = unit_theater(unit);
	    /* Goal might have become satisfied. */
	    if (plan->maingoal) {
		if (goal_truth(side, plan->maingoal) == 100) {
		    DMprintf("%s %s satisfied, removing\n",
			     unit_desig(unit), goal_desig(plan->maingoal));
		    net_force_replan(side, unit, FALSE);
		    set_unit_theater(unit, NULL);
		}
	    }
	    /* Theater might have become explored enough (90% known). */
	    if (plan->type == PLAN_EXPLORATORY
	        && (theater = unit_theater(unit)) != NULL
	        && theater->unexplored < theater->size / 10) {
		    DMprintf("%s theater %s is mostly known\n",
			     unit_desig(unit), theater->name);
		    net_force_replan(side, unit, FALSE);
		    set_unit_theater(unit, NULL);
	    }
	    /* Don't let defense-only units pile up. */
	    if (plan->type == PLAN_DEFENSIVE
		&& mobile(unit->type)
		&& (numoffensive[unit->type] / 3) < numdefensive[unit->type]
		&& flip_coin()) {
		DMprintf("%s one of too many on defense (%d off, %d def), replanning\n",
			 unit_desig(unit), numoffensive[unit->type], numdefensive[unit->type]);
		net_force_replan(side, unit, FALSE);
	    }
	    theater = unit_theater(unit);
	    DMprintf("%s currently assigned to %s",
	    	     unit_desig(unit),
		     (theater ? theater->name : "no theater"));
	    if (oldtheater != theater) {
	    	DMprintf(" (was %s)",
			 (oldtheater ? oldtheater->name : "no theater"));
	    }
	    DMprintf("\n");
	}
    }
}

/* Look at our current overall strategy and hack it as needed. */

static void
update_side_strategy(side)
Side *side;
{
    Theater *theater;

    DMprintf("%s updating strategy\n", side_desig(side));
    /* Add something to add/update theaters as things open up. (?) */
    for_all_theaters(side, theater) {
	decide_theater_needs(side, theater);
    }
}

/* Figure out how many units to request for each area. */

static void
decide_theater_needs(side, theater)
Side *side;
Theater *theater;
{
    if (theater->unexplored > 0) {
    	/* Exploration is less important when 90% of a theater is known. */
    	if (theater->unexplored > (theater->size / 10)) {
	    ++(mplayer(side)->explorersneeded);
    	}
	/* Should look for good exploration units. */
	theater->importance = 50;  /* should depend on context */
/*	theater->reinforce = EXPLORE_AREA;  */
#if 0
    } else if (0 /* few enemies? */) {
	if (theater->allied_makers == 0
	    && theater->makers > 0
	    && theater->nearby) {
	    theater->reinforce = GUARD_BORDER_TOWN + 2 * theater->makers;
	} else if (theater->makers > 0) {
	    theater->reinforce = (theater->border ? GUARD_BORDER_TOWN :
				  GUARD_TOWN) + 2 * theater->allied_makers;
	} else if (theater->allied_bases > 0) {
	    theater->reinforce = (theater->border ? GUARD_BORDER: GUARD_BASE);
	} else if (theater->border) {
	    theater->reinforce = NO_UNITS;
	} else {
	    theater->reinforce = NO_UNITS;
	}
    } else {
	if (theater->allied_makers > 0) {
	    theater->reinforce = DEFEND_TOWN + 5 * theater->makers;
	} else if (theater->allied_bases > 0) {
	    theater->reinforce = DEFEND_BASE + theater->allied_bases;
	} else {
	    theater->reinforce = 0 /* DEFEND_AREA */;
	}
#endif
    }
}

/* For each unit, decide what it should be doing (if anything).  This is
   when a side takes the initiative;  a unit can also request info from
   its side when it is working on its individual plan. */

static void
update_unit_plans(side)
Side *side;
{
    Unit *unit;

    for_all_side_units(side, unit) {
	if (is_active(unit) && unit->plan != NULL) {
	    mplayer_decide_plan(side, unit);
	}
    }
}

/* Randomly change a unit's plans.  (This is really more for
   debugging, exercising plan execution code in novel ways.) */

static void
update_unit_plans_randomly(side)
Side *side;
{
    Unit *unit;

    for_all_side_units(side, unit) {
	if (is_active(unit) && unit->plan && unit->plan->aicontrol) {
	    if (probability(10)) {
		DMprintf("Randomly changed %s plan %s",
			 unit_desig(unit), plan_desig(unit->plan));
		net_set_unit_plan_type(side, unit, xrandom((int) NUMPLANTYPES));
		DMprintf("to plan %s\n", plan_desig(unit->plan));
	    }
	    /* (should add/remove goals randomly) */
	    if (probability(10) && g_units_may_go_into_reserve()) {
		net_set_unit_reserve(side, unit, TRUE, FALSE);
	    }
	    if (probability(10) && g_units_may_go_to_sleep()) {
		net_set_unit_asleep(side, unit, TRUE, FALSE);
	    }
	}
    }
}

/* Push a new goal onto the side's list of goals. */

/* (this should only add goals that are not already present) */

static void
add_goal(side, goal)
Side *side;
Goal *goal;
{
    if (mplayer(side)->numgoals < MAXGOALS) {
	mplayer(side)->goals[(mplayer(side)->numgoals)++] = goal;
    }
}

static Goal *
has_goal(side, goaltype)
Side *side;
GoalType goaltype;
{
    int i;
    Goal *goal;

    for (i = 0; i < mplayer(side)->numgoals; ++i) {
	goal = mplayer(side)->goals[i];
	if (goal != NULL && goal->type == goaltype) {
	    return goal;
	}
    }
    return NULL;
}

static Goal *
has_unsatisfied_goal(side, goaltype)
Side *side;
GoalType goaltype;
{
    int i;
    Goal *goal;

    for (i = 0; i < mplayer(side)->numgoals; ++i) {
	goal = mplayer(side)->goals[i];
	if (goal != NULL && goal->type == goaltype && goal_truth(side, goal) < 100) {
	    return goal;
	}
    }
    return NULL;
}

/* This is for when a unit needs a plan and asks its side for one. */

static void
mplayer_decide_plan(Side *side, Unit *unit)
{
    Plan *plan = unit->plan;
    int u = unit->type;

    if (plan == NULL || !plan->aicontrol)
      return;
    /* Plans for advanced units are (for now) handled in run.c instead. */
    if (u_advanced(u))
    	return;
    /* code specific to the "time" game */
    /* don't mess up with units developing or upgrading */
    if (game_class == gc_time) {
	if (mplayer(side)->develop_status[u] == RS_UPGRADE_NEEDED
	    && plan->tasks != NULL
	    && plan->tasks->type == TASK_BUILD)
	  return;
	if (mplayer(side)->develop_status[u] == RS_DEVELOP_ASSIGNED
	    && plan->tasks != NULL
	    && plan->tasks->type == TASK_DEVELOP)
	  return;
    }
    /* If we're not trying to win at this game, then make the unit act
       randomly. */
    if (!mplayer(side)->trytowin) {
	net_set_unit_plan_type(side, unit, PLAN_RANDOM);
	net_clear_task_agenda(side, unit);
	return;
    }
    switch (plan->type) {
      case PLAN_PASSIVE:
      case PLAN_NONE:
	if (mobile(u)) {
	    if (u_colonizer_worth(u) > 0) {
		assign_to_colonize(side, unit);
		return;
	    }
	    /* Maybe assign to exploration. */
	    if (has_goal(side, GOAL_WORLD_KNOWN)) {
		if (need_this_type_to_explore(side, u)) {
		    /* also limit to a total percentage, in case
		       exploration needs are very high */
		    assign_to_exploration(side, unit);
		    return;
		}
	    }
	    if (type_can_attack(u) || type_can_fire(u)) {
		/* (A more precise test would be "can attack types
		    known to be or likely to be in the current game".)  */
		/* Assign most units to offense, save some for defense. */
		if (probability(75))
		  assign_to_offense(side, unit);
		else
		  assign_to_defense(side, unit);
	    } else if (type_can_build_attackers(side, u)) {
		assign_to_offense_support(side, unit);
	    } else {
	    }
	} else {
	    if (has_unsatisfied_goal(side, GOAL_VICINITY_HELD)
		&& type_can_build_attackers(side, u)) {
 		    assign_to_offense_support(side, unit);
	    } else if (has_goal(side, GOAL_WORLD_KNOWN)
	        && need_this_type_to_build_explorers(side, u)) {
		assign_to_explorer_construction(side, unit);
	    } else if (type_can_build_attackers(side, u)) {
	    	assign_to_offense_support(side, unit);
	    } else if (type_can_attack(u) || type_can_fire(u)) {
		assign_to_defense(side, unit);
	    } else {
		assign_to_defense_support(side, unit);
	    }
	}
	break;
      case PLAN_OFFENSIVE:
	/* leave plan alone */
	break;
      case PLAN_COLONIZING:
	/* leave plan alone */
	break;
      case PLAN_IMPROVING:
	/* leave plan alone */
	break;
      case PLAN_EXPLORATORY:
	/* leave plan alone */
	break;
      case PLAN_DEFENSIVE:
	/* leave plan alone */
	break;
      default:
	break;
    }
}

/* Reevaluate the current plan and its tasks and change them if
   necessary. Works the same way as rethink_plan but with an emphasis
   on defending transports/cities in a more intelligent way. An
   important difference from rethink_plan is that this function (used
   to be) called from execute_plan just before plan execution. This
   means that the plan always is up-to-date at execution time. Perhaps
   the stuff in rethink_plan should also be moved here? */

static int
mplayer_update_plan(Unit *unit)
{
    int garrison = 0, reassign = FALSE;
    int x = unit->x, y = unit->y;
    Plan *plan = unit->plan;
    Side *side = unit->side;
    Unit *unit2, *unit3;

    /* First check if our unit is assigned to protecting another unit,
       and release it from that duty if it is no longer needed. This
       is very important or all our units will soon be soaked up in
       this kind of defense! */
    if (plan->maingoal && plan->maingoal->type == GOAL_UNIT_OCCUPIED) {
	unit2 = find_unit(plan->maingoal->args[0]);
	if (!in_play(unit2)) {
	    /* Our protege has been destroyed. */
	    reassign = TRUE;
	} else if (!occ_can_defend_transport(unit2->type, unit->type)) {
	    /* We can't defend our protege (should never happen). */
	    reassign = TRUE;
	} else if (enemy_side(side, unit2->side)) {
	    /* Our protege is held by the enemy. */
	    /* Try to capture it if we have a decent chance. */
	    if (real_capture_chance(unit, unit2) > 20) {
		net_set_capture_task(unit, unit2->x, unit2->y);
		/* Otherwise attack it if possible. */
	    } else if (attack_can_damage_or_capture(unit, unit2) 
		       || fire_can_damage_or_capture(unit, unit2)) {
		net_set_hit_unit_task(unit, unit2->x, unit2->y, 
				      unit2->type, unit2->side->id);
		/* Else find a new goal in life. */
	    } else {
		reassign = TRUE;			
	    }
	} else for_all_occupants(unit2, unit3) {
	    /* Check if our protege is adequately defended by OTHER
               occupants. */
	    if (in_play(unit3)
		&& unit3 != unit
		&& unit3->plan
		&& unit3->plan->maingoal
		&& unit3->plan->maingoal->type == GOAL_UNIT_OCCUPIED
		&& unit3->plan->maingoal->args[0] == unit->id
		/* Occs that cannot defend the transport should never
		   have been assigned to occupy it, but check
		   anyway. */
		&& occ_can_defend_transport(unit3->type, unit2->type)) {
		++garrison;
	    }
	    /* If so, release our unit from its duty. */
	    if (garrison >= u_ai_min_defenders(unit->type)) {
		reassign = TRUE;
		break;
	    }				
	}
	/* We need a new goal, but we keep our old plan type. */
	if (reassign) {
	    /* First blast the occupy-unit goal. */
	    net_set_unit_main_goal(side, unit, NULL);
	    if (plan->type == PLAN_DEFENSIVE)
	      assign_to_defense(side, unit);
	    else if (plan->type == PLAN_OFFENSIVE)
	      assign_to_offense(side, unit);
	    else if (plan->type == PLAN_EXPLORATORY)
	      assign_to_exploration(side, unit);
	    else if (plan->type == PLAN_COLONIZING)
	      assign_to_colonize(side, unit);
	    else if (plan->type == PLAN_IMPROVING)
	      assign_to_improve(side, unit);
	    else
	      /* Random, passive or no plan. */
	      net_clear_task_agenda(side, unit);
	}
    }

    /* Then check if our unit is needed to protect any other nearby
       city this turn. Reassign to defense and return immediately if
       this is the case. */

    /* First assign unit to protect its own city if undefended. */
    unit2 = unit->transport;
    if (unit2 != NULL
	/* A city is a transport that can build. */
	&& can_build(unit2)
	&& !defended_by_occupants(unit2)
	&& occ_can_defend_transport(unit->type, unit2->type)) {
	assign_to_defend_unit(unit, unit2);
	return TRUE;
    }
    /* Then protect any other nearby city that needs it. */    
    for_all_side_units(side, unit2) {
	if (can_carry_type(unit2, unit->type)
	    /* A city is a transport that can build. */
	    && can_build(unit2)
	    && distance(x, y, unit2->x, unit2->y) 
	    <= u_ai_tactical_range(unit->type)
	    && !defended_by_occupants(unit2)
	    && occ_can_defend_transport(unit->type, unit2->type)) {
	    assign_to_defend_unit(unit, unit2);
	    return TRUE;
	}
    }
    return TRUE; /* but what is the return flag supposed to mean? */
}

/* Deal with tactical emergencies such as enemy units that suddenly
   appear nearby. */

static int mplayer_go_after_victim(Unit *unit, int range);
static int mplayer_go_after_captive(Unit *unit, int range);
static int mplayer_fire_at_opportunity(Unit *unit);

static int
mplayer_handle_emergency(Unit *unit)
{
    int x = unit->x, y = unit->y, range;
    Plan *plan = unit->plan;

    if (flip_coin())
      return 0;

    /* Drop the current task and promptly attack any enemies within
       range.  This is very important or we will loose the tactical
       initiative! */

    /* Limit attack range to 1 if we are defending a cell or unit. */
    range = (plan->maingoal
	     && ((plan->maingoal->type == GOAL_UNIT_OCCUPIED
		  && unit->transport
		  && plan->maingoal->args[0] == unit->transport->id)
		 || (plan->maingoal->type == GOAL_CELL_OCCUPIED
		     && plan->maingoal->args[0] == x
		     && plan->maingoal->args[1] == y)) ?
	     1 : u_ai_tactical_range(unit->type)); 
    
    /* Fall-through: execute task if we already are in a fight. */
    if (plan->tasks
	&& (plan->tasks->type == TASK_HIT_UNIT
	    || plan->tasks->type == TASK_HIT_POSITION
	    || plan->tasks->type == TASK_CAPTURE
	    /* Or if we are chasing a victim. */
	    || (plan->tasks->type == TASK_MOVE_TO
		&& plan->tasks->next
		&& (plan->tasks->next->type == TASK_HIT_UNIT
		    || plan->tasks->next->type == TASK_HIT_POSITION
		    || plan->tasks->next->type == TASK_CAPTURE)))) {
	return 0;
    } else if (can_capture_directly(unit)
	       && mplayer_go_after_captive(unit, range)) {
	/* Else try to capture enemy within attack range. */
	return 0;
    } else if (can_fire(unit)
	       && mplayer_fire_at_opportunity(unit)) {
	/* Else fire at enemy within firing range. */
	return 0;
    } else if ((can_fire(unit) || can_attack(unit))
	       && mplayer_go_after_victim(unit, range)) {
	/* Else attack enemy within attack range (or move in to attack
           or fire). */
	return 0;
    }
    return 0;
}

extern int victimx, victimy, victimrating, victimutype, victimsidenum;

static int
mplayer_go_after_victim(Unit *unit, int range)
{
    int x, y, rslt;

    tmpunit = unit;
    DMprintf("%s (mplayer) seeking victim within %d; found ",
	     unit_desig(unit), range);
    victimrating = -9999;
    rslt = search_around(unit->x, unit->y, range, victim_here, &x, &y, 1);
    if (rslt) {
	DMprintf("s%d %s at %d,%d\n", victimsidenum, u_type_name(victimutype), x, y);
	/* Set up a task to go after the unit found. */
	/* (should be able to set capture task if better) */
	net_set_hit_unit_task(unit, x, y, victimutype, victimsidenum);
	if (unit->transport != NULL
	    && mobile(unit->transport->type)
	    && unit->transport->plan) {
	    net_set_move_to_task(unit->transport, x, y, 1);
	}
    } else if (victimrating > -9999) {
	DMprintf("s%d %s (rated %d) at %d,%d\n",
		 victimsidenum, u_type_name(victimutype), victimrating,
		 victimx, victimy);
	/* Set up a task to go after the unit found. */
	/* (should be able to set capture task if better) */
	net_set_hit_unit_task(unit, victimx, victimy, victimutype,
			      victimsidenum);
	if (unit->transport != NULL
	    && mobile(unit->transport->type)
	    && unit->transport->plan) {
	    net_set_move_to_task(unit->transport, victimx, victimy, 1);
	}
	/* We succeeded after all. */
	rslt = TRUE;
    } else {
	DMprintf("nothing\n");
    }
    return rslt;
}

/* The point of this new function is to limit the search for captives
   to a given range. */

static int
mplayer_go_after_captive(Unit *unit, int range)
{
    int x, y, rslt;

    tmpunit = unit;

    DMprintf("%s (mplayer) searching for useful capture within %d; found ",
	     unit_desig(unit), range);
    rslt = search_around(unit->x, unit->y, range, 
			 useful_captureable_here, &x, &y, 1);
    if (rslt) {
	DMprintf("one at %d,%d\n", x, y);
	/* Set up a task to go after the unit found. */
	net_set_capture_task(unit, x, y);
	if (unit->transport
	    && mobile(unit->transport->type)
	    && unit->transport->plan) {
	    net_set_move_to_task(unit->transport, x, y, 1);
	}
	return TRUE;
    } else {
	DMprintf("nothing\n");
	return FALSE;
    }
}

static int
mplayer_fire_at_opportunity(Unit *unit)
{
    int x, y, range, rslt;
    extern int targetx, targety, targetrating, targetutype, targetsidenum;

    tmpunit = unit;
    range = u_range(unit->type);
    targetrating = -9999;
    DMprintf("%s (mplayer) seeking target within %d; found ",
             unit_desig(unit), range);
    rslt = search_around(unit->x, unit->y, range, target_here, &x, &y, 1);
    if (rslt) {
	DMprintf("s%d %s at %d,%d\n", targetsidenum, u_type_name(targetutype), x, y);
	/* Set up a task to shoot at the unit found. */
	net_set_hit_unit_task(unit, x, y, targetutype, targetsidenum);
    } else if (targetrating > -9999) {
	DMprintf("s%d %s (rated %d) at %d,%d\n",
		 targetsidenum, u_type_name(targetutype), targetrating, x, y);
	/* Set up a task to shoot at the unit found. */
	net_set_hit_unit_task(unit, targetx, targety, targetutype, targetsidenum);
    } else {
	DMprintf("nothing\n");
    }
    return rslt;
}

static int
need_this_type_to_explore(Side *side, int u)
{
    int s, numcontacted = 0, numfound = 0;

    /* Non-mobile units aren't very good explorers. */
    /* (should someday account for possibility of perpetual passenger
       with good vision radius) */
    if (!mobile(u))
      return FALSE;
    for (s = 1; s <= numsides; ++s) {
    	if (s == side->id)
    	  continue;
	if (mplayer(side)->contacted[s])
	  ++numcontacted;
	if (mplayer(side)->homefound[s])
	  ++numfound;
    }
    if (numcontacted == 0) {
	return TRUE;
    } else if (numfound == 0) {
	return probability(50);
    } else if (numfound < numsides - 1) {
	return probability(10);
    } else {
	return FALSE;
    }
}

struct weightelt {
    int weight;
    long data;
};

static int
compare_weights(CONST void *w1, CONST void *w2)
{
    return (((struct weightelt *) w2)->weight - ((struct weightelt *) w1)->weight);
}

/* Set the unit up as an explorer and let it go. */

static void
assign_to_exploration(Side *side, Unit *unit)
{
    int numweights = 0, weight, i, dist;
    struct weightelt weights[MAXTHEATERS];
    Theater *theater;

    /* Unit's goal in life will be to see that the world is all known. */
    net_set_unit_plan_type(side, unit, PLAN_EXPLORATORY);
    set_unit_theater(unit, NULL);
    /* Find the theater most in need of exploration. */
    for_all_theaters(side, theater) {
    	if (theater->size > 0 && theater->unexplored > 0) {
	    /* Weight by percentage of theater that is unknown. */
	    weight = (100 * theater->unexplored) / theater->size;
	    /* Downrate theaters that are far away. */
	    dist = distance(unit->x, unit->y, theater->x, theater->y)
	      - isqrt(theater->size) / 2;
	    if (dist < 0)
	      dist = 0;
	    weight /= max(1, (4 * dist) / area.maxdim);
	    /* Uprate the home front by a lot - it will become
	       completely known quickly, and then effectively drop out
	       of the list. */
	    if (theater == mplayer(side)->homefront)
	      weight *= 4;
	    /* Flatten out 10% variations, thus giving equal weight to
	       theaters of similar value. */
	    weight = 10 * (weight / 10);
	    weights[numweights].weight = weight;
	    weights[numweights].data = theater->id;
	    ++numweights;
    	}
    }
    if (numweights > 0) {
    	qsort(weights, numweights, sizeof(struct weightelt), compare_weights);
    	/* Choose randomly among theaters of equal weight. */
    	for (i = 0; i < numweights; ++i)
	  if (weights[i].weight < weights[0].weight)
	    break;
    	theater = mplayer(side)->theatertable[weights[xrandom(i)].data];
    } else {
    	theater = NULL;
    }
    assign_explorer_to_theater(side, unit, theater);
}

static int
probably_explorable(side, x, y, u)
Side *side;
int x, y, u;
{
    int dir, x1, y1, tview, t;

    for_all_directions(dir) {
	if (interior_point_in_dir(x, y, dir, &x1, &y1)) {
	    tview = terrain_view(side, x, y);
	    if (tview == UNSEEN)
	      return TRUE;
	    t = vterrain(tview);
	    if (could_be_on(u, t) && could_live_on(u, t))
	      return TRUE;
	}
    }
    return FALSE;
}

static void
assign_explorer_to_theater(side, unit, theater)
Side *side;
Unit *unit;
Theater *theater;
{
    int sq, x, y, tries = 0;
    Goal *goal;

    /* Undo any existing assignment. */
    if (unit_theater(unit) != NULL) {
	--(unit_theater(unit)->numassigned[unit->type]);
	set_unit_theater(unit, NULL);
	/* (should release existing main goal?) */
    }    
    if (theater != NULL) {
	set_unit_theater(unit, theater);
	++(theater->numassigned[unit->type]);
	goal = create_goal(GOAL_VICINITY_KNOWN, side, TRUE);
	sq = isqrt(theater->size);
	while (++tries < 100) {
	    /* Select a random point within the theater. */
	    x = theater->xmin;  y = theater->ymin;
	    x += (xrandom(theater->xmax - theater->xmin)
		  + xrandom(theater->xmax - theater->xmin)) / 2;
	    y += (xrandom(theater->ymax - theater->ymin)
		  + xrandom(theater->ymax - theater->ymin)) / 2;
	    /* Filter out invalid locations. */
	    if (!inside_area(x, y))
	      continue;
	    if (theater_at(side, x, y) == theater
		&& terrain_view(side, x, y) == UNSEEN
		&& probably_explorable(side, x, y, unit->type))
	      break;
	}
	goal->args[0] = x;  goal->args[1] = y;
	goal->args[2] = goal->args[3] = sq / 2;
	net_set_unit_main_goal(side, unit, goal);
	DMprintf("%s now assigned to exploration in %s, around %d,%d\n",
		 unit_desig(unit), theater->name, x, y);
    }
}

static int
need_this_type_to_build_explorers(side, u)
Side *side;
int u;
{
    int s, u2;
	
    for (s = 1; s <= numsides; ++s) {
	if (mplayer(side)->contacted[s])
	  return FALSE;
	if (mplayer(side)->homefound[s])
	  return FALSE;
    }
    for_all_unit_types(u2) {
	if (mobile(u2)
	    && type_allowed_on_side(u2, side)
	    && has_advance_to_build(side, u2)
	    /* should also check u2 is a useful explorer */
	    && uu_acp_to_create(u, u2) > 0)
	  return TRUE;
    }
    return FALSE;
}

/* Explorer constructors concentrate on building types that are good for
   exploration. */

static void
assign_to_explorer_construction(side, unit)
Side *side;
Unit *unit;
{
    /* Unit's goal in life will be to help see that the world is all known. */
    net_set_unit_plan_type(side, unit, PLAN_EXPLORATORY);
    DMprintf("%s assigned to explorer construction\n", unit_desig(unit));
}

/* Unit's goal in life will be to colonize the world. */

static void
assign_to_colonize(Side *side, Unit *unit)
{
   	int	type[MAXUTYPES] = {0};
	int	numtypes = 0, u2;
	Goal	*goal;
	
	net_clear_task_agenda(side, unit);

	/* Pick a unit type to build. */
	for_all_unit_types(u2) {
		if (u_advanced(u2) 
		    && could_create(unit->type, u2)
		    && has_advance_to_build(unit->side, u2)
		    && type_allowed_on_side(u2, unit->side)) {
			type[numtypes] = u2;
			numtypes++;
		}
	}
	if (numtypes) {
		/* (Should make a more intelligent choice here) */
		/* (Should perhaps wait til execution time) */
	    	u2 = type[xrandom(numtypes)];
		goal = create_goal(GOAL_COLONIZE, side, TRUE);
		goal->args[0] = u2;
		net_set_unit_plan_type(side, unit, PLAN_COLONIZING);
		net_set_unit_main_goal(side, unit, goal);
		DMprintf("%s will colonize by building %s\n", 
		     		unit_desig(unit), u_type_name(u2));
	} else	 {
		/* We cannot use this unit to colonize. */
		net_set_unit_plan_type(side, unit, PLAN_PASSIVE);
		DMprintf("%s cannot colonize, got passive plan instead\n", 
				unit_desig(unit));
	}
}

static void
assign_to_improve(side, unit)
Side *side;
Unit *unit;
{
    /* Unit's goal in life will be to improve the world. */
    net_set_unit_plan_type(side, unit, PLAN_IMPROVING);
    net_clear_task_agenda(side, unit);
    net_set_unit_main_goal(side, unit, NULL);
    DMprintf("%s assigned to improve\n", unit_desig(unit));
}

static void
assign_to_offense(side, unit)
Side *side;
Unit *unit;
{
    int numweights = 0, weight;
    struct weightelt weights[MAXTHEATERS];
    Goal *goal;
    Theater *homefront, *theater;

    net_set_unit_plan_type(side, unit, PLAN_OFFENSIVE);
    net_clear_task_agenda(side, unit);
    /* If our home area is being threatened, assign the unit to it. */
    homefront = mplayer(side)->homefront;
    if (homefront != NULL && homefront->enemystrengthmin > 0) {
	set_unit_theater(unit, homefront);
	goal = create_goal(GOAL_VICINITY_HELD, side, TRUE);
	goal->args[0] = homefront->x;  goal->args[1] = homefront->y;
	goal->args[2] = goal->args[3] = isqrt(homefront->size);
	net_set_unit_main_goal(side, unit, goal);
	DMprintf("%s assigned to offensive in the home front\n",
		 unit_desig(unit));
	return;
    }
    /* If the theater the unit is currently in is being threatened,
       assign the unit to it. */
    /* (should just increase it weight in next calculation?) */
    theater = theater_at(side, unit->x, unit->y);
    if (theater != NULL && theater->enemystrengthmin > 0) {
	set_unit_theater(unit, theater);
	goal = create_goal(GOAL_VICINITY_HELD, side, TRUE);
	/* (should randomize?) */
	goal->args[0] = theater->x;  goal->args[1] = theater->y;
	goal->args[2] = (theater->xmax - theater->xmin) / 2;
	goal->args[3] = (theater->ymax - theater->ymin) / 2;
	net_set_unit_main_goal(side, unit, goal);
	DMprintf("%s assigned to offensive in the theater where it's at now\n",
		 unit_desig(unit));
	return;
    }
    for_all_theaters(side, theater) {
    	if (theater->enemystrengthmin > 0 || theater->unexplored > 0) {
	    /* (should weight by strength relative to own units already there) */
	    weight = theater->enemystrengthmax * 20;
	    /* Prefer not to send unit to farther-away theaters. */
	    if (distance(unit->x, unit->y, theater->x, theater->y) > area.maxdim / 2) {
		weight /= 2;
	    }
	    /* Prefer theaters with more unknown territory. */
	    weight += (10 * theater->unexplored) / max(1, theater->size);
	    weights[numweights].weight = weight;
	    weights[numweights].data = theater->id;
	    ++numweights;
    	}
    }
    if (numweights > 0) {
    	qsort(weights, numweights, sizeof(struct weightelt), compare_weights);
    	theater = mplayer(side)->theatertable[weights[0].data];
    } else {
    	theater = theater_at(side, unit->x, unit->y);
    }
    set_unit_theater(unit, theater);
    if (theater != NULL) {
	++(theater->numassigned[unit->type]);
	goal = create_goal(GOAL_VICINITY_HELD, side, TRUE);
	/* (should randomize?) */
	goal->args[0] = theater->x;  goal->args[1] = theater->y;
	goal->args[2] = (theater->xmax - theater->xmin) / 2;
	goal->args[3] = (theater->ymax - theater->ymin) / 2;
	net_set_unit_main_goal(side, unit, goal);
	DMprintf("%s now assigned to offensive in %s",
		 unit_desig(unit), theater->name);
	if (numweights > 1) {
	    DMprintf(" (weight %d; runnerup was %s, weight %d)",
		     weights[0].weight,
		     (mplayer(side)->theatertable[weights[1].data])->name,
		     weights[1].weight);
	}
	DMprintf("\n");
    } else {
	DMprintf("%s now assigned to offensive in no theater\n",
		 unit_desig(unit));
    }
}

static void
assign_to_offense_support(side, unit)
Side *side;
Unit *unit;
{
    net_set_unit_plan_type(side, unit, PLAN_OFFENSIVE);
    net_clear_task_agenda(side, unit);
}

static int
type_can_build_attackers(side, u)
Side *side;
int u;
{
    int u2;
	
    for_all_unit_types(u2) {
	if (mobile(u2)
	    && (type_can_attack(u2) || type_can_fire(u2))
	    && has_advance_to_build(side, u2)
	    && type_allowed_on_side(u2, side)
	    && uu_acp_to_create(u, u2) > 0)
	  return TRUE;
    }
    return FALSE;
}

#if 0
static int
type_can_build_colonizers(side, u)
Side *side;
int u;
{
    int u2;
	
    for_all_unit_types(u2) {
	if (mobile(u2)
	    && u_colonizer_worth(u2) > 0
	    && could_create(u, u2)
	    && has_advance_to_build(side, u2)
	    && type_allowed_on_side(u2, side))
		return TRUE;
    }
    return FALSE;
}
#endif

/* For the given side and unit and plan, calculate the right type of
   unit to build. */

static int
preferred_build_type(side, unit, plantype)
Side *side;
Unit *unit;
int plantype;
{
    int u = unit->type, u2, u3, t;
    int x, y, dir, x1, y1, blockedallaround, uview, est, rslt;
    int prefs[MAXUTYPES];
    int fringeterrain[MAXTTYPES], sumfringe, totfringe;
    int enemytypes[MAXUTYPES];
    int numtotransport[MAXUTYPES];
    Unit *unit2, *occ;
    Theater *theater;

    if (plantype == PLAN_EXPLORATORY) {
	/* Calculate the amount of each type of terrain at the edges
	   of the known world. */
	for_all_terrain_types(t)
	  fringeterrain[t] = 0;
	for_all_cells(x, y) {
	    if (terrain_view(side, x, y) != UNSEEN) {
		for_all_directions(dir) {
		    if (point_in_dir(x, y, dir, &x1, &y1)
			&& terrain_view(side, x1, y1) == UNSEEN) {
			++(fringeterrain[(int) terrain_at(x, y)]);
			break;
		    }
		}
	    }
	}
    } else {
	/* should use estimated strengths instead? */
    	for_all_unit_types(u2)
    	  enemytypes[u2] = 0;
    	for_all_interior_cells(x, y) {
	    if (can_see_actual_units(side, x, y)) {
		/* Use new recursive macro instead that also sees occs within occs. */
		for_all_stack_with_occs(x, y, unit2) {
		    if (!trusted_side(side, unit2->side))
		      	    ++enemytypes[unit2->type];
		}
	    } else {
		uview = unit_view(side, x, y);
		if (uview != EMPTY) {
		    if (!trusted_side(side, side_n(vside(uview))))
		      ++enemytypes[vtype(uview)];
		}
	    }
    	}
    }
    /* Calculate a basic preference for each possible type. */
    for_all_unit_types(u2) {
	prefs[u2] = 0;
	est = est_completion_time(unit, u2);
	if (could_create(u, u2)
	    /* tmp hack until mplayer can do develop */
	    && (u_tech_to_build(u2) > 0 ? side->tech[u2] >= u_tech_to_build(u2) : TRUE)
	    && est >= 0
	    /* No longer included in type_allowed_on_side. */
	    && has_advance_to_build(side, u2)
	    && type_allowed_on_side(u2, side)) {
	    if (0 /* any demand in this unit's own theater */) {
	    } else if (need_more_transportation(side)) {
    		for_all_unit_types(u3) {
		    numtotransport[u3] = 0;
    		}
	    	for_all_theaters(side, theater) {
		    for_all_unit_types(u3) {
			numtotransport[u3] += theater->numtotransport[u3];
		    }
	    	}
    		for_all_unit_types(u3) {
		    if (numtotransport[u3] > 0
			&& mobile(u2)
			&& could_carry(u2, u3)) {
			prefs[u2] += numtotransport[u3];
		    }
	    	}
	    } else {
		/* Prefer units by overall suitability for general plan. */
		if (plantype == PLAN_EXPLORATORY) {
		    /* Weight unit types by suitability for exploration around
		       the edges of the known area. */
		    sumfringe = totfringe = 0;
		    for_all_terrain_types(t) {
			totfringe += fringeterrain[t];
			if (!terrain_always_impassable(u2, t))
			  sumfringe += fringeterrain[t];
		    }
		    if (totfringe < 1)
		      sumfringe = totfringe = 1;
		    /* Scale - so 5% diffs in amt of crossable terrain
		       don't affect result. */
		    prefs[u2] = (20 * sumfringe) / totfringe;
		    /* Prefer types that are quicker to build. */
		    prefs[u2] /= max(1, est / 8);
		} else if (plantype == PLAN_COLONIZING) {
		    /* First eliminate non-colonizing units. */
		    if (u_colonizer_worth(u2) <= 0) {
			prefs[u2] = 0;
		    	/* Weight unit type by effectiveness against
                           known enemies. */
		    } else {
			for_all_unit_types(u3) {
			    if (enemytypes[u3] > 0) {
				if (uu_zz_bhw(u2, u3) > 0) {
				    prefs[u2] +=
				      ((uu_zz_bhw(u2, u3) * 100) / bhw_max) * enemytypes[u3];
				}
				if (uu_zz_bcw(u2, u3) > 0) {
				    prefs[u2] += uu_zz_bcw(u2, u3) * enemytypes[u3];
				}
			    }
			}
			/* Prefer types that are quicker to build. */
			prefs[u2] /= max(1, est / 8);
		    }			
		} else if (plantype == PLAN_IMPROVING) {
		    /* Eliminate all mobile units. */
		    if (mobile(u2))
		      prefs[u2] = 0;
		} else {
		    /* Weight unit type by effectiveness against known
                       enemies. */
		    for_all_unit_types(u3) {
			if (enemytypes[u3] > 0) {
			    if (uu_zz_bhw(u2, u3) > 0) {
				prefs[u2] +=
				  ((uu_zz_bhw(u2, u3) * 100) / bhw_max) * enemytypes[u3];
			    }
			    if (uu_zz_bcw(u2, u3) > 0) {
				prefs[u2] += uu_zz_bcw(u2, u3) * enemytypes[u3];
			    }
			}
		    }
		    /* Prefer types that are quicker to build. */
		    prefs[u2] /= max(1, est / 8);
		}
		if (prefs[u2] < 1)
		  prefs[u2] = 1;
	    }
	}
    }
    /* Units that can't even get out of the builder get their
       preference cut.  This helps prevent the construction of large
       ships in Denver. */
    /* (should allow if units would have some other way to leave) */
    if (1 /* plantype == PLAN_EXPLORATORY */) {
	for_all_unit_types(u2) {
	    if (prefs[u2] > 0) {
		blockedallaround = TRUE;
		for_all_directions(dir) {
		    point_in_dir(unit->x, unit->y, dir, &x1, &y1);
		    if (!terrain_always_impassable(u2, terrain_at(x1, y1))) {
		    	blockedallaround = FALSE;
		    	break;
		    }
#if 0  /* should replace this with a more useful test */
		    if (unit_at(x1, y1) != NULL) {
		    	blockedallaround = FALSE;
		    	break;
		    }
#endif
		}
		if (blockedallaround)
		  prefs[u2] = 0;
	    }
	}
    }
    DMprintf("%s preferred build type is ", unit_desig(unit));
    /* Look for an existing incomplete occupant and prefer to build its type,
       if it is in the choices in the typelist. */
    for_all_occupants(unit, occ) {
	if (in_play(occ) && !completed(occ)) {
	    if (prefs[occ->type] > 0 && flip_coin()) {
		rslt = occ->type;
		DMprintf("%s (incomplete occupant)\n", u_type_name(rslt));
		return rslt;
	    }
	}
    }
    for_all_unit_types(u2)
      if (prefs[u2] < 0)
        prefs[u2] = 0;
    rslt = select_by_weight(prefs, numutypes);
    if (!is_unit_type(rslt))
      rslt = NONUTYPE;
    if (DebugM) {
	if (is_unit_type(rslt)) {
	    DMprintf("%s (choices were", u_type_name(rslt));
	    for_all_unit_types(u2) {
		if (prefs[u2] > 0)
		  DMprintf(" %s,%d", utype_name_n(u2, 1), prefs[u2]);
	    }
	    DMprintf(")");
	} else {
	    DMprintf("nothing (no choices)");
	}
	DMprintf("\n");
    }
    return rslt;
}

static int
need_more_transportation(side)
Side *side;
{
    int u3, u2, anytransport;
    Theater *theater;

    for_all_theaters(side, theater) {
	for_all_unit_types(u3) {
	    if (theater->numtotransport[u3] > 0) {
		anytransport = FALSE;
		for_all_unit_types(u2) {
		    if (theater->numassigned[u2] > 0
			&& mobile(u2)
			&& could_carry(u2, u3))
		      anytransport = TRUE;
		}
		if (!anytransport)
		  return TRUE;
	    }
	}
    }
    return FALSE;
}

/* True if unit has enough occupants assigned to its defense. */

static int
defended_by_occupants(Unit *unit)
{
    Unit *unit2;
    int defenders = 0;

    for_all_occupants(unit, unit2) {
	if (in_play(unit2)
	    && unit2->plan
	    && unit2->plan->maingoal
	    && unit2->plan->maingoal->type == GOAL_UNIT_OCCUPIED
	    && unit2->plan->maingoal->args[0] == unit->id
	    /* Occs that cannot defend the transport should not be
	       assigned to occupy it, but check anyway for ability to
	       protect. */
	    && occ_can_defend_transport(unit2->type, unit->type)) {
	    ++defenders;
	}
    }
    if (defenders < u_ai_min_defenders(unit->type)) 
      return FALSE;
    return TRUE;
}

static void
assign_to_defense(Side *side, Unit *unit)
{
    Theater *theater;
    Unit *unit2;
    Goal *goal;

    net_set_unit_plan_type(side, unit, PLAN_DEFENSIVE);

    /* Assign to defend current transport if it needs defenders. */
    unit2 = unit->transport;
    if (unit2 
	&& !defended_by_occupants(unit2)
	&& occ_can_defend_transport(unit->type, unit2->type)) {
	assign_to_defend_unit(unit, unit2);
	return;
    }
    /* Else assign to defend any other nearby transport that needs it. */    
    for_all_side_units(side, unit2) {
	if (can_carry_type(unit2, unit->type)
	    && (distance(unit->x, unit->y, unit2->x, unit2->y) 
		<= u_ai_tactical_range(unit->type))
	    && !defended_by_occupants(unit2)
	    && occ_can_defend_transport(unit->type, unit2->type)) {
	    assign_to_defend_unit(unit, unit2);
	    return;
	}
    }    	    		
    /* Else assign to defend the current theater. */
    theater = theater_at(side, unit->x, unit->y);
    set_unit_theater(unit, theater);
    if (theater != NULL) {
	++(theater->numassigned[unit->type]);
	goal = create_goal(GOAL_VICINITY_HELD, side, TRUE);
	goal->args[0] = theater->x;  goal->args[1] = theater->y;
	goal->args[2] = (theater->xmax - theater->xmin) / 2;
	goal->args[3] = (theater->ymax - theater->ymin) / 2;
	net_set_unit_main_goal(side, unit, goal);
	DMprintf("%s now assigned to defense in %s\n",
		 unit_desig(unit), theater->name);
    } else {
	DMprintf("%s now assigned to defense in no theater\n",
		 unit_desig(unit));
    }
}

/* Assign unit to defend unit2. Does NOT change the plan type. */

void
assign_to_defend_unit(Unit *unit, Unit *unit2)
{
    Goal *goal;

    /* Clear the task agenda. */
    net_clear_task_agenda(unit->side, unit);

    /* Set main goal to occupy unit2. */
    goal = create_goal(GOAL_UNIT_OCCUPIED, unit->side, TRUE);
    goal->args[0] = unit2->id;
    net_set_unit_main_goal(unit->side, unit, goal);
    DMprintf("%s assigned to occupy %s\n",
	     unit_desig(unit), unit_desig(unit2));
}

#if 0
/* Assign unit to defend cell at (x, y). Does NOT change the plan type. */

void
assign_to_defend_cell(Unit *unit, int x, int y)
{
    	Goal 	*goal;

	/* Clear the task aganda. */
	net_clear_task_agenda(unit->side, unit);

	/* Set main goal to occupy (x, y). */
	goal = create_goal(GOAL_CELL_OCCUPIED, unit->side, TRUE);
	goal->args[0] = x;
	goal->args[1] = y;
	net_set_unit_main_goal(unit->side, unit, goal);
	DMprintf("%s assigned to occupy cell at (%d, %d)\n",
	 		unit_desig(unit), x, y);
}
#endif

#if 0
/* Assign unit to defend the vicinity of (x, y). Does NOT change the
   plan type. */

void
assign_to_defend_vicinity(Unit *unit, int x, int y, int w, int h)
{
    	Goal 	*goal;

	/* Clear the task aganda. */
	net_clear_task_agenda(unit->side, unit);

	/* Set main goal to hold the vicinity of (x, y). */
	goal = create_goal(GOAL_VICINITY_HELD, unit->side, TRUE);
	goal->args[0] = x;  
	goal->args[1] = y;
	goal->args[2] = w;
	goal->args[3] = h;
	net_set_unit_main_goal(unit->side, unit, goal);
	DMprintf("%s now assigned to defense the vicinity of (%d, %d)\n",
		 unit_desig(unit), x, y);
}
#endif

#if 0
/* Assign unit to defend theater. Does NOT change the plan type. */

static void assign_to_defend_theater(Unit *unit, Theater *theater);

void
assign_to_defend_theater(Unit *unit, Theater *theater)
{
    	Goal 	*goal;

	/* Clear the task aganda. */
	net_clear_task_agenda(unit->side, unit);

	/* Set main goal to hold the vicinity of (x, y). */
	goal = create_goal(GOAL_VICINITY_HELD, unit->side, TRUE);
	goal->args[0] = theater->x;  
	goal->args[1] = theater->y;
	goal->args[2] = (theater->xmax - theater->xmin) / 2;
	goal->args[3] = (theater->ymax - theater->ymin) / 2;
	net_set_unit_main_goal(unit->side, unit, goal);
	DMprintf("%s now assigned to defense in %s\n",
		 unit_desig(unit), theater->name);
}
#endif

static void
assign_to_defense_support(side, unit)
Side *side;
Unit *unit;
{
    net_set_unit_plan_type(side, unit, PLAN_DEFENSIVE);
    net_clear_task_agenda(side, unit);
}

static int
can_develop_on(u, u2)
int u;
int u2;
{
    int acp_res = uu_acp_to_develop(u, u2);

    if (acp_res < 1 || acp_res > u_acp(u))
      return 0;

    return 1;
}

/* code specific to the "time" game */
static int 
needs_develop (side, u)
Side *side;
int u;
{
    int u2, i;

    if (game_class != gc_time)
      return 0;

    if (side->tech[u] >= u_tech_to_build(u) ||
	u_tech_max(u)  > u_tech_to_build(u))
      return 0;
    
    i = 0;
    for_all_unit_types(u2) {
	i += uu_acp_to_create(u, u2) > 0;
    }
    if (i < 2)
      return 0;

    return 1;
}

static int
assign_to_develop_on(side, unit, u2)
Side *side;
Unit *unit;
int u2;
{
    int lev = u_tech_to_build(u2);

    if (!can_develop_on(unit->type, u2)) {
	DMprintf("%s cannot develop on %s!\n",
	     unit_desig(unit), u_type_name(u2));
	return 0;
    }

    DMprintf("%s will develop for %s on %s (to level %d)\n",
	     unit_desig(unit), side_desig(side), u_type_name(u2), lev);

    net_set_unit_plan_type(side, unit, PLAN_PASSIVE);
    net_push_develop_task(unit, u2, lev);
    return 1;
}

static int
mplayer_adjust_plan(side, unit)
Side *side;
Unit *unit;
{
    int u3;
    Task *task;

    /* Plans for advanced units are (for now) handled in run.c instead. */
    if (u_advanced(unit->type))
      return FALSE;
    if ((unit->plan->type == PLAN_OFFENSIVE
	 || unit->plan->type == PLAN_COLONIZING
	 || unit->plan->type == PLAN_IMPROVING
	 || unit->plan->type == PLAN_EXPLORATORY)
	&& !mobile(unit->type)
	&& unit->plan->aicontrol
	&& !unit->plan->asleep
	&& unit->plan->tasks == NULL
	) {
	    u3 = preferred_build_type(side, unit, unit->plan->type);
	    if (is_unit_type(u3)) {
		task = unit->plan->tasks;
		if (task == NULL || task->type != TASK_BUILD) {
		    DMprintf("%s directed to build %s\n",
			     unit_desig(unit), u_type_name(u3));
		    net_set_build_task(unit, u3, 2);
		} else {
		    DMprintf("%s already building, leaving alone\n",
			     unit_desig(unit));
		}
		/* Only do one at a time, wait for next go-around for
                   next unit. */
		return FALSE;
	    }
    }
    if (unit->plan->waitingfortasks
	&& unit->plan->aicontrol
	) {
	net_force_replan(side, unit, FALSE);
    }
    if (!unit->plan->reserve
    	&& g_units_may_go_into_reserve()
	&& unit->plan->execs_this_turn > 10 * max(1, u_acp(unit->type))) {
	net_set_unit_reserve(side, unit, TRUE, FALSE);
    }
    /* Look at more units. */
    return TRUE;
}

#if 0
/* This is called when an exploring unit gets confused about what to do. */

static int
mplayer_guide_explorer(side, unit)
Side *side;
Unit *unit;
{
    if (probability(10) && build_base_for_self(side, unit))
      return TRUE;
    if (probability(10) && build_base_for_others(side, unit))
      return TRUE;
    return FALSE;
}

/* Decide for the unit whether it should build a base for its own benefit. */

static int
build_base_for_self(side, unit)
Side *side;
Unit *unit;
{
    int u = unit->type, u2, cando = FALSE;

    for_all_unit_types(u2) {
	if (uu_acp_to_create(u, u2) > 0
	    && type_allowed_on_side(u2, side)
	    && has_advance_to_build(side, u2)
	    && (uu_creation_cp(u, u2) >= u_cp(u2)
	        || uu_acp_to_build(u, u2) > 0)
	    && u_is_base(u2)
	    /* (should check if any advantage to building) */
	    ) {
	    cando = TRUE;
	    break;
	}
    }
    if (cando) {
	DMprintf("%s building %s as a base for itself\n",
		 unit_desig(unit), u_type_name(u2));
	net_set_build_task(unit, u2, 1);
	return TRUE;
    }
    return FALSE;
}

/* Decide for the unit whether it should build a base to help other units. */

static int
build_base_for_others(side, unit)
Side *side;
Unit *unit;
{
    int u = unit->type, u2, cando = FALSE;

    for_all_unit_types(u2) {
	if (uu_acp_to_create(u, u2) > 0
	    && (uu_creation_cp(u, u2) >= u_cp(u2)
	    && type_allowed_on_side(u2, side)
	    && has_advance_to_build(side, u2)
	        || uu_acp_to_build(u, u2) > 0)
	    && u_is_base(u2)
	    /* (should check if any advantage to building) */
	    ) {
	    cando = TRUE;
	    break;
	}
    }
    if (cando) {
	DMprintf("%s building %s as a base for others\n",
		 unit_desig(unit), u_type_name(u2));
	net_set_build_task(unit, u2, 1);
	return TRUE;
    }
    return FALSE;
}
#endif

static int
build_depot_for_self(Side *side, Unit *unit)
{
    int u = unit->type, u2, cando = FALSE;

    for_all_unit_types(u2) {
	if (uu_acp_to_create(u, u2) > 0
	    && ((uu_creation_cp(u, u2) >= u_cp(u2)
		&& type_allowed_on_side(u2, side)
		&& has_advance_to_build(side, u2))
		|| uu_acp_to_build(u, u2) > 0)
	    /* (should check if any advantage to building) */
	    ) {
	   cando = TRUE;
	   break;
	}
    }
    if (cando) {
	DMprintf("%s building %s as a depot for itself\n",
		 unit_desig(unit), u_type_name(u2));
	net_set_build_task(unit, u2, 1);
	return TRUE;
    }
    return FALSE;
}

static void
mplayer_react_to_action_result(side, unit, rslt)
Side *side;
Unit *unit;
int rslt;
{
}

/* This is a hook that runs after each task is executed. */

static void
mplayer_react_to_task_result(side, unit, task, rslt)
Side *side;
Unit *unit;
Task *task;
TaskOutcome rslt;
{
    int dx, dy, x1, y1, fact;
    Unit *occ;
    Theater *theater;

    /* React to an apparent blockage. */
    if (rslt == TASK_FAILED
	&& task != NULL
	&& task->type == TASK_MOVE_TO
	&& task->retrynum > 2) {
	if (desired_direction_impassable(unit, task->args[0], task->args[1])) {
	    if (could_be_ferried(unit, task->args[0], task->args[1])) {
		if (unit->plan->type == PLAN_EXPLORATORY && flip_coin()) {
		    DMprintf("%s blocked while exploring, \n", unit_desig(unit));
	      	    if (flip_coin()) {
			DMprintf("changing theaters\n");
			change_to_adjacent_theater(side, unit);
		    } else {
			DMprintf("changing goal within theater\n");
			assign_explorer_to_theater(side, unit, unit_theater(unit));
		    }
		    return;
		} else if (flip_coin()) {
		    DMprintf("%s blocked, will wait for transport\n",
			     unit_desig(unit));
		    theater = theater_at(side, unit->x, unit->y);
		    if (theater != NULL) {
			++(theater->numtotransport[unit->type]);
		    }
		    net_set_unit_reserve(side, unit, TRUE, FALSE);
		    net_set_unit_waiting_for_transport(side, unit, TRUE);
		    return;
		}
	    } else {
	    	if (unit->occupant) {
		    DMprintf("%s blocked while transporting, will sit briefly\n",
			     unit_desig(unit));
		    net_set_unit_reserve(side, unit, TRUE, FALSE);
		    for_all_occupants(unit, occ) {
		    	net_wake_unit(side, occ, FALSE);
		    }
		    return;
	    	}
		/* Another option is to transfer to another theater.
		   This is especially useful when exploring. */
		if (unit->plan->type == PLAN_EXPLORATORY && flip_coin()) {
		    DMprintf("%s blocked while exploring, changing theaters\n",
			     unit_desig(unit));
		    change_to_adjacent_theater(side, unit);
		    return;
		}
	    }
	    /* Try moving sideways. */
	    if (probability(80)) {
		dx = task->args[0] - unit->x;  dy = task->args[1] - unit->y;
		fact = (flip_coin() ? 50 : -50);
		x1 = unit->x - ((fact * dy) / 100);  y1 = unit->y + ((fact * dx) / 100);
		if (inside_area(x1, y1))
		  net_push_move_to_task(unit, x1, y1, 1);
	    }
	    return;
	} else if (blocked_by_enemy(unit, task->args[0], task->args[1], TRUE)) {
	    /* (should decide if allowable risk to passengers) */
	    DMprintf("%s blocked by enemy\n", unit_desig(unit));
	    if (!attack_blockage(side, unit, task->args[0], task->args[1], TRUE)) {
		if (blocked_by_enemy(unit, task->args[0], task->args[1], FALSE)) {
		    attack_blockage(side, unit, task->args[0], task->args[1], FALSE);
		} else {
		    /* (should move sideways?) */
		}
	    }
	} else {
	    /* what to do about other failures? */
	}
	return;
    }
    /* React to inability to resupply by trying to build a base. */
    if (rslt == TASK_FAILED
	&& task != NULL
	&& task->type == TASK_RESUPPLY
	&& task->retrynum > 2) {
    	net_set_unit_reserve(side, unit, FALSE, FALSE);
    	build_depot_for_self(side, unit);
    }
}

static void
change_to_adjacent_theater(side, unit)
Side *side;
Unit *unit;
{
    int dir;
    Theater *theater, *newtheater = NULL;

    theater = unit_theater(unit);
    if (theater != NULL) {
   	for_all_directions(dir) {
   	    if (theater == mplayer(side)->perimeters[dir]) {
		if (probability(20) && mplayer(side)->midranges[dir] != NULL)
		  newtheater = mplayer(side)->midranges[dir];
		else
   	    	  newtheater = mplayer(side)->perimeters[flip_coin() ? left_dir(dir) : right_dir(dir)];
   	    	break;
   	    }
   	    if (theater == mplayer(side)->midranges[dir]) {
		if (probability(20) && mplayer(side)->perimeters[dir] != NULL)
		  newtheater = mplayer(side)->perimeters[dir];
		else if (probability(20) && mplayer(side)->remotes[dir] != NULL)
		  newtheater = mplayer(side)->remotes[dir];
		else
   	    	  newtheater = mplayer(side)->midranges[flip_coin() ? left_dir(dir) : right_dir(dir)];
   	    	break;
   	    }
   	    if (theater == mplayer(side)->remotes[dir]) {
 		if (probability(20) && mplayer(side)->midranges[dir] != NULL)
		  newtheater = mplayer(side)->midranges[dir];
		else
   	    	  newtheater = mplayer(side)->remotes[flip_coin() ? left_dir(dir) : right_dir(dir)];
   	    	break;
   	    }
   	}
   	/* (should add grid case also?) */
   	if (newtheater != NULL) {
	    assign_explorer_to_theater(side, unit, newtheater);
   	}
    }
}

/* (should account for impassability because of borders, etc) */

static int
desired_direction_impassable(unit, x, y)
Unit *unit;
int x, y;
{
    int dirs[NUMDIRS], numdirs, i, x1, y1, t, numbaddirs = 0;

    numdirs = choose_move_dirs(unit, x, y, TRUE, NULL, NULL, dirs);
    for (i = 0; i < numdirs; ++i) {
	point_in_dir(unit->x, unit->y, dirs[i], &x1, &y1);
	t = terrain_at(x1, y1);
	if (terrain_always_impassable(unit->type, t))
	  ++numbaddirs;
    }
    return (numbaddirs == numdirs);
}

static int
could_be_ferried(unit, x, y)
Unit *unit;
int x, y;
{
    int dirs[NUMDIRS], numdirs, i, x1, y1, t, u2;

    if (!carryable(unit->type))
      return FALSE;
    numdirs = choose_move_dirs(unit, x, y, FALSE, NULL, NULL, dirs);
    for (i = 0; i < numdirs; ++i) {
	point_in_dir(unit->x, unit->y, dirs[i], &x1, &y1);
	t = terrain_at(x1, y1);
	/* See if there is a type that can carry us through via this direction. */
	for_all_unit_types(u2) {
	    if (could_carry(u2, unit->type)
	        && mobile(u2)
	        && !terrain_always_impassable(u2, t)
	        /* should also have "and this type is avail to us" */
	        ) {
	        return TRUE;
	    }
	}
    }
    return FALSE;
}

static int
blocked_by_enemy(Unit *unit, int x, int y, int shortest)
{
    int dirs[NUMDIRS], numdirs, i, x1, y1, numbaddirs = 0;
    Unit *unit2;

    numdirs = choose_move_dirs(unit, x, y, shortest, plausible_move_dir, NULL, dirs);
    /* No plausible dirs anyhow, so presence of enemy irrelevant. */
    if (numdirs == 0)
      return FALSE;
    for (i = 0; i < numdirs; ++i) {
	point_in_dir(unit->x, unit->y, dirs[i], &x1, &y1);
	unit2 = unit_at(x1, y1);
	if (in_play(unit2) && !trusted_side(unit->side, unit2->side))
	  ++numbaddirs;
    }
    return (numbaddirs == numdirs);
}

static int
attack_blockage(Side *side, Unit *unit, int x, int y, int shortest)
{
    int dirs[NUMDIRS], numdirs, i, x1, y1;
    Unit *unit2;

    numdirs = choose_move_dirs(unit, x, y, shortest, plausible_move_dir, NULL, dirs);
    for (i = 0; i < numdirs; ++i) {
	point_in_dir(unit->x, unit->y, dirs[i], &x1, &y1);
	unit2 = unit_at(x1, y1);
	if (in_play(unit2)
	    && !trusted_side(unit->side, unit2->side)
	    && uu_zz_bhw(unit->type, unit2->type) > 0
	    ) {
	    net_push_hit_unit_task(unit, x1, y1, unit2->type, side_number(unit2->side));
	    return TRUE;
	}
    }
    return FALSE;
}

static void
mplayer_react_to_new_side(side, side2)
Side *side, *side2;
{
    /* (Assumes we call this right after adding each new side) */
    int oldnumsides = numsides - 1;
    int *newpeople, s;
    Theater *theater;

    for_all_theaters(side, theater) {
	/* Grow any people count arrays if present. */
	if (theater->people != NULL) {
	    newpeople = (int *) xmalloc ((numsides + 1) * sizeof(int));
	    for (s = 0; s <= oldnumsides; ++s)
	      newpeople[s] = theater->people[s];
	    free(theater->people);
	    theater->people = newpeople;
	}
    }
    anewside = side2;
    estimate_strengths(side);
    anewside = NULL;
}

/* At the end of a turn, re-evaluate the plans of some units in case
   the situation changed. */

static void
mplayer_finish_movement(side)
Side *side;
{
    int u, scan;
    Unit *unit;
    Theater *theater;

    scan = FALSE;
    for_all_theaters(side, theater) {
	for_all_unit_types(u) {
	    if (theater->numtotransport[u] > 0) {
		scan = TRUE;
		break;
	    }
	}
	if (scan)
	  break;
    }
    if (scan) {
	/* Find a unit needing transport. */
	for_all_side_units(side, unit) {
	    if (is_active(unit)
		&& unit->plan
		&& unit->plan->aicontrol
		&& unit->plan->waitingfortransport) {
		search_for_available_transport(unit, 0);
		net_set_unit_waiting_for_transport(side, unit, FALSE);
	    }
	}
    }
    for_all_side_units(side, unit) {
	if (is_active(unit)
	    && unit->plan
	    && unit->plan->aicontrol) {
	    rethink_plan(unit);
	}
    }
}

static Unit *
search_for_available_transport(unit, purpose)
Unit *unit;
int purpose;
{
    int dist, closestdist = area.maxdim;
    Unit *transport, *closesttransport = NULL;
    Theater *theater = unit_theater(unit);

    /* (more efficient to search adjacent cells first?) */
    for_all_side_units(unit->side, transport) {
	if (is_active(transport)
	    && mobile(transport->type)
	    && could_carry(transport->type, unit->type)
	    && transport->occupant == NULL /* (should be "has room") */
	    && transport->act != NULL /* not quite correct, but to fix bug */
	    && (purpose == 1 ? accelerator(transport->type, unit->type) : TRUE)
	    ) {
	    /* Don't grab at units being moved manually. */
	    if (transport->plan
		&& !transport->plan->aicontrol)
	      continue;
	    /* Maybe this one is already coming to get somebody. */
	    if (transport->plan
		&& transport->plan->tasks != NULL
		&& transport->plan->tasks->type == TASK_PICKUP) {
		if (transport->plan->tasks->args[0] == unit->id)
		  return transport;
		/* Picking up somebody else - don't hijack. */
		continue;
	    }
	    if (transport->plan
		&& transport->plan->tasks != NULL
		&& transport->plan->tasks->type == TASK_MOVE_TO
		&& transport->plan->tasks->next != NULL
		&& transport->plan->tasks->next->type == TASK_PICKUP) {
		if (transport->plan->tasks->next->args[0] == unit->id)
		  return transport;
		/* Picking up somebody else - don't hijack. */
		continue;
	    }
	    dist = distance(unit->x, unit->y, transport->x, transport->y);
	    if (dist < closestdist || (dist == closestdist && flip_coin())) {
		closesttransport = transport;
		closestdist = dist;
	    }
	    /* If transport already adjacent, no need to keep looking. */
	    if (closestdist <= 1)
	      break;
	}
    }
    if (closesttransport != NULL && closesttransport->plan != NULL) {
	net_clear_task_agenda(unit->side, unit);
	/* (could inherit unit's goal, but not needed) */
	net_set_unit_main_goal(unit->side, closesttransport, NULL);
	net_push_pickup_task(closesttransport, unit);
	net_push_move_to_task(closesttransport, unit->x, unit->y, 1);
	net_push_occupy_task(unit, closesttransport);
	/* No longer count this unit as needing transport. */
	if (theater != NULL) {
	    --(theater->numtotransport[unit->type]);
	    set_unit_theater(closesttransport, theater);
	}
	DMprintf("%s will be picked up by closest transport %s\n",
	         unit_desig(unit), unit_desig(closesttransport));
	return closesttransport;
    }
    return NULL;
}

/* For units with plans and that are under AI control, consider changing the
   current plan/tasks. */

static void
rethink_plan(unit)
Unit *unit;
{
    int dist, x1, y1;
    Task *toptask = unit->plan->tasks, *nexttask = NULL;
    Plan *plan = unit->plan;
    Unit *transport;

    if (toptask)
      nexttask = toptask->next;
    /* If we have a long ways to go, see if there is a transport available that
       can get us there faster.  */
    if (toptask != NULL
	&& (toptask->type == TASK_HIT_UNIT
	    || (toptask->type == TASK_MOVE_TO
		&& nexttask != NULL
		&& nexttask->type == TASK_HIT_UNIT))
        && !plan->reserve
        && !plan->asleep
        && !plan->waitingfortransport
        && (unit->transport == NULL || !mobile(unit->transport->type))
        && ((dist = distance(unit->x, unit->y,
			     toptask->args[0], toptask->args[1]))
            >= 4 * u_acp(unit->type))
        && accelerable(unit->type)
        ) {
        DMprintf("%s looking for transport to accelerate with;\n", unit_desig(unit));
        transport = search_for_available_transport(unit, 1);
        if (transport != NULL) {
	    /* net_push_sentry_task(unit, max(1, dist / max(1, u_acp(transport->type)))); */
	if (g_units_may_go_into_reserve())
	    net_set_unit_reserve(unit->side, unit, TRUE, FALSE);
	    net_set_unit_waiting_for_transport(unit->side, unit, FALSE);
        } else {
	    DMprintf("  found nothing\n");
        }
    }
    if (unit->plan->type == PLAN_OFFENSIVE
        && toptask != NULL
        && toptask->type == TASK_MOVE_TO
        && distance(unit->x, unit->y, toptask->args[0], toptask->args[1])
		>= min(2, u_acp(unit->type))
        && enemy_close_by(unit->side, unit, 1 /* 2 would be better? */, &x1, &y1)
        ) {
	net_push_hit_unit_task(unit, x1, y1, NONUTYPE, -1);
	DMprintf("%s sees enemy close by, will attack it\n", unit_desig(unit));
    }
    /* (should also notice fire opportunities) */
    /* If we see somebody that could be captured and help us explore, set up
       to produce capturers. */
    if (!mobile(unit->type)
	&& (unit->plan->type == PLAN_EXPLORATORY || unit->plan->type == PLAN_OFFENSIVE)
	) {
	int range = 4, rslt, x, y;

	DMprintf("%s searching for useful capture within %d in order to choose build; found ",
	     	 unit_desig(unit), range);
	tmpunit = unit;
	rslt = search_around(unit->x, unit->y, range, useful_captureable_here,
			     &x, &y, 1);
	if (rslt && is_unit_type(tmputype)) {
	    DMprintf("%s at %d,%d", u_type_name(tmputype), x, y);
	    if (toptask != NULL
		&& toptask->type == TASK_BUILD
		&& uu_capture(toptask->args[0], tmputype)
		) {
		/* Already doing the right thing. */
		DMprintf(" - already building %s", u_type_name(toptask->args[0]));
	    } else {
		/* (should find best type that can capture quickly,
		    schedule to build it) */
		DMprintf(" - duhhh, what now?");
	    }
	} else {
	    DMprintf("nothing");
	}
	DMprintf("\n");
    }
}

/* Detect an enemy unit in our own or an adjacent cell. */

/* static int victimx, victimy, victimrating; */

static int
enemy_close_by(side, unit, dist, xp, yp)
Side *side;
Unit *unit;
int dist, *xp, *yp;
{
    int x = unit->x, y = unit->y, dir, x1, y1;

    victimrating = -9999;
    tmpunit = unit;
    victim_here(x, y);
    for_all_directions(dir) {
	if (point_in_dir(x, y, dir, &x1, &y1)) {
	    victim_here(x, y);
	}
    }
    if (victimrating > -9999) {
	*xp = victimx;  *yp = victimy;
	return TRUE;
    } else {
	return FALSE;
    }
}

#if 0
static void
mplayer_react_to_unit_loss(side, unit)
Side *side;
Unit *unit;
{
    int x = unit->x, y = unit->y;
    Theater *th;

    if (!inside_area(x, y)) {
	x = unit->prevx;  y = unit->prevy;
    }
    if (!inside_area(x, y))
      return;
    /* Count the unit as having been lost in a particular theater. */
    if (mplayer(side) && (th = theater_at(side, x, y)) != NULL) {
    	++(th->units_lost);
    }
}
#endif

static void
mplayer_receive_message(side, sender, str)
Side *side, *sender;
char *str;
{
    /* First detect standard messages. */
    if (strcmp(str, "Eh?") == 0) {
	/* Don't respond, otherwise we might infinitely recurse. */
    } else if (allied_side(side, sender)) {
	/* (should say something) */
    } else {
	/* Detect insults and respond appropriately. */
	if (strstr(str, "idiot")) {
	    net_send_message(side, add_side_to_set(sender, NOSIDES), "Loser!");
	} else if (strstr(str, "loser")) {
	    net_send_message(side, add_side_to_set(sender, NOSIDES), "Idiot!");
	} else {
	    /* No idea what the message was, be puzzled. */
	    net_send_message(side, add_side_to_set(sender, NOSIDES), "Eh?");
	}
    }
}

/* This is used by interfaces to display the name of the theater in
   use at a given point. */

static char *
mplayer_at_desig(Side *side, int x, int y)
{
    Theater *theater;

    if (mplayer(side) == NULL)
      return "";
    theater = theater_at(side, x, y);
    return (theater ? theater->name : "<no theater>");
}

/* This is used by interfaces to display boundaries between theaters,
   by comparing the numbers returned. */

static int
mplayer_theater_at(Side *side, int x, int y)
{
    Theater *theater;

    if (mplayer(side) == NULL)
      return 0;
    theater = theater_at(side, x, y);
    return (theater ? theater->id : 0);
}

/* Collect initial strength information stored in the mplayer's
   private saved data. */

static int
mplayer_read_strengths(Side *side)
{
    int sn1, u, found = FALSE;
    char *propname;
    Obj *props, *bdg, *rest, *sidebdg, *urest;
    Side *side1;
    Strategy *strategy = mplayer(side);

    props = find_at_key(side->aidata, "mplayer");
    for (; props != lispnil; props = cdr(props)) {
	bdg = car(props);
	propname = c_string(car(bdg));
	if (strcmp(propname, "strengths0") == 0) {
	    found = TRUE;
	    rest = cdr(bdg);
	    for_all_sides(side1) {
		sn1 = side1->id;
		sidebdg = car(rest);
		urest = cadr(sidebdg);
		for_all_unit_types(u) {
		    strategy->strengths0[sn1][u] = c_number(car(urest));
		    urest = cdr(urest);
		}
		rest = cdr(rest);
	    }
	} else if (strcmp(propname, "alstrengths0") == 0) {
	    found = TRUE;
	    rest = cdr(bdg);
	    for_all_sides(side1) {
		sn1 = side1->id;
		sidebdg = car(rest);
		urest = cadr(sidebdg);
		for_all_unit_types(u) {
		    strategy->alstrengths0[sn1][u] = c_number(car(urest));
		    urest = cdr(urest);
		}
		rest = cdr(rest);
	    }
	} else if (strcmp(propname, "points0") == 0) {
	    found = TRUE;
	    rest = cdr(bdg);
	    for_all_sides(side1) {
		sn1 = side1->id;
		strategy->points0[sn1] = c_number(car(rest));
		rest = cdr(rest);
	    }
	} else if (strcmp(propname, "alpoints0") == 0) {
	    found = TRUE;
	    rest = cdr(bdg);
	    for_all_sides(side1) {
		sn1 = side1->id;
		strategy->alpoints0[sn1] = c_number(car(rest));
		rest = cdr(rest);
	    }
	} else {
	}
    }
    return found;
}

/* Write out any state that the mplayer must preserve.  We don't actually write;
   instead we build a Lisp object and pass that back to the writing routines. */

static Obj *
mplayer_save_state(side)
Side *side;
{
    int sn1, u;
    Obj *rslt, *vallist, *uvallist;
    Side *side1;
    Strategy *strategy = mplayer(side);

    rslt = lispnil;
    /* Just return last result if it's already been computed. */
    if (strategy->writable_state != lispnil || xmalloc_warnings)
      return strategy->writable_state;
    /* We're pushing bindings onto a list, so do in reverse of desired order. */
    vallist = lispnil;
    for_all_sides(side1) {
	sn1 = side1->id;
	uvallist = lispnil;
	for_all_unit_types(u) {
	    uvallist = cons(new_number(strategy->alstrengths0[sn1][u]), uvallist);
	}
	uvallist = reverse(uvallist);
	push_binding(&vallist, new_number(sn1), uvallist);
    }
    vallist = reverse(vallist);
    push_cdr_binding(&rslt, intern_symbol("alstrengths0"), vallist);
    vallist = lispnil;
    for_all_sides(side1) {
	sn1 = side1->id;
	uvallist = lispnil;
	for_all_unit_types(u) {
	    uvallist = cons(new_number(strategy->strengths0[sn1][u]), uvallist);
	}
	uvallist = reverse(uvallist);
	push_binding(&vallist, new_number(sn1), uvallist);
    }
    vallist = reverse(vallist);
    push_cdr_binding(&rslt, intern_symbol("strengths0"), vallist);
    vallist = lispnil;
    for_all_sides(side1) {
	sn1 = side1->id;
	vallist = cons(new_number(strategy->alpoints0[sn1]), vallist);
    }
    vallist = reverse(vallist);
    push_cdr_binding(&rslt, intern_symbol("alpoints0"), vallist);
    vallist = lispnil;
    for_all_sides(side1) {
	sn1 = side1->id;
	vallist = cons(new_number(strategy->points0[sn1]), vallist);
    }
    vallist = reverse(vallist);
    push_cdr_binding(&rslt, intern_symbol("points0"), vallist);
    strategy->writable_state = rslt;
    return rslt;
}
