/* Functions common to all AIs.
   Copyright (C) 1992-1997 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"
#include "kernel.h"
/* (should do a better separation of kernel in set_side_ai) */
#include "kpublic.h"
extern void try_to_draw PARAMS ((Side *side, int flag, char *ainame));
extern void give_up PARAMS ((Side *side, char *ainame));

/* The number of AI types defined.  Increment for each new AI type
   that is compiled in. */

#define numaitypes 2

/* Declarations of all AI types. */

extern AI_ops mplayer_ops;

AI_ops **all_ai_ops;

void
init_ai_types()
{
    /* Fill in the table of pointers to AI types. */
    all_ai_ops = (AI_ops **) xmalloc(numaitypes * sizeof(AI_ops *));
    all_ai_ops[0] = (AI_ops *) xmalloc(sizeof(AI_ops));
    all_ai_ops[1] = &mplayer_ops;
}

void
init_ai(side)
Side *side;
{
    int i;
    char *typename;
    int (*test) PARAMS ((void));
    void (*fn) PARAMS ((Side *side));

    if (side_wants_ai(side)) {
	/* (should merge with same bit in set_side_ai) */
	if (strcmp(side->player->aitypename, "ai") == 0) {
	    /* (should use the "best" default for this game and side) */
	    side->player->aitypename = "mplayer";
	}
	for (i = 0; i < numaitypes; ++i) {
	    typename = (all_ai_ops[i])->name;
	    if (typename != NULL
		&& strcmp(typename, side->player->aitypename) == 0) {
		test = (all_ai_ops[i])->to_test_compat;
		if (test == NULL || (*test)()) {
		    side->aitype = i;
		    fn = (all_ai_ops[i])->to_init;
		    if (fn)
		      (*fn)(side);
		} else {
		    /* desired aitype not avail - should complain */
		}
	    }
	}
	if (!side_has_ai(side)) {
	    init_warning("could not make an AI (type %s) for %s",
			 side->player->aitypename, side_desig(side));
	}
    }
}

/* Change the AI running a side.  This has to clean up if the AI is
   being turned off. */

void
set_side_ai(side, typename)
Side *side;
char *typename;
{
    Unit *unit;

    if (!empty_string(typename) && strcmp(typename, "ai") == 0) {
	/* (should use the "best" default for this game and side) */
	typename = "mplayer";
    }
    /* (only copy if not an already-defined name, otherwise assign from
       static list) */
    if (typename != NULL)
      typename = copy_string(typename);
    side->player->aitypename = typename;
    /* If remote, do nothing beyond the recording the name of the AI. */
    if (numremotes > 0 && side->player->rid != my_rid)
      return;
    /* If switching to human, clear out everything that was set up by an AI. */
    if (empty_string(typename)) {
	for_all_side_units(side, unit) {
	    unit->aihook = NULL;
	    if (unit->plan && unit->plan->aicontrol) {
		net_force_replan(side, unit, FALSE);
	    }
	    /* Still let units finish their currently buffered action. */
	}
    }
    if (empty_string(typename)) {
	/* (should just deactivate instead of erasing and maybe
           reallocating later) */
	side->ai = NULL;
    } else {
	init_ai(side);
    }
}

void
ai_init_turn(side)
Side *side;
{
    void (*fn) PARAMS ((Side *side));

    fn = (all_ai_ops[side->aitype])->to_init_turn;
    if (fn)
      (*fn)(side);
}

void
ai_decide_plan(side, unit)
Side *side;
Unit *unit;
{
    void (*fn) PARAMS ((Side *side, Unit *unit));

    fn = ((all_ai_ops[side->aitype])->to_decide_plan);
    if (fn)
      (*fn)(side, unit);
}

/* Forward an action result to the appropriate AI routine. */

void
ai_react_to_action_result(side, unit, rslt)
Side *side;
Unit *unit;
int rslt;
{
    void (*fn) PARAMS ((Side *side, Unit *unit, int rslt));

    fn = ((all_ai_ops[side->aitype])->to_react_to_action_result);
    if (fn)
      (*fn)(side, unit, rslt);
}

/* Forward a task result to the appropriate AI routine. */

void
ai_react_to_task_result(side, unit, task, rslt)
Side *side;
Unit *unit;
Task *task;
TaskOutcome rslt;
{
    void (*fn) PARAMS ((Side *side, Unit *unit, Task *task, TaskOutcome rslt));

    fn = ((all_ai_ops[side->aitype])->to_react_to_task_result);
    if (fn)
      (*fn)(side, unit, task, rslt);
}

void
ai_react_to_new_side(side, side2)
Side *side, *side2;
{
    void (*fn) PARAMS ((Side *side, Side *side2));

    fn = ((all_ai_ops[side->aitype])->to_react_to_new_side);
    if (fn)
      (*fn)(side, side2);
}

int
ai_adjust_plan(side, unit)
Side *side;
Unit *unit;
{
    int (*fn) PARAMS ((Side *side, Unit *unit));

    fn = ((all_ai_ops[side->aitype])->to_adjust_plan);
    if (fn)
      return (*fn)(side, unit);
    else
      return FALSE;
}

void
ai_finish_movement(side)
Side *side;
{
    void (*fn) PARAMS ((Side *side));

    fn = ((all_ai_ops[side->aitype])->to_finish_movement);
    if (fn)
      (*fn)(side);
}

/* Forward a textual message to the appropriate AI routine. */

void
ai_receive_message(side, sender, str)
Side *side, *sender;
char *str;
{
    void (*fn) PARAMS ((Side *side, Side *sender, char *str));

    fn = ((all_ai_ops[side->aitype])->to_receive_message);
    if (fn)
      (*fn)(side, sender, str);
}

void
ai_save_state(side)
Side *side;
{
    Obj *(*fn) PARAMS ((Side *side)), *state = lispnil;

    fn = ((all_ai_ops[side->aitype])->to_save_state);
    if (fn)
      state = (*fn)(side);
    /* Don't bother if there's no AI state to mess with. */
    if (side->aidata == lispnil && state == lispnil)
      return;
    side->aidata = replace_at_key(side->aidata, side->player->aitypename, state);
}

int
ai_region_at(side, x, y)
Side *side;
int x, y;
{
    int (*fn) PARAMS ((Side *side, int x, int y));

    fn = ((all_ai_ops[side->aitype])->region_at);
    if (fn)
      return (*fn)(side, x, y);
    else
      return 0;
}

char *
ai_at_desig(side, x, y)
Side *side;
int x, y;
{
    char *(*fn) PARAMS ((Side *side, int x, int y));

    fn = ((all_ai_ops[side->aitype])->at_desig);
    if (fn)
      return (*fn)(side, x, y);
    else
      return NULL;
}

void
try_to_draw(side, flag, ainame)
Side *side;
int flag;
char *ainame;
{
    /* If no change, nothing to say or do. */
    if (flag == side->willingtodraw)
      return;
    /* If there is a human player and the decision has not been
       delegated, allow the human to make the final decision. */
    if (side_has_display(side) && !side->ai_may_resign) {
	if (flag)
	  notify(side, "Your AI %s recommends being willing to draw.",
		 ainame);
	else
	  notify(side, "Your AI %s recommends not being willing to draw.",
		 ainame);
	return;
    }
    net_set_willing_to_draw(side, flag);
}

/* If an AI resigns, it tries to help its friends. */

void
give_up(side, ainame)
Side *side;
char *ainame;
{
    Side *side1;

    /* If there is a human player and the decision has not been
       delegated, allow the human to make the final decision. */
    if (side_has_display(side) && !side->ai_may_resign) {
	notify(side, "Your AI %s recommends resignation", ainame);
	return;
    }

// --- SUPPORT FOR TURNING OFF AUTOMATIC AI RESIGNATION --------------------

    /* Don't proceed if autoresignation has been disabled. */
    else if (!side->ai_may_resign)
	return;

// ----------------------------------------------------------------

    /* (should have to give units to controlling side if there is one) */
    /* (should controlling side be able to disallow resignation?) */
    /* Try to give away all of our units to an ally. */
    for_all_sides(side1) {
	if (side != side1 && allied_side(side, side1) && side1->ingame) {
	    net_resign_game(side, side1);
	    return;
	}
    }
    /* (should give to any positively-regarded side?) */
    /* No allies left in game, let everything become independent. */
    /* (should disband all units that can be disbanded?) */
    net_resign_game(side, NULL);
}

/* (should go elsewhere eventually?) */

/* Goal handling. */

GoalDefn goaldefns[] = {

#undef  DEF_GOAL
#define DEF_GOAL(NAME,code,ARGTYPES) { NAME, ARGTYPES },

#include "goal.def"

    { NULL, NULL }
};


/* General handling of goals. */

Goal *
create_goal(type, side, tf)
GoalType type;
Side *side;
int tf;
{
    Goal *goal = (Goal *) xmalloc(sizeof(Goal));

    goal->type = type;
    goal->side = side;
    goal->tf = tf;
    return goal;
}

int
lookup_goal_type(name)
char *name;
{
    int i;

    for (i = 0; goaldefns[i].name != NULL; ++i)
      if (strcmp(name, goaldefns[i].name) == 0)
	return i; /* should get real enum? */
    return GOAL_NO;
}

int
cell_unknown(x, y)
int x, y;
{
    return (!all_see_all && !tmpside->see_all && terrain_view(tmpside, x, y) == UNSEEN);
}

int
enemies_present(x, y)
int x, y;
{
    if (units_visible(tmpside, x, y)) {
    	return (unit_at(x, y) != NULL && !trusted_side(tmpside, unit_at(x, y)->side));
    } else if (terrain_view(tmpside, x, y) != UNSEEN) {
    	return (vside(unit_view(tmpside, x, y)) != side_number(tmpside));
    } else {
    	return FALSE;
    }
}

/* Test a goal to see if it is true for side, as specified. */

int
goal_truth(side, goal)
Side *side;
Goal *goal;
{
    int x, y;
    Side *side2 = NULL;

    if (goal == NULL) return 0;
    switch (goal->type) {
      case GOAL_WON_GAME:
	side2 = goal->side;
	return (side2 ? (side_won(side2) ? 100 : -100) : 0);
      case GOAL_LOST_GAME:
	side2 = goal->side;
	return (side2 ? (side_lost(side2) ? 100 : -100) : 0);
      case GOAL_POSITIONS_KNOWN:
	/* what if no enemies present? then this is undefined? */
	/* should goals have preconditions or prerequisites? */
	return 0;
      case GOAL_WORLD_KNOWN:
	tmpside = side;
	for_all_interior_cells(x, y) {
	    if (cell_unknown(x, y)) return -100;
	}
	return 100;
      case GOAL_VICINITY_KNOWN:
	tmpside = side;
	if (search_around(goal->args[0], goal->args[1], goal->args[2],
			  cell_unknown, &x, &y, 1)) {
	    return -100;
	} else {
	    return 100;
	}
      case GOAL_VICINITY_HELD:
      	tmpside = side;
	if (search_around(goal->args[0], goal->args[1], goal->args[2],
			  enemies_present, &x, &y, 1)) {
	    return -100;
	} else {
	    return 100;
	}
      case GOAL_CELL_OCCUPIED:
	return 0;
      case GOAL_HAS_UNIT_TYPE:
	return 0;
      case GOAL_HAS_UNIT_TYPE_NEAR:
	return 0;
      case GOAL_HAS_MATERIAL_TYPE:
	return 0;
      default:
	case_panic("goal type", goal->type);
	return 0;
    }
}

/* (might eventually want another evaluator that guesses at another
   side's goals) */

char *goalbuf = NULL;

char *
goal_desig(goal)
Goal *goal;
{
    int numargs, i, arg;
    char *argtypes;

    if (goal == NULL)
      return "<null goal>";
    if (goalbuf == NULL)
      goalbuf = xmalloc(BUFSIZE);
    sprintf(goalbuf, "<goal s%d %s%s",
	    side_number(goal->side), (goal->tf ? "" : "not "),
	    goaldefns[goal->type].name);
    argtypes = goaldefns[goal->type].argtypes;
    numargs = strlen(argtypes);
    for (i = 0; i < numargs; ++i) {
	arg = goal->args[i];
	switch (argtypes[i]) {
	  case 'h':
	    tprintf(goalbuf, "%d", arg);
	    break;
	  case 'm':
	    if (is_material_type(arg))
	      tprintf(goalbuf, " %s", m_type_name(arg));
	    else
	      tprintf(goalbuf, " m%d?", arg);
	    break;
	  case 'S':
	    tprintf(goalbuf, " `%s'", side_desig(side_n(arg)));
	    break;
	  case 'u':
	    if (is_unit_type(arg))
	      tprintf(goalbuf, " %s", u_type_name(arg));
	    else
	      tprintf(goalbuf, " m%d?", arg);
	    break;
	  case 'U':
	    tprintf(goalbuf, " `%s'", unit_desig(find_unit(arg)));
	    break;
	  case 'w':
	    tprintf(goalbuf, " %dx", arg);
	    break;
	  case 'x':
	    tprintf(goalbuf, " %d,", arg);
	    break;
	  case 'y':
	    tprintf(goalbuf, "%d", arg);
	    break;
	  default:
	    tprintf(goalbuf, " %d", arg);
	    break;
	}
    }
    strcat(goalbuf, ">");
    return goalbuf;
}


/* General collections of numbers used by all machine players and unit plans. */

int bhw_max;

/* Init used by all machine players.  Precompute useful information
   relating to unit types in general, and that usually gets referenced
   in inner loops. */

void
ai_init_shared()
{
    int u, u1, u2, t, m1, numbuilders, tmp;
    
    /* Need 3 scratch layers for routefinding. */
    allocate_area_scratch(3);

    /* Recognize unit types that are bases */
    for_all_unit_types(u1) {
	set_u_is_base(u1, FALSE);
	tmp = FALSE;
	for_all_material_types(m1) {
	    if (um_base_production(u1, m1) > 0) {
		tmp = TRUE;
		break;
	    }
	}
	if (tmp) {
	    for_all_unit_types(u2) {
		if ((u1 != u2) && could_carry(u1,u2)) {
		    set_u_is_base(u1, TRUE);
		    continue;
		}
	    }
	}
    }
    /* Note that is_base_builder is set to the type of base that can */
    /* be built.  That means that unit zero can not be a base which */
    /* can be built. */
    for_all_unit_types(u1) {
	set_u_is_transport(u1, FALSE);
	set_u_is_carrier(u1, FALSE);
	set_u_is_base_builder(u1, FALSE);
	set_u_can_make(u1, FALSE);
	set_u_can_capture(u1, FALSE);
	numbuilders = 0;
/*	ave_build_time[u1] = 0;  */
	for_all_unit_types(u2) {
	    if (u_is_base(u2) &&
		could_create(u1, u2) &&
		1 /* can be made quickly? */) {
		set_u_is_base_builder(u1, u2);
	    }
	    if (u_speed(u1) > 0 && could_carry(u1, u2)) {
		set_u_is_transport(u1, TRUE);
	    }
	    if (could_create(u2, u1)) {
		numbuilders++;
/*		ave_build_time[u1] += uu_make(u2,u1);  */
		set_u_can_make(u2, TRUE);
	    }
	    if (uu_capture(u1, u2) > 0 || uu_indep_capture(u1, u2) > 0) {
		set_u_can_capture(u1, TRUE);
	    }
	}
/*	if (numbuilders > 0)
	  ave_build_time[u1] /= numbuilders;  */
    }
    /* a carrier is a unit that is a mobile base, but that cannot
       move a passenger anywhere the passenger could not go itself. */
    for_all_unit_types(u1) {
	if (u_is_transport(u1)) {
	    set_u_is_carrier(u1, TRUE);
	    for_all_unit_types(u2) {
		if (could_carry(u1, u2)) {
		    for_all_terrain_types(t) {
			if (could_be_on(u1, t) && !could_be_on(u2, t))
			  set_u_is_carrier(u1, FALSE);
		    }
		}
	    }
	}
    }
    for_all_unit_types(u) {
	set_u_bw(u, basic_worth(u));
    }
    for_all_unit_types(u) {
	for_all_unit_types(u2) {
	    set_uu_bhw(u, u2, basic_hit_worth(u, u2));
	    set_uu_bfw(u, u2, basic_fire_worth(u, u2));
	    set_uu_bcw(u, u2, basic_capture_worth(u, u2));
	    set_uu_btw(u, u2, basic_transport_worth(u, u2));
	    if (uu_zz_bhw(u, u2) > bhw_max)
	      bhw_max = uu_zz_bhw(u, u2);
	}
    }
    /* Tell how things rated. */
    if (DebugM)
      display_assessment();
}

int basic_transport_worth PARAMS ((int u1, int u2));
void set_uu_btw PARAMS ((int u1, int u2, int v));

#define DICE(N,NUMDICE,SPOTS,OFFSET)  \
  (((N) >> 14 == 0 || (N) >> 14 == 3) ?  \
   (NUMDICE = 0, SPOTS = 0, OFFSET = (N)) :  \
   (NUMDICE = ((N) >> 11) & 0x07, SPOTS = ((N) >> 7) & 0x0f, OFFSET = (N) & 0x7f))

/* A crude estimate of the worth of having one type of unit. */

int
basic_worth(u)
int u;
{
    int worth = 0, u2, r, range;
  
    worth += u_hp(u) * 10;
    for_all_unit_types(u2) {
	if (could_create(u, u2))
	  worth += (u_bw(u2) * (50)) / 1 /* uu_make(u, u2) */;
	/* (should account for shared capacity) */
	if (could_carry(u, u2))
	  worth += (1 + u_speed(u)) * uu_capacity_x(u, u2) *
	    (u_is_base(u) ? 10 : 1) * u_bw(u2) / 30;
    }
    range = 12345;
    for_all_material_types(r) {
	worth += um_base_production(u, r) * (u_is_base(u) ? 4 : 1);
	if (um_consumption_per_move(u, r) > 0)
	  range = min(range, um_storage_x(u, r) / max(1, um_consumption_per_move(u, r)));
	if (um_base_consumption(u, r) > 0) 
	  range =
	    min(range, u_speed(u) * um_storage_x(u, r) / max(1, um_base_consumption(u, r)));
    }
    worth += u_speed(u) * u_hp(u);
    worth += (range == 12345 ? area.maxdim : range)
      * u_hp(u) / max(1, 10 - u_speed(u));
    for_all_unit_types(u2) {
	worth += (worth * uu_capture(u, u2)) / 150;
    }
    worth = isqrt(worth);
    DMprintf("unit type %s worth %d \n ", u_type_name(u), worth);
    return worth;
}

/* A basic estimate of the payoff of one unit type attacking another type
   directly.  This is "context-free", does not account for overall goals etc. */

/* (should account for number of attacks possible in one turn) */

int
basic_hit_worth(u, e)
int u, e;
{
    int dam, numdice, numspots, offset, avgdamage, worth = 0, anti = 0;

    if (uu_acp_to_attack(u, e) < 1)
      return -9999;
    dam = uu_damage(u, e);
    DICE(dam, numdice, numspots, offset);
    avgdamage = offset + (numdice * numspots) / 2;
    if (avgdamage > u_hp(e))
      avgdamage = u_hp(e);
    worth = (uu_hit(u, e) * avgdamage) / u_hp(e);
    if (1 /* strength of counterattack */) {
	dam = uu_damage(e, u);
	DICE(dam, numdice, numspots, offset);
	avgdamage = offset + (numdice * numspots) / 2;
	if (avgdamage > u_hp(u))
	  avgdamage = u_hp(u);
	anti = (uu_hit(e, u) * avgdamage) / u_hp(u);
    }
    return worth - (anti * 9) / 10;
}

int
basic_fire_worth(u, e)
int u, e;
{
    int hitchance, dam, numdice, numspots, offset, avgdamage, worth = 0;

    if (u_acp_to_fire(u) < 1)
      return -9999;
    if (uu_fire_hit(u, e) != -1)
      hitchance = uu_fire_hit(u, e);
    else
      hitchance = uu_hit(u, e);
    if (uu_fire_damage(u, e) != -1)
      dam = uu_fire_damage(u, e);
    else
      dam = uu_damage(u, e);
    DICE(dam, numdice, numspots, offset);
    avgdamage = offset + (numdice * numspots) / 2;
    if (avgdamage > u_hp(e))
      avgdamage = u_hp(e);
    worth = (hitchance * avgdamage) / u_hp(e);
    return worth;
}

/* A crude estimate of the payoff of one unit type trying to capture. */

int
basic_capture_worth(u, e)
int u, e;
{
    int acp, worth1 = 0, worth2 = 0;

    acp = max(uu_acp_to_capture(u, e), uu_acp_to_attack(u, e));
    if (acp < 1)
      return -9999;
    if (uu_capture(u, e) > 0) {
	worth1 = (uu_capture(u, e) * u_acp(u)) / acp;
    }
    if (uu_indep_capture(u, e) > 0) {
	worth2 = (uu_indep_capture(u, e) * u_acp(u)) / acp;
    }
    /* (should account for chance of dying in attempt) */
    return max(worth1, worth2);
}

int
basic_transport_worth(u, u2)
int u, u2;
{
    int worth = 0;

    if (could_carry(u, u2)) {
	worth += 1;
    }
    return worth;
}

/* Some notion of the unit's "strength"? */

int
unit_strength(u)
int u;
{
    return 1;
}

/* Display the results of our calculations. */

void
display_assessment()
{
    int u, u2;

    DMprintf("\nUnit Attributes:\n");
    for_all_unit_types(u) {
	DMprintf(" %-3.3s : base %d, transport %d, carrier %d, worth %d\n",
	       shortest_unique_name(u), u_is_base(u),
	       u_is_transport(u), u_is_carrier(u), u_bw(u));
	DMprintf("    Operate between ranges %d and %d\n", operating_range_worst(u), operating_range_best(u));
    }
    DMprintf("\nUnit vs Unit Combat:\n");
    for_all_unit_types(u) {
	DMprintf(" %-3.3s:", shortest_unique_name(u));
	for_all_unit_types(u2)
	  DMprintf("%5d", uu_zz_bhw(u, u2));
	DMprintf("\n");
    }
    DMprintf("\nUnit vs Unit Fire:\n");
    for_all_unit_types(u) {
	DMprintf(" %-3.3s:", shortest_unique_name(u));
	for_all_unit_types(u2)
	  DMprintf("%5d", uu_zz_bfw(u, u2));
	DMprintf("\n");
    }
    DMprintf("\nUnit vs Unit Capture:\n");
    for_all_unit_types(u) {
	DMprintf(" %-3.3s:", shortest_unique_name(u));
	for_all_unit_types(u2)
	  DMprintf(" %4d", uu_zz_bcw(u, u2));
	DMprintf("\n");
    }
    DMprintf("\nUnit vs Unit Transport:\n");
    for_all_unit_types(u) {
	DMprintf(" %-3.3s:", shortest_unique_name(u));
	for_all_unit_types(u2)
	  DMprintf(" %4d", uu_zz_btw(u, u2));
	DMprintf("\n");
    }
    DMprintf("\n");
}

int
is_base_for(u1, u2)
int u1, u2;
{
    return (u_speed(u1) == 0
	    && (uu_capacity_x(u2, u1) > 0
		|| (uu_size(u2, u1) <= u_capacity(u1))));
}

int
is_carrier_for(u1, u2)
int u1, u2;
{
    return (u_speed(u1) > 0
	    && (uu_capacity_x(u2, u1) > 0
		|| (uu_size(u2, u1) <= u_capacity(u1))));
}

/* Since *.def parameters don't have setters usually, we have to supply
   some here.  These are very sensitive to how the parameters are organized,
   and they don't do any checking, so you have to careful about using them. */

void set_u_is_base(u, n) int u, n; {  utypes[u].is_base = n;  }
void set_u_is_transport(u, n) int u, n; {  utypes[u].is_transport = n;  }
void set_u_is_carrier(u, n) int u, n; {  utypes[u].is_carrier = n;  }
void set_u_is_base_builder(u, n) int u, n; {  utypes[u].is_base_builder = n;  }
void set_u_can_make(u, n) int u, n; {  utypes[u].can_make = n;  }
void set_u_can_capture(u, n) int u, n; {  utypes[u].can_capture = n;  }
void set_u_bw(u, n) int u, n; {  utypes[u].bw = n;  }

int bhwtab = -1;
int bfwtab = -1;
int bcwtab = -1;
int btwtab = -1;

void
set_uu_bhw(u1, u2, v)
int u1, u2, v;
{
    if (bhwtab < 0) {
	for (bhwtab = 0; tabledefns[bhwtab].name != NULL; ++bhwtab) {
	    if (strcmp("zz-basic-hit-worth", tabledefns[bhwtab].name) == 0) {
		allocate_table(bhwtab, FALSE);
		break;
	    }
	}
    }
    if (tabledefns[bhwtab].table == NULL)
      run_error("no bhw table allocated");
    (*(tabledefns[bhwtab].table))[numutypes * u1 + u2] = v;
}

void
set_uu_bfw(u1, u2, v)
int u1, u2, v;
{
    if (bfwtab < 0) {
	for (bfwtab = 0; tabledefns[bfwtab].name != NULL; ++bfwtab) {
	    if (strcmp("zz-basic-fire-worth", tabledefns[bfwtab].name) == 0) {
		allocate_table(bfwtab, FALSE);
		break;
	    }
	}
    }
    if (tabledefns[bfwtab].table == NULL)
      run_error("no bfw table allocated");
    (*(tabledefns[bfwtab].table))[numutypes * u1 + u2] = v;
}

void
set_uu_bcw(u1, u2, v)
int u1, u2, v;
{
    if (bcwtab < 0) {
	for (bcwtab = 0; tabledefns[bcwtab].name != NULL; ++bcwtab) {
	    if (strcmp("zz-basic-capture-worth", tabledefns[bcwtab].name) == 0) {
		allocate_table(bcwtab, FALSE);
		break;
	    }
	}
    }
    if (tabledefns[bcwtab].table == NULL)
      run_error("no bcw table allocated");
    (*(tabledefns[bcwtab].table))[numutypes * u1 + u2] = v;
}


void
set_uu_btw(u1, u2, v)
int u1, u2, v;
{
    if (btwtab < 0) {
	for (btwtab = 0; tabledefns[btwtab].name != NULL; ++btwtab) {
	    if (strcmp("zz-basic-transport-worth", tabledefns[btwtab].name) == 0) {
		allocate_table(btwtab, FALSE);
		break;
	    }
	}
    }
    if (tabledefns[btwtab].table == NULL)
      run_error("no btw table allocated");
    (*(tabledefns[btwtab].table))[numutypes * u1 + u2] = v;
}
