/* dragonfly robot for xtank * known to work with version 1.3f * Copyright (C) 1992 David B. Wilson * * This program 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; version 2 of the License. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include "xtanklib.h" #include /* Bug in xtank that I need to compensate for here */ #include "vehicle.h" extern Vehicle *cv; #define aRmor(num) (cv->armor.side[(int)(num)]) #define max_aRmor(num) (cv->vdesc->armor.side[(int)(num)]) extern int dragonfly_main(); Prog_desc dragonfly_prog = { "dragonfly", "H2SO4", "Seeks out, rams and fires", "David B Wilson", PLAYS_COMBAT | DOES_SHOOT | DOES_EXPLORE, 6, dragonfly_main }; typedef struct { int xy; int value; } GridBox; typedef struct { int grid_x,grid_y; int time; /* When information last updated */ int trust; /* How much we trust the blip */ } Target; #define MaxTargets (MAX_VEHICLES+2*MAX_BLIPS+1) typedef struct { int mode; int wall_blast_cost; Vehicle_info self; Weapon_info weapons[MAX_WEAPONS]; Vehicle_info enemy; Location myloc; int target_nature; int number_targets; Target targets[MaxTargets]; int number_blips; Target blips[MaxTargets]; /* Space for output from the navigator */ int dir1,dir2,dirlast; /* Scratch space for the navigator */ int distances[GRID_WIDTH][GRID_HEIGHT]; int heap_index[GRID_WIDTH][GRID_HEIGHT]; int entered_to[GRID_WIDTH][GRID_HEIGHT]; GridBox boxes[GRID_WIDTH*GRID_HEIGHT+1]; } All; #define TARGET_NONE 0 #define TARGET_RANDOM 1 #define TARGET_RADAR 2 #define TARGET_ARMOR 3 #define Urgent (1+4+16) #define Armor 3 #define Ammo 12 #define Fuel 48 #define Visual 192 #define VisualNow 64 #define VisualLast 128 #define Refurbish (256+512+1024) #define RefurbishArmor 256 #define RefurbishAmmo 512 #define RefurbishFuel 1024 #define North 0 #define East 1 #define South 2 #define West 3 #define NorthEast 4 #define SouthEast 5 #define SouthWest 6 #define NorthWest 7 #define Stay 8 #define Fire 16 #define Compass 15 static short CompassX[] = { 0, /* N */ 1, /* E */ 0, /* S */ -1, /* W */ 1, /* NE */ 1, /* SE */ -1, /* SW */ -1, /* NW */ 0 /* . */ }; static short CompassY[] = { -1, /* N */ 0, /* E */ 1, /* S */ 0, /* W */ -1, /* NE */ 1, /* SE */ 1, /* SW */ -1, /* NW */ 0 /* . */ }; #define Turn(Dx,Dy) turn_vehicle(ATAN2((Dy),(Dx))) #define Turn2(Dx,Dy,Da) turn_vehicle(ATAN2((Dy),(Dx))+(Da)) #define Mode (allp->mode) #define Dir1 (allp->dir1) #define Dir2 (allp->dir2) #define DirLast (allp->dirlast) #define Self (allp->self) #define Myloc (allp->myloc) #define TargetNature (allp->target_nature) #define NumberTargets (allp->number_targets) #define Targets (allp->targets) #define NumberBlips (allp->number_blips) #define Blips (allp->blips) #define Enemy (allp->enemy) #define Weapons (allp->weapons) #define WallBlastCost (allp->wall_blast_cost) static void cleanup (allp) All *allp; { free((char*)allp); } dragonfly_main() { All *allp; int dx,dy; double r,angle,deviate; int i; /* generic loop iteration */ allp = (All*)malloc(sizeof(All)); if (!allp) dragonfly_problem("No memory!"); set_cleanup_func(cleanup,(void*)allp); get_self(&Self); /* Learn about myself */ for (i=0; i 10) set_rel_drive(4.0); else set_rel_drive(0.0); switch (landmark(Myloc.grid_x,Myloc.grid_y)) { case ARMOR: for (flag=1,i=FRONT; i<=BOTTOM; ++i) flag = flag && (aRmor(i) == max_aRmor(i)); if (flag) Mode &= ~Refurbish; break; case FUEL: if (fuel() == max_fuel()) Mode &= ~Refurbish; break; case AMMO: for (flag=1,i=0; i 0) WallBlastCost = 60; } Mode &= ~Fuel; } static dragonfly_conspire (allp) All *allp; { Message msg; Byte data[2]; if (Self.team == NEUTRAL) return; data[0] = Myloc.grid_x; data[1] = Myloc.grid_y; /* send_msg(MAX_VEHICLES+Self.team,OP_LOCATION,data); */ while (receive_msg(&msg)) if (msg.sender_team == Self.team); } static dragonfly_passable (allp,angle) All *allp; double angle; { Location start,finish; start.x = Myloc.x + 12*sin(angle); start.y = Myloc.y - 12*cos(angle); finish.x = start.x + max_speed()*TICKSZ*cos(angle); finish.y = start.y + max_speed()*TICKSZ*sin(angle); start.grid_x = start.x / BOX_WIDTH; start.grid_y = start.y / BOX_HEIGHT; finish.grid_x = finish.x / BOX_WIDTH; finish.grid_y = finish.y / BOX_HEIGHT; start.box_x = start.x % BOX_WIDTH; start.box_y = start.y % BOX_HEIGHT; finish.box_x = finish.x % BOX_WIDTH; finish.box_y = finish.y % BOX_HEIGHT; if (!clear_path(&start,&finish)) return 0; start.x = Myloc.x - 12*sin(angle); start.y = Myloc.y + 12*cos(angle); finish.x = start.x + max_speed()*TICKSZ*cos(angle); finish.y = start.y + max_speed()*TICKSZ*sin(angle); start.grid_x = start.x / BOX_WIDTH; start.grid_y = start.y / BOX_HEIGHT; finish.grid_x = finish.x / BOX_WIDTH; finish.grid_y = finish.y / BOX_HEIGHT; start.box_x = start.x % BOX_WIDTH; start.box_y = start.y % BOX_HEIGHT; finish.box_x = finish.x % BOX_WIDTH; finish.box_y = finish.y % BOX_HEIGHT; if (!clear_path(&start,&finish)) return 0; return 1; } static dragonfly_num_nasties (allp, num_bullets, bullets, angle) All *allp; int num_bullets; Bullet_info *bullets; double angle; { int i, num_nasties=0; double sina,cosa,xspeed,yspeed,dx,dy,bx,by,bvx,bvy; sina = sin(angle); cosa = cos(angle); xspeed = max_speed()*cosa; yspeed = max_speed()*sina; for (i=0; i0.0 && bx<200.0 && by>-13.0 && by<13.0 && bvx<0.0) ++num_nasties; break; default: /* Not too nasty */ break; } return num_nasties; } #define NDEV 5 static double deviations[] = {0.0,0.6,-0.6,1.2,-1.2}; dragonfly_deviate (allp, angle, bite, deviate, obst) All *allp; int bite; /* How many bullets are we willing to bite? */ double angle, *deviate; int obst; /* Should we check for obstacles? */ { int i, num_bullets; Bullet_info bullets[MAX_BULLETS]; get_bullets(&num_bullets,bullets); /* Where is the ammo */ /* Try to find a good direction */ for (i=0; i 0) --Blips[i].trust; /* Record new blips */ get_blips(&num_blips,blips); for (i=0; i 0) { TargetNature = TARGET_RADAR; NumberTargets = j; } else if (TargetNature != TARGET_RANDOM) { TargetNature = TARGET_RANDOM; NumberTargets = 1; Targets[0].grid_x = random() % GRID_WIDTH; Targets[0].grid_y = random() % GRID_HEIGHT; Targets[0].time = frame_number(); } } /* If trouble, describe it and give up */ dragonfly_problem (str) char *str; { send_msg(RECIPIENT_ALL, OP_TEXT, str); while (1) done(); } /* Returns a lower bound / estimate on the distance from the grid * box xc,yc to one of the targets. Satisfies the requirements of * dragonfly_navigate, namely if xc,yc is one of the targets, returns 0, * and the difference in the returned value at two points lower bounds * the distance between the points. * Units are 10 per grid box. */ static dragonfly_estimate (allp, xc, yc) All *allp; int xc, yc; { int i,dist,mindist=Infinity; for (i=0; idistances[xc][yc]) #define EnteredTo(xc,yc) (allp->entered_to[xc][yc]) #define HeapIndex(xc,yc) (allp->heap_index[xc][yc]) #define Boxes (allp->boxes) #define UpdateIndex(ind) (allp->heap_index[0][Boxes[ind].xy] = (ind)) #define CurrentX (allp->myloc.grid_x) #define CurrentY (allp->myloc.grid_y) #define Estimate(xc,yc) (dragonfly_estimate(allp,(xc),(yc))) dragonfly_navigate (allp) All *allp; { GridBox ebox, tbox; int x0,y0,x1,y1,number_boxes,number_expanded=0; int i,child; int edge_weight; int dir; /* Sanity check */ if (NumberTargets <=0 || NumberTargets > MaxTargets) dragonfly_problem("Bad number of targets!"); x0 = CurrentX; y0 = CurrentY; Boxes[1].xy = x0*GRID_HEIGHT + y0; /* Put first element into heap */ Boxes[1].value = Estimate(x0,y0); Distances(x0,y0) = 0; EnteredTo(x0,y0) = Stay; number_boxes = 1; UpdateIndex(1); while (1) { if (number_boxes == 0) { /* Target is unreachable */ Dir1 = Dir2 = DirLast = Stay; dragonfly_navigate_reinitialize(allp,number_boxes,number_expanded); return 0; } ebox = Boxes[1]; /* Grab the smallest element */ x0 = ebox.xy / GRID_HEIGHT; y0 = ebox.xy % GRID_HEIGHT; if (Estimate(x0,y0)==0) /* Maybe we're done */ break; Boxes[GRID_WIDTH*GRID_HEIGHT - number_expanded++] = ebox; tbox = Boxes[number_boxes--]; /* Maintain heap property */ for (i=1; 2*i<=number_boxes;) { /* Float down */ if (2*i==number_boxes || Boxes[2*i].value < Boxes[2*i+1].value) child = 2*i; else child = 2*i+1; if (tbox.value >= Boxes[child].value) { Boxes[i] = Boxes[child]; UpdateIndex(i); i=child;} else break; /* done floating hole down */ } Boxes[i] = tbox; UpdateIndex(i); for (dir=North; dir<=West; dir = (dir&Compass)+1) { x1 = x0 + CompassX[dir & Compass]; /* Find neighbor coordinates */ y1 = y0 + CompassY[dir & Compass]; if (map_off_grid(x1,y1)) continue; dir |= dragonfly_edge_weight(allp,x0,y0,dir,&edge_weight); /* Relax the edge and put in heap if not already there */ if (Distances(x0,y0)+edge_weight < Distances(x1,y1)) { if (Distances(x1,y1) < Infinity) i = HeapIndex(x1,y1); else i = ++number_boxes; Distances(x1,y1) = Distances(x0,y0)+edge_weight; EnteredTo(x1,y1) = dir; tbox.xy = x1*GRID_HEIGHT + y1; tbox.value = Distances(x1,y1) + Estimate(x1,y1); while (i>1 && tbox.value <= Boxes[i/2].value) { /* Float up */ Boxes[i] = Boxes[i/2]; UpdateIndex(i); i /= 2; } Boxes[i] = tbox; UpdateIndex(i); } } } /* Now we just follow the pointers back */ Dir1 = Stay; Dir2 = Stay; DirLast = EnteredTo(x0,y0); while ((dir = EnteredTo(x0,y0)) != Stay) { Dir2 = Dir1; Dir1 = dir; if (dir & Fire) DirLast = dir; x0 -= CompassX[dir & Compass]; y0 -= CompassY[dir & Compass]; } dragonfly_navigate_reinitialize(allp,number_boxes,number_expanded); return 1; } dragonfly_navigate_initialize (allp) All *allp; { int *distp,*distplim; distp = &Distances(0,0); /* Set distances to infinity */ distplim = &Distances(GRID_WIDTH-1,GRID_HEIGHT-1); while (distp <= distplim) *(distp++) = Infinity; } dragonfly_navigate_reinitialize (allp,number_boxes,number_expanded) All *allp; { int *distp,i; distp = &Distances(0,0); /* Set distances to infinity */ for (i=1; i<=number_boxes; ++i) distp[Boxes[i].xy] = Infinity; for (i=0; i