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

computer.c

/*--------------------------------------------------------------------------*/
/* computer                                                                 */
/*--------------------------------------------------------------------------*/

#include <config.h>

#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

#include "computer.h"
#include "actors.h"
#include "board.h"
#include "field.h"
#include "main.h"
#include "list.h"

/*--------------------------------------------------------------------------*/
/* defines                                                                  */
/*--------------------------------------------------------------------------*/

#define DATA(x)         (computer_data[configs[computer_side]->data_num].x)
#define FIELD_SKILL     DATA(skill_level)
#define SHOOT_DISTANCE  DATA(shoot_distance)
#define ACTOR_DISTANCE  DATA(actor_distance)
#define ACTOR_RECHARGE  DATA(actor_recharge)
#define NUM_GOOD_MOVES  DATA(num_good_moves)

#define FIELD_SKILL_HARD        0
#define FIELD_SKILL_MEDIUM      10
#define FIELD_SKILL_EASY        20

/*--------------------------------------------------------------------------*/
/* structures                                                               */
/*--------------------------------------------------------------------------*/

typedef struct {
   int light_sum;                       /* damage light caused */
   int dark_sum;                        /* damage dark caused */
   int light_hits;                      /* number of times light hit */
   int dark_hits;                       /* number of times dark hit */
   int count;
} DAMAGE;

typedef struct {
   /* field */
   int skill_level;                     /* how easy should the computer be */
   int shoot_distance;                  /* escape missiles this close */
   int actor_distance;                  /* escape actors this close, who */
   int actor_recharge;                  /*   are at this recharge level */
   /* board */
   int num_good_moves;                  /* good moves to consider (max 32) */
} COMPUTER_DATA;

typedef struct {
   int spell;
   int x1, y1;
   int x2, y2;
   int score;
} MOVE;

/*--------------------------------------------------------------------------*/
/* functions                                                                */
/*--------------------------------------------------------------------------*/

static void computer_board(COMMAND *cmd);
static void computer_board_teleport_exchange(int spell, MOVE *move, int me, int him);
static void computer_board_teleport(MOVE *move);
static void computer_board_heal(MOVE *move);
static void computer_board_shift_time(MOVE *move);
static void computer_board_exchange(MOVE *move);
static void computer_board_summon_elemental(MOVE *move);
static void computer_board_revive(MOVE *move);
static void computer_board_imprison(MOVE *move);
static int computer_board_spell(void);
static void computer_board_battle_score(MOVE *move);
static void computer_board_move_score(MOVE *move);
static void computer_board_insert_move(int x1, int y1, int x2, int y2);
static int computer_board_good_moves(int pass);
static void computer_field(COMMAND *cmd);
static int computer_field_offense(COMMAND *cmd);
static int computer_field_defense(COMMAND *cmd);
static void computer_field_move(COMMAND *cmd, int do_move);
static int computer_field_collision_course(FIELD_ACTOR *target, FIELD_ACTOR *source, int dir);
static int computer_field_direction(int x1, int y1);
static void computer_field_stop(void);

extern void Xarchon_AI_Computer(int *x1, int *y1, int *x2, int *y2);

/*--------------------------------------------------------------------------*/
/* variables                                                                */
/*--------------------------------------------------------------------------*/

static MOVE good_moves[33];

static int computer_side;
static COMPUTER_CONFIG *configs[2];

static void (*computer_board_spell_funcs[SPELL_COUNT_2])(MOVE *move) = {
   NULL,
   computer_board_teleport,
   computer_board_heal,
   computer_board_shift_time,
   computer_board_exchange,
   computer_board_summon_elemental,
   computer_board_revive,
   computer_board_imprison,
   NULL,                                /* cease conjuring */
   NULL
};

static COMPUTER_DATA computer_data[3] = {
   { 45, 2, 7, FPS * 0.5, 6 },
   { 9, 2, 7, FPS * 0.5, 4 },
   { 4, 5, 5, FPS * 0.3, 1 }
};
   
static DAMAGE computer_damage[ACTOR_NUM_LIGHT + 1][ACTOR_NUM_DARK + 1];
static int computer_damage_ok = 0;

/*--------------------------------------------------------------------------*/
/* computer_start                                                           */
/*--------------------------------------------------------------------------*/

void computer_start(int side, COMPUTER_CONFIG *config)
{
   configs[side] = config;
}

/*--------------------------------------------------------------------------*/
/* computer_turn                                                            */
/*--------------------------------------------------------------------------*/

void computer_turn(int side, int mode, COMMAND *cmd)
{
   computer_side = side;
   switch (mode) {
      case IFACE_BOARD:
         computer_board(cmd);
         break;
      case IFACE_FIELD_START:
         break;
      case IFACE_FIELD:
         computer_field(cmd);
         break;
      case IFACE_FIELD_STOP:
         computer_field_stop();
         break;
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board                                                           */
/*--------------------------------------------------------------------------*/

void computer_board(COMMAND *cmd)
{
   int n;
   MOVE *move;

   if (computer_board_spell())
      move = &good_moves[0];
   else {
      if (configs[computer_side]->old_board_mode) {
         n = computer_board_good_moves(1);
         move = &good_moves[random() % n];
      } else {
         move = &good_moves[0];
         Xarchon_AI_Computer(&move->x1, &move->y1, &move->x2, &move->y2);
      }
      move->spell = 0;
   }
   cmd->b.spell = move->spell;
   cmd->b.x1 = move->x1;
   cmd->b.y1 = move->y1;
   cmd->b.x2 = move->x2;
   cmd->b.y2 = move->y2;
}

/*--------------------------------------------------------------------------*/
/* computer_board_teleport_exchange                                         */
/*--------------------------------------------------------------------------*/

void computer_board_teleport_exchange(int spell, MOVE *move, int me, int him)
{
   int x1, y1;
   int x2, y2;
   int flags;

   board_find_actor(me, &x1, &y1);
   flags = CELL_POWER | CELL_IMPRISON;
   if (x1 != -1 && (board_cells[y1][x1].flags & flags) == 0) {
      board_find_actor(him, &x2, &y2);
      if (spell == SPELL_TELEPORT)      /* it is possible to teleport    */
         flags ^= CELL_IMPRISON;        /*   *into* an imprisoned square */
      if (x2 != -1 && (board_cells[y2][x2].flags & flags) == 0) {
         move->spell = spell;
         move->x1 = x1;
         move->y1 = y1;
         move->x2 = x2;
         move->y2 = y2;
      }
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board_teleport                                                  */
/*--------------------------------------------------------------------------*/

void computer_board_teleport(MOVE *move)
{
   int me, him;
   int x, y;
   int lumi;

   if (random() % 5 == 0) {
      me = (computer_side == 0) ? ACTOR_UNICORN : ACTOR_BASILISK;
      him = (computer_side == 0) ? ACTOR_BASILISK : ACTOR_UNICORN;
      board_find_actor(him, &x, &y);
      if (x != -1) {
         lumi = board_cell_lumi(&board_cells[y][x]);
         if ((computer_side == 0 && (lumi & CELL_LIGHT)) ||
             (computer_side == 1 && (lumi & CELL_DARK)))
            computer_board_teleport_exchange(SPELL_TELEPORT, move, me, him);
      }
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board_heal                                                      */
/*--------------------------------------------------------------------------*/

void computer_board_heal(MOVE *move)
{
   int x, y;
   int type;

   type = (computer_side == 0) ? ACTOR_UNICORN : ACTOR_BASILISK;
   board_find_actor(type, &x, &y);
   if (x != -1 && (board_cells[y][x].flags & CELL_POWER) == 0 &&
       board_cells[y][x].actor->strength <= actors_list[type].strength / 2) {
      move->spell = SPELL_HEAL;
      move->x1 = x;
      move->y1 = y;
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board_shift_time                                                */
/*--------------------------------------------------------------------------*/

void computer_board_shift_time(MOVE *move)
{
   int x, y;

   if (!board_is_imprison_ok()) {
      for (y = 0; y < BOARD_YCELLS; y++)
         for (x = 0; x < BOARD_XCELLS; x++)
            if (board_cells[y][x].flags & CELL_IMPRISON) {
               move->spell = SPELL_SHIFT_TIME;
               return;
            }
   }

   if (!spell_avails[!computer_side][SPELL_SHIFT_TIME]) {
      move->spell = SPELL_SHIFT_TIME;
      return;
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board_exchange                                                  */
/*--------------------------------------------------------------------------*/

void computer_board_exchange(MOVE *move)
{
   int me, him;

   if (board_turn % 40 == random() % 40) {
      me = (computer_side == 0) ? ACTOR_DJINNI : ACTOR_DRAGON;
      him = (computer_side == 0) ? ACTOR_GOBLIN : ACTOR_KNIGHT;
      computer_board_teleport_exchange(SPELL_EXCHANGE, move, me, him);
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board_summon_elemental                                          */
/*--------------------------------------------------------------------------*/

void computer_board_summon_elemental(MOVE *move)
{
   int x, y;
   int type;

   if (board_turn % 20 == random() % 20) {
      type = (computer_side == 0) ? ACTOR_SHAPESHIFTER : ACTOR_PHOENIX;
      board_find_actor(type, &x, &y);
      if (x != -1 && (board_cells[y][x].flags & CELL_POWER) == 0) {
         move->spell = SPELL_SUMMON_ELEMENTAL;
         move->x1 = x;
         move->y1 = y;
      }
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board_revive                                                    */
/*--------------------------------------------------------------------------*/

void computer_board_revive(MOVE *move)
{
   int actors[10], i;

   if (random() % 2 == 0)
      if (board_revive_check(actors, NULL, NULL))
         for (i = 0; actors[i] != 0; i++)
            if (actors[i] == ACTOR_PHOENIX ||
                actors[i] == ACTOR_DJINNI ||
                actors[i] == ACTOR_UNICORN ||
                actors[i] == ACTOR_SHAPESHIFTER ||
                actors[i] == ACTOR_DRAGON ||
                actors[i] == ACTOR_BASILISK) {
               move->spell = SPELL_REVIVE;
               move->x1 = actors[i];
               return;
            }
}

/*--------------------------------------------------------------------------*/
/* computer_board_imprison                                                  */
/*--------------------------------------------------------------------------*/

void computer_board_imprison(MOVE *move)
{
   int x, y;
   int target_x, target_y;
   int type;
   int count;

   if (board_is_imprison_ok()) {
      type = (computer_side == 0) ? ACTOR_SORCERESS : ACTOR_WIZARD;
      board_find_actor(type, &target_x, &target_y);
      if (target_x == -1) {
         count = 0;
         for (y = 0; y < BOARD_YCELLS; y++)
            for (x = 0; x < BOARD_XCELLS; x++)
               if (board_cells[y][x].actor != NULL &&
                   actor_is_side(board_cells[y][x].actor, !computer_side)) {
                  target_x = x;
                  target_y = y;
                  count++;
               }
         if (count != 1)
            target_x = -1;
      }
      if (target_x != -1 &&
          (board_cells[target_y][target_x].flags & CELL_POWER) == 0) {
         move->spell = SPELL_IMPRISON;
         move->x1 = target_x;
         move->y1 = target_y;
      }
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board_spell                                                     */
/*--------------------------------------------------------------------------*/

int computer_board_spell(void)
{
   int x, y;
   int type;
   MOVE move;
   int i = 0, i0;

   /* spells are only available if we have non-imprisoned master */
   type = (computer_side == 0) ? ACTOR_WIZARD : ACTOR_SORCERESS;
   if (!board_find_actor(type, &x, &y))
      return 0;
   if (board_cells[y][x].flags & CELL_IMPRISON)
      return 0;

   /* select a random spell that is worth casting */
   move.spell = 0;
   i0 = random() % (SPELL_COUNT - 2) + 1;  /* returns 1..7 */
   for (i = i0 + 1; i != i0; i++) {
      if (i < SPELL_FIRST)              /* no spell number 0 */
         continue;
      if (i >= SPELL_LAST) {
         i = SPELL_FIRST - 1;
         continue;
      }
      if (spell_avails[computer_side][i]) {
         computer_board_spell_funcs[i](&move);
         if (move.spell != 0) {
            good_moves[0].spell = i;
            good_moves[0].x1 = move.x1;
            good_moves[0].y1 = move.y1;
            good_moves[0].x2 = move.x2;
            good_moves[0].y2 = move.y2;
            return 1;
         }
      }
   }
   return 0;
}

/*--------------------------------------------------------------------------*/
/* computer_board_battle_score                                              */
/*--------------------------------------------------------------------------*/

void computer_board_battle_score(MOVE *move)
{
   int health;
   int attacker_damage, defender_damage;
   int attacker_hits, defender_hits;

   health = field_initial_life(board_cells[move->y1][move->x1].actor,
                               &board_cells[move->y2][move->x2]);
   computer_field_score(board_cells[move->y1][move->x1].actor,
                        board_cells[move->y2][move->x2].actor,
                        &attacker_damage, &defender_damage,
                        &attacker_hits, &defender_hits);
   if (health <= defender_damage)       /* our guy is going to die */
      move->score = 0;
   else {
      health = field_initial_life(board_cells[move->y2][move->x2].actor,
                                  &board_cells[move->y2][move->x2]);
      move->score = attacker_damage * 500 / health;
   }
}

/*--------------------------------------------------------------------------*/
/* computer_board_move_score                                                */
/*--------------------------------------------------------------------------*/

void computer_board_move_score(MOVE *move)
{
   int x, y;
   int n_actors, n_power_points;

   /* score move regardless of power points */
   if (board_cells[move->y2][move->x2].actor == NULL)
      move->score = random() % 500;
   else
      computer_board_battle_score(move);

   /* if moving from a regular square into a power point, give bonus */
   if ((board_cells[move->y1][move->x1].flags & CELL_POWER) == 0 &&
       (board_cells[move->y2][move->x2].flags & CELL_POWER) != 0) {
      n_actors = 0;
      n_power_points = 1;
      for (y = 0; y < BOARD_YCELLS; y++)
         for (x = 0; x < BOARD_XCELLS; x++)
            if (board_cells[y][x].actor != NULL &&
                actor_is_side(board_cells[y][x].actor, computer_side)) {
               n_actors++;
               if (board_cells[y][x].flags & CELL_POWER)
                  n_power_points++;
            }
      if (n_actors >= 5)
         move->score += n_power_points * 500;
   }

   /* if moving from a power point to a regular square, reduce score */
   /* or:  if moving the master */
   if ((board_cells[move->y1][move->x1].flags & CELL_POWER) != 0 ||
        (board_cells[move->y1][move->x1].actor->type & ACTOR_MASTER) == ACTOR_MASTER)
      move->score /= 10;
}

/*--------------------------------------------------------------------------*/
/* computer_board_insert_move                                               */
/*--------------------------------------------------------------------------*/

void computer_board_insert_move(int x1, int y1, int x2, int y2)
{
   MOVE move;
   int i, min_i;

   move.x1 = x1;
   move.y1 = y1;
   move.x2 = x2;
   move.y2 = y2;
   computer_board_move_score(&move);

   for (i = 0; i < NUM_GOOD_MOVES; i++)
      if (good_moves[i].x1 == -1)
         break;
   if (i >= NUM_GOOD_MOVES) {
      min_i = 0;
      for (i = 1; i < NUM_GOOD_MOVES; i++)
         if (good_moves[i].score < good_moves[min_i].score)
            min_i = i;
      if (good_moves[min_i].score < move.score)
         i = min_i;
   }
   if (i < NUM_GOOD_MOVES)
      memcpy(&good_moves[i], &move, sizeof(MOVE));
}

/*--------------------------------------------------------------------------*/
/* computer_board_good_moves                                                */
/*--------------------------------------------------------------------------*/

int computer_board_good_moves(int pass)
{
   int i;
   int x1, y1;
   int x2, y2;
   ACTOR *actor;

   for (i = 0; i < NUM_GOOD_MOVES + 1; i++)
      good_moves[i].x1 = -1;

   for (y1 = 0; y1 < BOARD_YCELLS; y1++)
      for (x1 = 0; x1 < BOARD_XCELLS; x1++) {
         if (!board_is_pickable(x1, y1, 0))
            continue;

         /* filtering (pass 1 only):  pick only ground actors on */
         /* even-numbered turns;  only fly ones on odd-numbered turns */
         actor = board_cells[y1][x1].actor;
         /*
         if ((actor->type & ACTOR_MASK) != ACTOR_MANTICORE)
            continue;
         */
         if (pass == 1 &&
             (((board_turn / 2) % 2 != 0 && (actor->type & ACTOR_FLY) == 0) ||
              ((board_turn / 2) % 2 == 0 && (actor->type & ACTOR_GROUND) == 0)))
            continue;
         /* filtering (pass 1,2 only):  don't pick the master */
         if (pass == 2 && (actor->type & ACTOR_MASTER) == 0)
            continue;

         for (y2 = y1 - actor->distance; y2 <= y1 + actor->distance; y2++)
            for (x2 = x1 - actor->distance; x2 <= x1 + actor->distance; x2++)
               if (y2 >= 0 && y2 < BOARD_YCELLS &&
                   x2 >= 0 && x2 < BOARD_XCELLS &&
                   (x2 != x1 || y2 != y1) &&
                   (board_cells[y2][x2].actor == NULL ||
                    actor_is_side(board_cells[y2][x2].actor, !computer_side)) &&
                   board_get_route(x1, y1, x2, y2) != NULL)
                  computer_board_insert_move(x1, y1, x2, y2);
      }

   for (i = 0; i < NUM_GOOD_MOVES && good_moves[i].x1 != -1; i++)
      ;
   if (i == 0 && pass < 3)              /* retry (the higher the pass num, */
      i = computer_board_good_moves(pass + 1); /* the less filters are done) */
   if (i == 0) {
      fprintf(stderr, "computer_board_good_moves():  cannot find any possible move to make\n");
      exit(EXIT_FAILURE);
   }
   return i;
}

/*--------------------------------------------------------------------------*/
/* computer_field                                                           */
/*--------------------------------------------------------------------------*/

void computer_field(COMMAND *cmd)
{
   int ok = 0;

   if (field_frame_time % (random() % (FIELD_SKILL / 10 + 1) + 1) != 0) {
      cmd->f.dir = 0;
      cmd->f.fire = 0;
      return;
   }
   
   if ((field_me->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT) {
      ok = computer_field_defense(cmd);
      if (!ok)
         ok = computer_field_offense(cmd);
   } else {
      ok = computer_field_offense(cmd);
      if (!ok)
        ok = computer_field_defense(cmd);
   }
   computer_field_move(cmd, !ok);
}

/*--------------------------------------------------------------------------*/
/* computer_field_offense                                                   */
/*--------------------------------------------------------------------------*/

int computer_field_offense(COMMAND *cmd)
{
   FIELD_ACTOR *weapon;
   int dist0, dist1;
   int dir, dir_last;

   weapon = field_me->weapon;
   cmd->f.dir = computer_field_direction(field_he->x, field_he->y);
   /* dark cloud attacker:  closely follow enemy when cloud is active */
   if ((weapon->actor->type & ACTOR_MASK) == ACTOR_DARK_CLOUD &&
       weapon->state != 0) {
      /* ... unless enemy is firing a light cloud */
      if ((field_he->weapon->actor->type & ACTOR_MASK) != ACTOR_LIGHT_CLOUD ||
          field_he->weapon->state == 0) {
         cmd->f.fire = 0;
         return 1;
      }
   }
   /* otherwise, any attacker:  don't try offense until weapon is recharged */
   if (!WAS_TIME(weapon, weapon->actor->recharge))
      return 0;
   cmd->f.fire = 1;

   /* hand/cloud attackers:  attack if close enough */
   if ((weapon->actor->type & ACTOR_WEAPON_HAND) == ACTOR_WEAPON_HAND ||
       (weapon->actor->type & ACTOR_WEAPON_CLOUD) == ACTOR_WEAPON_CLOUD) {
      dist0 = abs(field_he->x - field_me->x) * abs(field_he->x - field_me->x)
            + abs(field_he->y - field_me->y) * abs(field_he->y - field_me->y);
      /*
      if ((weapon->actor->type & ACTOR_WEAPON_HAND) == ACTOR_WEAPON_HAND)
      */
         dist1 = CELL_X(1) * CELL_X(1) + CELL_Y(1) * CELL_Y(1);
      /*
      else */                           /* cloud */
      /* dist1 = CELL_X(2) * CELL_X(2) + CELL_Y(2) * CELL_Y(2); */
      if (dist0 <= dist1)
         return 1;
   }

   /* shoot attackers:  attack if clear line of fire */
   if ((weapon->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT) {
      dir_last = (FIELD_SKILL < FIELD_SKILL_EASY) ? STATE_MOVE_DOWN_RIGHT : STATE_MOVE_RIGHT;
      for (dir = STATE_MOVE_FIRST; dir <= dir_last; dir++)
         if (computer_field_collision_course(field_he, field_me, dir)) {
            cmd->f.dir = dir;
            return 1;
         }
   }

   cmd->f.dir = 0;
   cmd->f.fire = 0;

   return 0;
}

/*--------------------------------------------------------------------------*/
/* computer_field_defense                                                   */
/*--------------------------------------------------------------------------*/

int computer_field_defense(COMMAND *cmd)
{
   FIELD_ACTOR *weapon;
   FIELD_ACTOR *threat = NULL;
   int opposite_ok = 0;
   int state = 0;
   int dist0, dist1;
   int i;
   int x, y;

   /* avoid missile: if his shoot-type weapon is close enough */
   weapon = field_he->weapon;
   dist0 = abs(weapon->x - field_me->x) * abs(weapon->x - field_me->x)
         + abs(weapon->y - field_me->y) * abs(weapon->y - field_me->y);
   dist1 = CELL_X(SHOOT_DISTANCE) * CELL_X(SHOOT_DISTANCE)
         + CELL_Y(SHOOT_DISTANCE) * CELL_Y(SHOOT_DISTANCE);
   if ((weapon->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT &&
       weapon->state != 0 && dist0 <= dist1 &&
       computer_field_collision_course(field_me, weapon, weapon->state)) {
      threat = weapon;
      state = weapon->state;
      opposite_ok = 0;
   }

   /* avoid actor: if he is close and ready to fire */
   /* avoid cloud: if enemy is close and currently firing it */
   if (threat == NULL) {
      dist0 = abs(field_he->x - field_me->x) * abs(field_he->x - field_me->x)
            + abs(field_he->y - field_me->y) * abs(field_he->y - field_me->y);
      dist1 = CELL_X(ACTOR_DISTANCE) * CELL_X(ACTOR_DISTANCE)
            + CELL_Y(ACTOR_DISTANCE) * CELL_Y(ACTOR_DISTANCE);
      if (dist0 <= dist1 && WAS_TIME(weapon, ACTOR_RECHARGE) &&
          (field_me->weapon->actor->type & ACTOR_WEAPON_SHOOT) == ACTOR_WEAPON_SHOOT) {
         threat = field_he;
         opposite_ok = 1;
      }
      dist1 = CELL_X(2) * CELL_X(2) + CELL_Y(2) * CELL_Y(2);
      if (FIELD_SKILL < FIELD_SKILL_EASY &&
          dist0 <= dist1 && WAS_TIME(weapon, ACTOR_RECHARGE) &&
          (field_me->weapon->actor->type & ACTOR_WEAPON_SHOOT) != ACTOR_WEAPON_SHOOT) {
         threat = field_he;
         opposite_ok = 1;
      }
   }

   if (threat == NULL)
      return 0;

   for (i = 0; i < 1000; i++) {         /* retry at most this many times */
      cmd->f.dir = STATE_MOVE_UP + (random() % (STATE_MOVE_COUNT - 1));
      if (cmd->f.dir == state ||
          (!opposite_ok && cmd->f.dir == state_opposite[state]))
         continue;
      x = field_me->x + CELL_X(state_move_x_step[cmd->f.dir]);
      y = field_me->y + CELL_Y(state_move_y_step[cmd->f.dir]);
      if (x < CELL_X(1) || x > CELL_X(FIELD_XCELLS - 2) ||
          y < CELL_Y(1) || y > CELL_Y(FIELD_YCELLS - 2))
         continue;
      dist1 = abs(threat->x - x) * abs(threat->x - x)
            + abs(threat->y - y) * abs(threat->y - y);
      if (dist1 > dist0)
         break;
   }

   cmd->f.fire = 0;
   return 1;
}

/*--------------------------------------------------------------------------*/
/* computer_field_move                                                      */
/*--------------------------------------------------------------------------*/

void computer_field_move(COMMAND *cmd, int do_move)
{
   FIELD_ACTOR *weapon;
   int ok;
   int x, y;

   weapon = field_he->weapon;

   if (do_move) {                       /* if no move has been made yet */
      cmd->f.fire = 0;
      if (FIELD_SKILL < FIELD_SKILL_MEDIUM)
         ok = WAS_TIME(weapon, weapon->actor->recharge / 2);
      else
         ok = 1;                        /* always maintain a line of fire */
      if (field_frame_time % FIELD_SKILL == 0 && ok)
         cmd->f.dir = computer_field_direction(field_he->x, field_he->y);
      else {
         cmd->f.dir = random() % (STATE_MOVE_COUNT + FIELD_SKILL);
         if (cmd->f.dir >= STATE_MOVE_COUNT)
            cmd->f.dir = field_me->last_state;
      }
   } else
      /* if a fire move has been made on easy/medium skill levels */
      if (FIELD_SKILL >= FIELD_SKILL_MEDIUM && cmd->f.fire == 1)
         return;

   /* avoid running into the path of the enemy's missile */

   if ((weapon->actor->type & ACTOR_WEAPON_SHOOT) != ACTOR_WEAPON_SHOOT ||
       weapon->state == 0)
      return;

   if (field_frame_time % (random() % (FIELD_SKILL / 10 + 1) + 1) != 0)
      return;

   /* if weapon is going to hit us */
   x = field_me->x;
   y = field_me->y;
   field_me->x += CELL_X(state_move_x_step[cmd->f.dir]);
   field_me->y += CELL_Y(state_move_y_step[cmd->f.dir]);
   if (!computer_field_collision_course(field_me, weapon, weapon->state)) {
      field_me->x = x;
      field_me->y = y;
      return;
   }

   while (1) {
      field_me->x = x + CELL_X(state_move_x_step[cmd->f.dir]);
      field_me->y = y + CELL_Y(state_move_y_step[cmd->f.dir]);
      if (!computer_field_collision_course(field_me, weapon, weapon->state))
         break;
      cmd->f.dir = STATE_MOVE_UP + (random() % (STATE_MOVE_COUNT - 1));
   }
   cmd->f.fire = 0;
   field_me->x = x;
   field_me->y = y;
}

/*--------------------------------------------------------------------------*/
/* computer_field_collision_course                                          */
/*--------------------------------------------------------------------------*/

int computer_field_collision_course(FIELD_ACTOR *target, FIELD_ACTOR *source, int dir)
{
   int dx, dy;
   int x, y;
   char rock;

   dx = CELL_X(state_move_x_step[dir]);
   dy = CELL_Y(state_move_y_step[dir]);
   x = source->x;
   y = source->y;
   while (x >= 0 && x < CELL_X(FIELD_XCELLS) &&
          y >= 0 && y < CELL_Y(FIELD_YCELLS)) {
      if (field_collision(x, y, target->x, target->y))
         return 1;
      rock = field_cells[y / CELL_YSIZE][x / CELL_XSIZE];
      if ((rock >> ROCK_IX_SHIFT) != -1 &&
          (rock & ROCK_LUMI_MASK) < ROCK_WALKABLE)
         break;
      x += dx;
      y += dy;
   }
   return 0;
}

/*--------------------------------------------------------------------------*/
/* computer_field_direction                                                 */
/*--------------------------------------------------------------------------*/

int computer_field_direction(int x1, int y1)
{
   int dir = 0;
   int x0, y0;

   x0 = field_me->x / CELL_XSIZE;
   y0 = field_me->y / CELL_YSIZE;
   x1 /= CELL_XSIZE;
   y1 /= CELL_YSIZE;

   if (x0 == x1 && y0 == y1)
      return 0;

   if (x0 == x1) {
      if (y0 > y1) {
         dir = STATE_MOVE_UP;
         y0 = max(0, y0 - 1);
         if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE)
            dir = (random() % 2 == 0) ? STATE_MOVE_UP_LEFT : STATE_MOVE_UP_RIGHT;
      } else {
         dir = STATE_MOVE_DOWN;
         y0 = min(FIELD_YCELLS - 1, y0 + 1);
         if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE)
            dir = (random() % 2 == 0) ? STATE_MOVE_DOWN_LEFT : STATE_MOVE_DOWN_RIGHT;
      }
      return dir;
   }

   if (y0 == y1) {
      if (x0 > x1) {
         dir = STATE_MOVE_LEFT;
         x0 = max(0, x0 - 1);
         if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE)
            dir = (random() % 2 == 0) ? STATE_MOVE_UP_LEFT : STATE_MOVE_DOWN_LEFT;
      } else {
         dir = STATE_MOVE_RIGHT;
         x0 = min(FIELD_XCELLS - 1, x0 + 1);
         if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE)
            dir = (random() % 2 == 0) ? STATE_MOVE_UP_RIGHT : STATE_MOVE_DOWN_RIGHT;
      }
      return dir;
   }

   if (x0 > x1) {
      x0 = max(0, x0 - 1);
      if (y0 > y1) {
         dir = STATE_MOVE_UP_LEFT;
         y0 = max(0, y0 - 1);
         if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE)
            dir = (random() % 2 == 0) ? STATE_MOVE_UP : STATE_MOVE_LEFT;
      } else {
         dir = STATE_MOVE_DOWN_LEFT;
         y0 = min(FIELD_YCELLS - 1, y0 + 1);
         if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE)
            dir = (random() % 2 == 0) ? STATE_MOVE_DOWN : STATE_MOVE_LEFT;
      }
      return dir;
   }

   if (x0 < x1) {
      x0 = min(FIELD_XCELLS - 1, x0 + 1);
      if (y0 > y1) {
         dir = STATE_MOVE_UP_RIGHT;
         y0 = max(0, y0 - 1);
         if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE)
            dir = (random() % 2 == 0) ? STATE_MOVE_UP : STATE_MOVE_RIGHT;
      } else {
         dir = STATE_MOVE_DOWN_RIGHT;
         y0 = min(FIELD_YCELLS - 1, y0 + 1);
         if ((field_cells[y0][x0] >> ROCK_IX_SHIFT) != ROCK_NONE)
            dir = (random() % 2 == 0) ? STATE_MOVE_DOWN : STATE_MOVE_RIGHT;
      }
      return dir;
   }

   fprintf(stderr, "computer_field_direction():  i should never get here\n");
   return 0;
}

/*--------------------------------------------------------------------------*/
/* computer_field_stop                                                      */
/*--------------------------------------------------------------------------*/

void computer_field_stop(void)
{
   int light, dark;
   DAMAGE *damage;

   /* unlike other computer...() functions, this one isn't side-dependant: */
   /* whenever it is invoked, field_me is light, and field_he is dark. */

   light = field_me->orig_actor->type & ACTOR_MASK;
   if (light >= ACTOR_AIR_ELEM && light <= ACTOR_WATER_ELEM)
      light = ACTOR_NUM_LIGHT;          /* use last entry in stats array */
   else
      light -= ACTOR_LIGHT_FIRST;       /* otherwise get index for entry */

   dark = (field_he->orig_actor->type & ACTOR_MASK);
   if (dark >= ACTOR_AIR_ELEM && dark <= ACTOR_WATER_ELEM)
      dark = ACTOR_NUM_DARK;            /* use last entry in stats array */
   else
      dark -= ACTOR_DARK_FIRST;         /* otherwise get index for entry */

   damage = &computer_damage[light][dark];
   damage->light_sum += field_he->orig_life - field_he->life;
   damage->dark_sum += field_me->orig_life - field_me->life;
   damage->light_hits += field_me->num_hits;
   damage->dark_hits += field_he->num_hits;
   damage->count++;
}

/*--------------------------------------------------------------------------*/
/* computer_field_score                                                     */
/*--------------------------------------------------------------------------*/

int computer_field_score(ACTOR *attacker, ACTOR *defender,
                         int *attacker_damage, int *defender_damage,
                         int *attacker_hits, int *defender_hits)
{
   int light, dark;
   DAMAGE *damage;

   if (attacker->type & ACTOR_LIGHT) {
      light = attacker->type & ACTOR_MASK;
      dark = defender->type & ACTOR_MASK;
   } else {
      light = defender->type & ACTOR_MASK;
      dark = attacker->type & ACTOR_MASK;
   }

   if (light >= ACTOR_AIR_ELEM && light <= ACTOR_WATER_ELEM)
      light = ACTOR_NUM_LIGHT;          /* use last entry in stats array */
   else
      light -= ACTOR_LIGHT_FIRST;       /* otherwise get index for entry */

   if (dark >= ACTOR_AIR_ELEM && dark <= ACTOR_WATER_ELEM)
      dark = ACTOR_NUM_DARK;            /* use last entry in stats array */
   else
      dark -= ACTOR_DARK_FIRST;         /* otherwise get index for entry */

   damage = &computer_damage[light][dark];
   if (damage->count == 0) {
      *attacker_damage = 0;
      *defender_damage = 0;
      *attacker_hits = 1;
      *defender_hits = 1;
   } else if (attacker->type & ACTOR_LIGHT) {
      *attacker_damage = damage->light_sum / damage->count;
      *defender_damage = damage->dark_sum / damage->count;
      *attacker_hits = max(1, damage->light_hits);
      *defender_hits = max(1, damage->dark_hits);
   } else {
      *attacker_damage = damage->dark_sum / damage->count;
      *defender_damage = damage->light_sum / damage->count;
      *attacker_hits = max(1, damage->dark_hits);
      *defender_hits = max(1, damage->light_hits);
   }
   return damage->count;
}

/*--------------------------------------------------------------------------*/
/* computer_config_read                                                     */
/*--------------------------------------------------------------------------*/

void computer_config_read(FILE *fp, COMPUTER_CONFIG *config)
{
   char *path;
   FILE *fp1;
   int i, j;
   DAMAGE *damage;

   if (fp != NULL) {
      fscanf(fp, "%32s %d", config->rules, &config->old_board_mode);
      if (strcmp(config->rules, "easy") == 0)
         config->data_num = 0;
      else if (strcmp(config->rules, "medium") == 0)
         config->data_num = 1;
      else if (strcmp(config->rules, "hard") == 0)
         config->data_num = 2;
      else {
         fprintf(stderr, "computer_config_read():  unknown rules `%s'\n", config->rules);
         exit(EXIT_FAILURE);
      }
   }

   if (!computer_damage_ok) {
      path = malloc(PATH_MAX);
      sprintf(path, "%s/statistics", DATADIR);
      fp1 = fopen(path, "r");
      if (fp1 == NULL) {
         fprintf(stderr, "statistics file `%s' not found\n", path);
         exit(EXIT_FAILURE);
      }
      for (i = 0; i < ACTOR_NUM_LIGHT + 1; i++)
         for (j = 0; j < ACTOR_NUM_DARK + 1; j++) {
            damage = &computer_damage[i][j];
            if (fscanf(fp1, "%d %d %d %d %d",
                       &damage->light_sum, &damage->dark_sum,
                       &damage->light_hits, &damage->dark_hits,
                       &damage->count) == EOF) {
               fprintf(stderr, "computer_config_read():  not enough statistics\n");
               exit(EXIT_FAILURE);
            }
         }
      fclose(fp1);
      free(path);
      computer_damage_ok = 1;
   }
}

/*--------------------------------------------------------------------------*/
/* computer_config_write                                                    */
/*--------------------------------------------------------------------------*/

void computer_config_write(FILE *fp, COMPUTER_CONFIG *config)
{
   fprintf(fp, "%-32s %d\n",
           (config->rules[0] == 0) ? "easy" : config->rules,
           config->old_board_mode);

#ifdef AUTOPILOT
   { int i, j;
   for (i = 0; i < ACTOR_NUM_LIGHT + 1; i++) {
      for (j = 0; j < ACTOR_NUM_DARK + 1; j++)
         printf("%5d %5d %5d %5d %5d\n",
                 computer_damage[i][j].light_sum,
                 computer_damage[i][j].dark_sum,
                 computer_damage[i][j].light_hits,
                 computer_damage[i][j].dark_hits,
                 computer_damage[i][j].count);
      printf("\n");
   }
   }
#endif
}

/****************************************************************************/
/*                                                                          */
/* GTK+ Stuff                                                               */
/*                                                                          */
/****************************************************************************/

#include <gtk/gtk.h>
#include "gtk-callbacks.h"
#include "gtk-interface.h"
#include "gtk-support.h"

/*--------------------------------------------------------------------------*/
/* variables                                                                */
/*--------------------------------------------------------------------------*/

static COMPUTER_CONFIG *orig_config, work_config;
static GtkWidget *window;

/*--------------------------------------------------------------------------*/
/* computer_config_edit                                                     */
/*--------------------------------------------------------------------------*/

void computer_config_edit(COMPUTER_CONFIG *_config)
{
   GtkWidget *widget;

   orig_config = _config;
   memcpy(&work_config, orig_config, sizeof(COMPUTER_CONFIG));
   window = create_computer_window();
   widget = lookup_widget(window, work_config.rules);
   if (widget != NULL)
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
   if (orig_config->old_board_mode) {
      widget = lookup_widget(window, "old_board_mode");
      gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(widget), TRUE);
   }
   gtk_widget_show(window);
}

/*--------------------------------------------------------------------------*/
/* computer_skill_toggled                                                   */
/*--------------------------------------------------------------------------*/

void computer_skill_toggled(GtkToggleButton *button, gpointer data)
{
   strcpy(work_config.rules, data);
}

/*--------------------------------------------------------------------------*/
/* computer_ok_clicked                                                      */
/*--------------------------------------------------------------------------*/

void computer_ok_clicked(GtkButton *button, gpointer data)
{
   GtkWidget *widget;

   memcpy(orig_config, &work_config, sizeof(COMPUTER_CONFIG));
   widget = lookup_widget(window, "old_board_mode");
   orig_config->old_board_mode =
      gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
   gtk_widget_destroy(window);
}

/*--------------------------------------------------------------------------*/
/* computer_cancel_clicked                                                  */
/*--------------------------------------------------------------------------*/

void computer_cancel_clicked(GtkObject *object, gpointer data)
{
   gtk_widget_destroy(window);
}

Generated by  Doxygen 1.6.0   Back to index