Logo Search packages:      
Sourcecode: xconq version File versions  Download package

iplayer.c

/* Implementation of the lobotomized "iplayer" AI in Xconq.
   Copyright (C) 1987-1989, 1991-2000 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 "kpublic.h"
#include "ai.h"

extern void register_iplayer(AI_ops *ops);

/* Local function declarations. */

static void iplayer_init(Side *side);
static void iplayer_init_turn(Side *side);
static void iplayer_create_strategy(Side *side);
static void iplayer_review_units(Side *side);
static void iplayer_decide_plan(Side *side, Unit *unit);
static int iplayer_adjust_plan(Side *side, Unit *unit);
static void iplayer_react_to_task_result(Side *side, Unit *unit, Task *task, TaskOutcome rslt);
static void iplayer_react_to_new_side(Side *side, Side *side2);
static void iplayer_finish_movement(Side *side);
static void iplayer_rethink_plan(Unit *unit);

void
register_iplayer(AI_ops *ops)
{
    ops->name = "iplayer";
    ops->help = "AI for independent units only";
    ops->to_init = iplayer_init;
    ops->to_init_turn = iplayer_init_turn;
    ops->to_decide_plan = iplayer_decide_plan;
    ops->to_react_to_task_result = iplayer_react_to_task_result;
    ops->to_react_to_new_side = iplayer_react_to_new_side;
    ops->to_adjust_plan = iplayer_adjust_plan;
    ops->to_finish_movement = iplayer_finish_movement;
}

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

    /* Delete any old strategy object in case we just switched AI type. */
    if (side->ai != NULL) {
      free(side->ai);
      side->ai = NULL;
    }    
    iplayer_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;
    /* Reset plans of any units that were not doing anything. */
    for_all_side_units(side, unit) {
      if (in_play(unit) && ai_controlled(unit)) {
          net_force_replan(side, unit, TRUE);
      }
    }
}

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

static void
iplayer_init_turn(Side *side)
{
    /* 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;
    /* Iplayers 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 iplayer init turn\n", side_desig(side));
    /* Check out all of our units. */
    iplayer_review_units(side);
    /* (should be integrated better) */
    iplayer_finish_movement(side);
    /* Update unit plans. */
    update_unit_plans(side);
    update_all_progress_displays("", side->id);
    DMprintf("%s iplayer init turn done\n", side_desig(side));
}

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

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

    /* Put the specific structure into a generic slot. */
    side->ai = (struct a_ai *) strategy;
}

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

static void
iplayer_review_units(Side *side)
{
    int u;
    int numoffensive[MAXUTYPES], numdefensive[MAXUTYPES];
    Unit *unit;
    Plan *plan;

    for_all_unit_types(u) {
      numoffensive[u] = numdefensive[u] = 0;
    }
    for_all_side_units(side, unit) {
      if (in_play(unit) && ai_controlled(unit)) {
          /* 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) && ai_controlled(unit)) {
          plan = unit->plan;
          /* 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);
            }
          }
          /* Don't let defense-only units pile up. */
          if (plan->type == PLAN_DEFENSIVE
            && mobile(unit->type)
            && (numoffensive[unit->type] / 3) < numdefensive[unit->type]
            /* However, don't mess with units that have specific
                   defensive goals. */
            && (plan->maingoal == NULL
                  || (plan->maingoal->type != GOAL_UNIT_OCCUPIED
                   && plan->maingoal->type != GOAL_CELL_OCCUPIED))
            && 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);
          }
      }
    }
}

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

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

    switch (plan->type) {
      case PLAN_PASSIVE:
      case PLAN_NONE:
      if (mobile(u)) {
          if (u_colonizer_worth(u) > 0) {
            assign_to_colonize(side, unit);
            return;
          }
          /* Assign most units to offense, save some for defense. */
          if (u_offensive_worth(u) > 0
            && probability(75)) {
              assign_to_offense(side, unit);
          } else if (u_defensive_worth(u) > 0) {
              assign_to_defense(side, unit);
          /* In the unlikely case that mobile units can build anything. */
          } else if (can_build_attackers(side, u)) {
            assign_to_offense_support(side, unit);
          } else if (can_build_defenders(side, u)) {
            assign_to_defense_support(side, unit);
          } else {
          }
      } else {
          /* Unit doesn't move. */
          if (can_build_colonizers(side, u)
            /* (should fine-tune this test) */
            && probability(60)) {
            assign_to_colonization_support(side, unit);
          } else if (can_build_attackers(side, u)) {
            assign_to_offense_support(side, unit);
          } else if (can_build_defenders(side, u)) {
            assign_to_defense_support(side, unit);
          } else if (u_defensive_worth(u) > 0) {
            assign_to_defense(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;
    }
}

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

    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, 0, 0);
            } 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;
}

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

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

    /* 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 (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;
          }
          /* 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);
    }
}

/* This function is called whenever a new side appears in the game.  It
   mainly needs to make that any allocated data is resized appropriately. */

static void
iplayer_react_to_new_side(Side *side, Side *side2)
{
}

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

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

    for_all_side_units(side, unit) {
      if (is_active(unit) && ai_controlled(unit)) {
          iplayer_rethink_plan(unit);
      }
    }
}

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

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

    if (toptask)
      nexttask = toptask->next;
    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");
    }
}

Generated by  Doxygen 1.6.0   Back to index