From: alexs@tss.com (Alex Siegel)
Subject: A very good tank program
Keywords: program,robot,source
Reply-To: alexs%tss.com@uunet.uu.net
Organization: Teknekron Software Systems/Manufacturing Group
Date: Sun, 22 Nov 1992 22:47:36 GMT

Here is a very good tank program.  I would be interested if anybody
can come up with one which is clearly superior for any vehicle.  The
save file for the vehicle that I use is at the bottom of this file.

			Alex Siegel

/*
 ** Xtank
 **
 ** bullseye.c
 */

#include <xtanklib.h>
#include <sysdep.h>
#include <lowlib.h>
#include <stdio.h>
#include <math.h>

static void main();

Prog_desc bullseye_prog = {
  "bullseye",
  "Ideal",
  "Acts as a very accurate gunner and avoids bullets.",
  "alexs%tss.com@uunet.uu.net (Alex Siegel)",
  PLAYS_COMBAT | DOES_SHOOT | USES_TEAMS | USES_SIDE_MOUNTS | DOES_DODGE,
  8,
  main
};

typedef struct {
  int numweaps;
  Weapon_info winfo[MAX_WEAPONS];
  struct hitinfo_s {
    enum {
      HITNOWAY,		/* Target is too fast or impossible */
      HITTOOFAR,	/* Target is too far, but may get closer */
      HITOK		/* Target can be hit */
    } canhit;
    FLOAT px, py;
  } hitinfo[MAX_WEAPONS];
} bullseye_info;

static int bullseye_find(me, target)
Vehicle_info *me, *target;
{
  Vehicle_info vinfo[MAX_VEHICLES];
  int num_vinfos;
  Vehicle_info *v;
  int dx, dy, range, min_range;
  int i;
  
  get_vehicles(&num_vinfos, vinfo);
  
  /* * Find the closest vehicle with a clear path to it. */
  min_range = 99999999;
  for (i = 0; i < num_vinfos; i++) {
    v = &vinfo[i];
    
    /* ignore vehicles on the same team */
    if(me->team == v->team)
      continue;
    /* ignore vehicles that I can't get to */
    if(! clear_path(&(v->loc), &(me->loc)))
      continue;
    
    dx = v->loc.x - me->loc.x;
    dy = v->loc.y - me->loc.y;
    range = dx * dx + dy * dy;
    if(range < min_range) {
      min_range = range;
      *target = *v;
    } /* if */
  } /* for */

  return (min_range != 99999999);
}

static void bullseye_shoot(wlist, me, target)
bullseye_info *wlist;
Vehicle_info *me, *target;
{
  FLOAT dx, dy, dvx, dvy, ws, t, a, b, c, d;
  int j;
  Weapon_info *wi;
  struct hitinfo_s *hi;
  MountLocation tnum;
  Angle ang;
  Location pos;

  /* Initialize the hit information on the weapons */
  for(j = 0; j < wlist->numweaps; ++j) {
    /* Get the hitting information */
    hi = &(wlist->hitinfo[j]);
    hi->canhit = HITNOWAY;
  } /* for */

  /* Go through all of the weapons and compute leading target positions */
  for(j = 0; j < wlist->numweaps; ++j) {
    /* Get the weapons info */
    wi = &(wlist->winfo[j]);
    /* Get the relative position */
    dx = target->loc.x - me->loc.x;
    dy = target->loc.y - me->loc.y;
    /* Get the relative velocity */
    dvx = target->xspeed - me->xspeed;
    dvy = target->yspeed - me->yspeed;
    /* Get the weapons speed */
    ws = wi->ammo_speed;
    
    /* Compute the time to impact */
    a = dvx*dvx + dvy*dvy - ws*ws;	/* Coefficient of t^2 */
    b = 2.0*(dvx*dx + dvy*dy);		/* Coefficient of t */
    c = dx*dx + dy*dy;			/* Constant */
    d = b*b - 4.0*a*c;
    if(d < 0)
      continue;				/* Target is too fast */
    d = sqrt((double) d);
    t = (-b - d)/(2.0*a);
    if(t < 0)
      t = (-b + d)/(2.0*a);
    if(t < 0)
      continue;				/* Target is too fast */

    /* Get the hitting information */
    hi = &(wlist->hitinfo[j]);
    /* Compute the target position and check if the target is too far away */
    hi->canhit = (t > (float) wi->frames ? HITTOOFAR : HITOK);
    hi->px = dx + t*dvx;
    hi->py = dy + t*dvy;
  } /* for */
  
  /* Go through all of the mounts */
  for(tnum = MOUNT_TURRET1; tnum < NUM_MOUNTS; tnum++) {

    /* Look for the first good weapon on this turret to get a target */
    /* position from */
    for(j = 0; j < wlist->numweaps; ++j) {
      /* Check the turrent number */
      wi = &(wlist->winfo[j]);
      if(wi->mount != tnum)
	continue;
      /* Check the hit information */
      hi = &(wlist->hitinfo[j]);
      if(hi->canhit != HITNOWAY)
	break;
    } /* for */
    if(j == wlist->numweaps)
      continue;

    /* Aim the turret if it is a turret, otherwise just compute the */
    /* desired angle. */
    if(IS_TURRET(tnum)) {
      /* Aim the turret at the position computed for this weapon */
      ang = aim_turret(tnum, (int) hi->px, (int) hi->py);
      /* Get the difference in remaining angle */
      ang -= me->turret_angle[tnum];
    } /* if */

    else {
      /* Compute the angle to target */
      ang = atan2((double) hi->py, (double) hi->px);
      /* Get the difference from the desired angle and the weapon */
      /* angle */
      if(tnum == MOUNT_FRONT)
	ang -= me->heading;
      else if(tnum == MOUNT_BACK)
	ang -= me->heading + PI;
      else if(tnum == MOUNT_LEFT)
	ang -= me->heading - PI/2.0;
      else if(tnum == MOUNT_RIGHT)
	ang -= me->heading + PI/2.0;
      else
	continue;
    } /* else */

    /* Normalize the angular difference */
    if(ang < 0)
      ang = -ang;
    while(ang > 2.0*PI)
      ang -= 2.0*PI;
    if(ang > PI)
      ang = 2.0*PI - ang;

    /* Check the remaining angle */
    if(ang > 0.2) {
      /* Turn off all weapons on this mount if the angle is too large */
      for(j = 0; j < wlist->numweaps; ++j) {
	/* Check the turrent number */
	wi = &(wlist->winfo[j]);
	if(wi->mount != tnum)
	  continue;
	/* Check the hit information */
	hi = &(wlist->hitinfo[j]);
	if(hi->canhit == HITOK)
	  /* Do not fire this weapon */
	  hi->canhit = HITTOOFAR;
      } /* for */
    } /* if */
  } /* for */

  /* Shoot all weapons that can hit */
  for(j = 0; j < wlist->numweaps; ++j) {
    /* Get the hitting information */
    hi = &(wlist->hitinfo[j]);
    if(hi->canhit == HITOK)
      /* Shoot the weapon */
      fire_weapon(j);
  } /* for */
}

static int bullseye_dodge(me)
Vehicle_info *me;
{
  int i, numb;
  Bullet_info *cur, binfo[MAX_BULLETS];

  get_bullets(&numb, binfo);
  for(i=0; i < numb; ++i) {
    FLOAT px, py, vx, vy, fx, fy, t;
    Angle ang;
    
    cur = &(binfo[i]);
    px = cur->loc.x - me->loc.x;
    py = cur->loc.y - me->loc.y;
    vx = cur->xspeed - me->xspeed;
    vy = cur->yspeed - me->yspeed;
    
    if(vx == 0.0 && vy == 0.0)
      continue;
    t = -(px*vx + py*vy)/(vx*vx + vy*vy);
    if(t <= 2.0 || t > 20.0)
      continue;
    fx = px + t*vx;
    fy = py + t*vy;
    if(fx*fx + fy*fy >
       (float) ((me->bwidth * me->bwidth + me->bheight * me->bheight)/4))
      continue;

    if(! clear_path(&(cur->loc), &(me->loc)))
      continue;

    if(fx == 0.0 && fy == 0.0)
      ang = me->heading + PI/2.0;
    else
      ang = ATAN2(-fy, -fx);

    turn_vehicle(ang);
    set_rel_drive((FLOAT) 9.0);
    return(1);
  } /* for */
  return(0);
} /* bullseye_dodge */

static void delta_loc(locp, delta_x, delta_y)
Location *locp;					/* gets modified */
int delta_x, delta_y;			/* in fine units */
{
  locp->x += delta_x;
  locp->y += delta_y;
  locp->grid_x = locp->x / BOX_WIDTH;
  locp->box_x = locp->x % BOX_WIDTH;
  locp->grid_y = locp->y / BOX_HEIGHT;
  locp->box_y = locp->y % BOX_HEIGHT;
}

static int bullseye_avoid(me)
Vehicle_info *me;
{
  Location loc;
  FLOAT dx, dy, cs, sn;
  
  cs = cos((double) me->heading);
  sn = sin((double) me->heading);

  dx = cs * 80.0 + sn * 20.0;
  dy = sn * 80.0 - cs * 20.0;
  loc = me->loc;
  delta_loc(&loc, (int) dx, (int) dy);
  if(! clear_path(&loc, &(me->loc))) {
    turn_vehicle(me->heading + PI/4.0);
    return(1);
  } /* if */

  dx = cs * 80.0 - sn * 20.0;
  dy = sn * 80.0 + cs * 20.0;
  loc = me->loc;
  delta_loc(&loc, (int) dx, (int) dy);
  if(! clear_path(&loc, &(me->loc))) {
    turn_vehicle(me->heading - PI/4.0);
    return(1);
  } /* if */

  return(0);
} /* bullseye_avoid */

static void main()
{
  Vehicle_info me, enemy;
  Location myloc;
  bullseye_info wlist;
  int i, n;
  enum {
    WANDER, CHASE, DODGE, AVOID
  } state = WANDER;
  int count = 0;
  Angle ang;

  /* Start moving right away */
  set_rel_drive((FLOAT) 9.0);
  
  /* Find out info about weapons */
  get_self(&me); 
  wlist.numweaps = n = num_weapons();
  for(i = 0; i < n; i++)
    get_weapon(i, &(wlist.winfo[i]));
  
  /* Loop forever */
  while(1) {
    /* find the nearest enemy */
    if(bullseye_find(&me, &enemy)) {
      bullseye_shoot(&wlist, &me, &enemy);
      if(state == WANDER)
	state = CHASE;
    } /* if */
    else if(state == CHASE)
      state = WANDER;

    switch(state) {
    case CHASE:
    case WANDER:
      if(bullseye_avoid(&me)) {
	state = AVOID;
	count = 3;
	break;
      } /* if */

      if(bullseye_dodge(&me)) {
	state = DODGE;
	count = 10;
	break;
      } /* if */

      if(state == CHASE) {
	ang = ATAN2((enemy.loc.y - me.loc.y), (enemy.loc.x - me.loc.x));
	turn_vehicle(ang);
      } /* if */
      break;
      
    case DODGE:
      if(bullseye_avoid(&me)) {
	state = AVOID;
	count = 3;
	break;
      } /* if */

    case AVOID:
      count --;
      if(count <= 0)
	state = WANDER;
    } /* switch */
    
    /* Give up remaining cpu time to improve speed of game */
    done();

    /* Get a new copy of my information */
    get_self(&me);
  } /* while */
} /* main */


Vehicle: Ideal
Designer: alex
Body: Hexo
Engine: Super Combustion
W#0: T1 Heavy Machine Gun
W#1: T1 Heavy Machine Gun
W#2: T1 Heavy Machine Gun
W#3: T1 Heavy Machine Gun
Armor Type: Compound Steel
Front: 11
Back: 10
Left: 19
Right: 19
Top: 3
Bottom: 4
Special: Console
Special: Mapper
Special: Radar
Special: Navigation
Heat sinks: 1
Suspension: Normal
Treads: Smooth
Bumpers: Rubber