#include "MyStrategy.h"
#define _USE_MATH_DEFINES
#include <cmath>
using namespace model;
class MyTank;
MyTank *me = NULL;
#define MAX_FLOAT 1e6f
#define MAX_ITEMS 32
#define SHELL_VEL 16.7f
const float M_PIf = (float)M_PI;
const float M_2PIf = (float)M_PI*2.f;
float clampAngle(float angle)
{
float angle02Pi = fmod(angle + M_PIf, M_2PIf);
angle02Pi += angle02Pi > 0.f ? 0.f : M_2PIf;
float result = angle02Pi - M_PIf;
return result;
}
float min(float a, float b) { return a > b ? b : a; }
float max(float a, float b) { return a > b ? a : b; }
class vecf
{
public:
float x;
float y;
vecf(float X = 0, float Y = 0) { x = X; y = Y; };
~vecf() {} ;
vecf operator*(float scalar) const { return vecf(x * scalar, y * scalar); }
vecf operator/(float scalar) const { return vecf(x / scalar, y / scalar);}
vecf operator+(const vecf &vect) const { return vecf(x + vect.x, y + vect.y); }
vecf operator-(const vecf &vect) const { return vecf(x - vect.x, y - vect.y); }
bool operator>(const vecf &vect) const { return x > vect.x && y > vect.y; }
bool operator<(const vecf &vect) const { return x < vect.x && y < vect.y; }
float crossproduct(const vecf &vect2) const { return (x * vect2.y) - (y * vect2.x); }
float length() const { return sqrtf(x * x +y * y); }
float lengthSqare() const { return x*x+y*y; }
float dotproduct(const vecf &vect) const { return (x * vect.x) + (y * vect.y); }
void rotate(float angle)
{
float xt = x * cosf(-angle) + y * sinf(-angle);
float yt = - x * sinf(-angle) + y * cosf(-angle);
x = xt;
y = yt;
}
void normalise()
{
float mag = sqrtf(x* x + y * y);
if (fabsf(mag) < 1e-5f)
{
x=1.f;
y=0.f;
}
else
{
x /= mag;
y /= mag;
}
}
float angleTo(const vecf& b)
{
return clampAngle(atan2(b.y-y,b.x-x));
}
float globalAngle() const { return clampAngle(atan2(y,x)); }
};
vecf angleToDir(float angle)
{
vecf dir(1.f, 0.f);
dir.rotate(angle);
return dir;
}
bool intervalsIntersect(vecf &a, vecf &b)
{
if (a.y < b.x || a.x > b.y)
return false;
return true;
}
bool intervalsWillCollide(vecf &a, vecf &b, float aVel, vecf &outTime)
{
if (intervalsIntersect(a, b))
{
outTime.x = 0.f;
if (fabsf(aVel <= 1e-5f))
outTime.y = MAX_FLOAT;
if (aVel > 0.f)
outTime.y = (b.y - a.x) / aVel;
else
outTime.y = (b.x - a.y) / aVel;
return true;
}
if (a.y < b.x)
{
if (aVel <= 1e-5f)
return false;
outTime.x = (b.x - a.y) / aVel;
outTime.y = (b.y - a.x) / aVel;
return true;
}
// else a.x > b.y
if (aVel >= -1e-5f)
return false;
outTime.x = (b.y - a.x) / aVel;
outTime.y = (b.x - a.y) / aVel;
return true;
}
enum TracedObject
{
Nothing = 0,
Wall = 1 << 1,
FriendlyTank = 1 << 2,
EnemyTank = 1 << 3,
DeadTank = 1 << 4,
BonusHealth = 1 << 5,
BonusArmor = 1 << 6,
BonusWeapon = 1 << 7,
LightShell = 1 << 8,
HeavyShell = 1 << 9
};
struct Box
{
vecf pos;
vecf vel;
float angle;
vecf size;
// calculated
vecf fwdNorm;
vecf rightNorm;
vecf velNorm;
vecf points[4];
vecf fwdInterval;
vecf rightInterval;
void setVelOnly(vecf &_vel)
{
vel = _vel;
velNorm = vel;
velNorm.normalise();
}
void initHypotheticalBullet(vecf &_pos, float _angle)
{
pos = _pos;
vel = angleToDir(_angle) * SHELL_VEL;
angle = _angle;
size.x = 23.f;
size.y = 9.f;
calculateAll();
}
void init(Unit &unit)
{
pos.x = unit.x();
pos.y = unit.y();
vel.x = unit.speed_x();
vel.y = unit.speed_y();
angle = unit.angle();
size.x = unit.height();
size.y = unit.width();
calculateAll();
}
void init(vecf &_pos, vecf &_vel, float _angle, vecf &_size)
{
pos = _pos;
vel = _vel;
angle = _angle;
size = _size;
calculateAll();
}
void calculateAll()
{
fwdNorm = angleToDir(angle);
rightNorm = angleToDir(angle + M_PIf * 0.5f);
velNorm = vel;
velNorm.normalise();
points[0] = pos - fwdNorm * size.x * 0.5f - rightNorm * size.y * 0.5f;
points[1] = pos + fwdNorm * size.x * 0.5f - rightNorm * size.y * 0.5f;
points[2] = pos + fwdNorm * size.x * 0.5f + rightNorm * size.y * 0.5f;
points[3] = pos - fwdNorm * size.x * 0.5f + rightNorm * size.y * 0.5f;
fwdInterval.x = 0;
fwdInterval.y = size.x;
rightInterval.x = 0;
rightInterval.y = size.y;
}
vecf projectToRay(vecf &rayStart, vecf &rayDirNorm)
{
vecf res;
vecf toP0 = points[0] - rayStart;
res.x = res.y = rayDirNorm.dotproduct(toP0);
for (int i = 1; i < 4; ++i)
{
vecf toPi = points[i] - rayStart;
float proj = rayDirNorm.dotproduct(toPi);
res.x = min(res.x, proj);
res.y = max(res.y, proj);
}
return res;
}
bool isIntersecting(Box &b)
{
vecf bToAFwdPrj = b.projectToRay(points[0], fwdNorm);
if (!intervalsIntersect(bToAFwdPrj, fwdInterval))
return false;
vecf bToARightPrj = b.projectToRay(points[0], rightNorm);
if (!intervalsIntersect(bToARightPrj, rightInterval))
return false;
vecf aToBFwdPrj = projectToRay(b.points[0], b.fwdNorm);
if (!intervalsIntersect(aToBFwdPrj, b.fwdInterval))
return false;
vecf aToBRightPrj = projectToRay(b.points[0], b.rightNorm);
if (!intervalsIntersect(aToBRightPrj, b.rightInterval))
return false;
return true;
}
bool isOnCollisionCourseWith(Box &b, vecf &outTime)
{
vecf intervals[4];
vecf bRelativeVel = b.vel - vel;
float bToAFwdRelativeVelPrj = fwdNorm.dotproduct(bRelativeVel);
vecf bToAFwdPrj = b.projectToRay(points[0], fwdNorm);
if (!intervalsWillCollide(bToAFwdPrj, fwdInterval, bToAFwdRelativeVelPrj, intervals[0]))
return false;
float bToARightRelativeVelPrj = rightNorm.dotproduct(bRelativeVel);
vecf bToARightPrj = b.projectToRay(points[0], rightNorm);
if (!intervalsWillCollide(bToARightPrj, rightInterval, bToARightRelativeVelPrj, intervals[1]))
return false;
vecf aRelativeVel = bRelativeVel * -1.f;
float aToBFwdRelativeVelPrj = b.fwdNorm.dotproduct(aRelativeVel);
vecf aToBFwdPrj = projectToRay(b.points[0], b.fwdNorm);
if (!intervalsWillCollide(aToBFwdPrj, b.fwdInterval, aToBFwdRelativeVelPrj, intervals[2]))
return false;
float aToBRightRelativeVelPrj = b.rightNorm.dotproduct(aRelativeVel);
vecf aToBRightPrj = projectToRay(b.points[0], b.rightNorm);
if (!intervalsWillCollide(aToBRightPrj, b.rightInterval, aToBRightRelativeVelPrj, intervals[3]))
return false;
vecf intersection = intervals[0];
for (int i = 1; i < 3; ++i)
{
intersection.x = max(intersection.x, intervals[i].x);
intersection.y = min(intersection.y, intervals[i].y);
}
if (intersection.x > intersection.y)
return false;
outTime = intersection;
return true;
}
};
class MyTank
{
public:
vecf pos;
vecf vel;
float angle;
float w;
float gunRelAngle;
float gunW;
Tank *t;
Bonus *bon;
float innerRadius;
float outerRadius;
vecf targetPos;
float targetAngle;
vecf forwardNorm;
vecf rightNorm;
bool isAlive;
TracedObject objType;
MyTank()
{
}
void setPart(Unit &unit)
{
pos.x = (float)unit.x(); pos.y = (float)unit.y();
vel.x = (float)unit.speed_x(); vel.y = (float)unit.speed_y();
angle = (float)unit.angle();
w = (float)unit.angular_speed();
float tWid = (float)unit.width();
float tHei = (float)unit.height();
innerRadius = min(tWid,tHei) * 0.5f;
outerRadius = sqrtf(tWid * tWid + tHei * tHei)*0.5f;
forwardNorm = angleToDir(angle);
rightNorm = angleToDir(angle + M_PIf * 0.5f);
}
void set(Tank &tank)
{
setPart(tank);
gunRelAngle = (float)tank.turret_relative_angle();
gunW = (float)tank.turret_turn_speed();
t = &tank;
isAlive = tank.crew_health() > 0 && tank.hull_durability() > 0;
objType = isAlive ? (tank.teammate() ? FriendlyTank : EnemyTank) : DeadTank;
}
void set(Shell &shell)
{
setPart(shell);
gunRelAngle = 0.f;
gunW = 0.f;
t = NULL;
isAlive = false;
objType = (shell.type() == REGULAR ? LightShell : HeavyShell);
}
void set(Bonus &bonus)
{
setPart(bonus);
gunRelAngle = 0.f;
gunW = 0.f;
t = NULL;
bon = &bonus;
isAlive = false;
switch(bonus.type())
{
case MEDIKIT:
objType = BonusHealth;
break;
case REPAIR_KIT:
objType = BonusArmor;
break;
case AMMO_CRATE:
objType = BonusWeapon;
break;
}
}
float turretAngleTo(vecf tgtpos) const
{
float absolute_angle_to = (tgtpos-pos).globalAngle();
float relative_angle_to = absolute_angle_to - angle - gunRelAngle;
float result = clampAngle(relative_angle_to);
return result;
}
void updateTargetPos(vecf from, vecf srcVel)
{
float distance = (pos - from).length();
float timeToTravel = 0.f;
for (int i = 0; i < 5; ++i)
{
//timeToTravel = distance / SHELL_VEL;
float underLog;
underLog = (!me->t->premium_shell_count()) ?
1.f - 0.0050505f*distance/SHELL_VEL :
1.f - 0.0102040f*distance/SHELL_VEL ;
if (underLog > 1e-6f)
timeToTravel = (!me->t->premium_shell_count()) ?
-199.5f * logf(underLog) :
-100.0f * logf(underLog);
else
{
timeToTravel = 5000.f;
break;
}
targetPos = pos + (vel) * timeToTravel;
distance = (targetPos - from).length();
}
targetAngle = angle + w * timeToTravel;
}
};
int curTime = 0;
vecf center;
std::vector<Tank> data;
std::vector<Bonus> bonusData;
std::vector<Shell> shellData;
MyTank myTanks[MAX_ITEMS];
MyTank bonus[MAX_ITEMS];
int bonusN = 0;
int myTanksN = 0;
int dataN = 0;
MyTank shell[MAX_ITEMS];
int shellN = 0;
MyTank *tank[MAX_ITEMS];
MyTank *aliveTank[MAX_ITEMS];
int tankN = 0;
int aliveTankN = 0;
int timeToChangeDirection = 360;
bool isDirectionForward = true;
vecf worldSize;
float largeDist;
typedef enum
{
BL_ME = 0,
BL_DEAD_TANKS,
BL_ENEMY_TANKS,
BL_PREDICTED_ENEMY_TANKS,
BL_BULLETS,
BL_PREDICTED_BULLETS,
BL_WALLS,
BL_BONUSES,
BOX_LISTS_N
}BoxListType;
Box boxList[BOX_LISTS_N][MAX_ITEMS];
int boxListLength[BOX_LISTS_N];
Box tempBoxList[MAX_ITEMS];
int tempBoxListN;
void parseData(Tank &self, World &world)
{
curTime = world.tick();
worldSize.x = (float)world.width();
worldSize.y = (float)world.height();
center = worldSize * 0.5f;
largeDist = worldSize.x + worldSize.y;
data.clear();
myTanksN = 0;
tankN = 0;
aliveTankN = 0;
data = world.tanks();
for (unsigned int i = 0; i < data.size(); ++i)
{
myTanks[i].set(data.at(i));
myTanksN = i+1;
MyTank &cur = myTanks[i];
if (cur.t->id() == self.id())
{
me = &cur;
continue;
}
tank[tankN] = &cur;
tankN++;
if (cur.t->crew_health()>0 && cur.t->hull_durability()>0)
{
aliveTank[aliveTankN]=&cur;
aliveTankN++;
}
}
bonusData = world.bonuses();
bonusN = 0;
for (unsigned int i = 0; i < bonusData.size(); ++i)
{
bonus[i].set(bonusData.at(i));
bonusN = i+1;
}
shellData = world.shells();
shellN = 0;
for (unsigned int i = 0; i < shellData.size(); ++i)
{
shell[shellN].set(shellData.at(i));
shellN++;
}
}
void getFreeDists(vecf onwards, float &onwardsFree, float &backwardsFree)
{
onwardsFree = largeDist;
backwardsFree = largeDist;
for (int i = 0; i < tankN; ++i)
{
MyTank &t = *tank[i];
vecf toT = t.pos - me->pos;
float onwardsDist = toT.dotproduct(onwards);
vecf onwardsVec = onwards*onwardsDist;
vecf perpVec = toT - onwardsVec;
float perpDist = perpVec.length();
if (perpDist < t.innerRadius)
{
if (onwardsDist > 0.f)
{
float d = onwardsDist - t.outerRadius - me->outerRadius;
if (d<0)
d=0;
onwardsFree = min(onwardsFree, d);
}
else
{
float backwardsDist = -1.f * onwardsDist;
float d = backwardsDist - t.outerRadius - me->outerRadius;
if (d<0)
d=0;
backwardsFree = min(backwardsFree, d);
}
}
}
}
float getToWall(vecf &pos, vecf &onwards)
{
float toWallX = largeDist;
float toWallY = largeDist;
if (fabsf(onwards.x) > 1e-5f)
{
float wallX = (onwards.x > 0.f ? worldSize.x : 0.f);
toWallX = (wallX - pos.x) / onwards.x;
}
if (fabsf(onwards.y) > 1e-5f)
{
float wallY = (onwards.y > 0.f ? worldSize.y : 0.f);
toWallY = (wallY - pos.y) / onwards.y;
}
float toWallOnwards = min(toWallX, toWallY);
return max(0.f, toWallOnwards);
}
typedef struct
{
TracedObject objType;
float dist;
} TraceResult;
TraceResult traceWall(vecf &from, vecf &dir, float radius)
{
float toWallX = largeDist;
float toWallY = largeDist;
if (fabsf(dir.x) > 1e-5f)
{
float wallX = (dir.x > 0.f ? worldSize.x : 0.f);
toWallX = (wallX - from.x) / dir.x;
}
if (fabsf(dir.y) > 1e-5f)
{
float wallY = (dir.y > 0.f ? worldSize.y : 0.f);
toWallY = (wallY - from.y) / dir.y;
}
float toWallOnwards = min(toWallX, toWallY) - radius;
float dist = max(0.f, toWallOnwards);
TraceResult res;
res.dist = dist;
res.objType = Wall;
return res;
}
typedef struct
{
int index;
vecf timeInterval;
} ArrayTraceResult;
ArrayTraceResult traceArray(Box* aObjects, int aN, Box &b)
{
ArrayTraceResult res;
res.index = -1;
res.timeInterval.x = MAX_FLOAT;
res.timeInterval.y = MAX_FLOAT-1.f;
for (int i = 0; i < aN; ++i)
{
vecf timeInterval;
if (aObjects[i].isOnCollisionCourseWith(b, timeInterval))
{
if (timeInterval.x < res.timeInterval.x)
{
res.timeInterval = timeInterval;
res.index = i;
}
}
}
return res;
}
TraceResult traceTanks(vecf &from, vecf &dir, float radius)
{
TraceResult res;
res.dist = largeDist;
res.objType = Nothing;
for (int i = 0; i < tankN; ++i)
{
MyTank &t = *tank[i];
vecf toT = t.pos - from;
float onwardsDist = toT.dotproduct(dir);
vecf onwardsVec = dir * onwardsDist;
vecf perpVec = toT - onwardsVec;
float perpDist = perpVec.length();
if (perpDist < t.outerRadius + radius)
{
if (onwardsDist > 0.f)
{
float d = onwardsDist - t.outerRadius - radius;
if (d<0)
d=0;
if (d < res.dist)
{
res.dist = d;
if (t.isAlive)
res.objType = EnemyTank;
else
res.objType = DeadTank;
}
}
}
}
return res;
}
TraceResult traceBonuses(vecf &from, vecf &dir, float radius)
{
TraceResult res;
res.dist = largeDist;
res.objType = Nothing;
for (int i = 0; i < bonusN; ++i)
{
MyTank &bon = bonus[i];
vecf toT = bon.pos - from;
float onwardsDist = toT.dotproduct(dir);
vecf onwardsVec = dir * onwardsDist;
vecf perpVec = toT - onwardsVec;
float perpDist = perpVec.length();
if (perpDist <= bon.outerRadius + radius)
{
if (onwardsDist > 0.f)
{
float d = onwardsDist - bon.outerRadius - radius;
if (d<0)
d=0;
if (d < res.dist)
{
res.dist = d;
res.objType = bon.objType;
}
}
}
}
return res;
}
TraceResult Trace(vecf &from, vecf &dir, float radius)
{
TraceResult wall = traceWall(from, dir, radius);
TraceResult tank = traceTanks(from, dir, radius);
TraceResult bonus = traceBonuses(from, dir, radius);
if (wall.dist < tank.dist)
{
if (wall.dist < bonus.dist)
return wall;
else
return bonus;
}
if (bonus.dist < tank.dist)
return bonus;
return tank;
}
void addStaticPredictionsToList(Box* list, int &listN)
{
for (int i = 0; i < tankN; ++i)
{
MyTank &mt = *tank[i];
if (mt.isAlive && !mt.t->teammate())
{
vecf zero;
vecf predictionSize = vecf(mt.t->height(), mt.t->width())*0.25f;
list[listN].init(mt.targetPos, zero, mt.targetAngle, predictionSize);
listN++;
}
}
}
void addDeadTanksToList(Box* list, int &listN)
{
for (int i = 0; i < tankN; ++i)
{
if (!tank[i]->isAlive)
{
list[listN].init(*tank[i]->t);
listN++;
}
}
}
void addBonusesToList(Box* list, int &listN)
{
for (int i = 0; i < bonusN; ++i)
{
list[listN].init(*bonus[i].bon);
listN++;
}
}
TankType MyStrategy::SelectTank(int tank_index, int team_size)
{
return MEDIUM;
}
#define MAX_SAMPLES 5000
vecf prevPos[MAX_SAMPLES]={0};
vecf prevVel[MAX_SAMPLES]={0};
float thr[2]={0,0};
void driveTo(vecf pos)
{
vecf toPos = pos - me->pos;
vecf dst = toPos;
dst.rotate(-me->angle);
float ang = dst.globalAngle();
float len = dst.length();
// x - fwd
// y - right
float lim = min(1.f, 0.001f * len);
float angLim = min(lim,0.2f);
if (fabsf(ang) < angLim)
{
//printf("fwd\n");
thr[0] = 1.f;
thr[1] = 1.f;
}
else if (fabsf(clampAngle(ang+M_PIf)) < angLim)
{
//printf("back\n");
thr[0] = -1.f;
thr[1] = -1.f;
isDirectionForward = false;
timeToChangeDirection = 100;
}
else if (ang > 0.f)
{
if (ang < M_PIf * 0.5f)
{
//printf("fwd-right\n");
thr[0] = 1.f;
thr[1] = -1.f+lim*2.f;
}
else
{
//printf("back-left\n");
thr[0] = -1.f;
thr[1] = -0.1f-lim;
}
}
else
{
if (ang > -M_PIf * 0.5f)
{
//printf("fwd-left\n");
thr[0] = -1.f+lim;
thr[1] = 1.f;
}
else
{
//printf("back-right\n");
thr[0] = -0.1f-lim*2.f;
thr[1] = -1.f;
}
}
}
void moveToCorner()
{
float CORNER_MUL = 0.15f;
vecf corners[4];
corners[0] = vecf(worldSize.x * (1.f - CORNER_MUL), worldSize.y * (1.f - CORNER_MUL));
corners[1] = vecf(worldSize.x * (1.f - CORNER_MUL), worldSize.y * (CORNER_MUL));
corners[2] = vecf(worldSize.x * (CORNER_MUL), worldSize.y * (1.f - CORNER_MUL));
corners[3] = vecf(worldSize.x * (CORNER_MUL), worldSize.y * (CORNER_MUL));
vecf nearestCorner = corners[0];
for (int i = 1; i < 4; ++i)
{
if ( (corners[i] - me->pos).lengthSqare() < (nearestCorner - me->pos).lengthSqare() )
nearestCorner = corners[i];
}
driveTo(nearestCorner);
}
float oldThr[2] = {-1.f,-1.f};
void MyStrategy::Move(Tank self, World world, model::Move& move)
{
parseData(self, world);
for (int i = 0; i < aliveTankN; ++i)
{
aliveTank[i]->updateTargetPos(me->pos, vecf());
}
//int d=0;
//for (short i = 0; i >= 0; i++)
// for (short x = 0; x < 100; x++)
// d = i+x;
bool isCurrentlyHuntingForBonus = false;
bool isDrivingHard = false;
vecf onwards = me->forwardNorm * (isDirectionForward ? 1.f : -1.f);
float onwardsFree, backwardsFree;
getFreeDists(onwards, onwardsFree, backwardsFree);
float toWallOnwards = getToWall(me->pos, onwards);
vecf onRight = onwards;
onRight.rotate(M_PIf * 0.2f);
float rightFree, rightBackFree;
getFreeDists(onRight, rightFree, rightBackFree);
float toWallOnRight = getToWall(me->pos, onRight);
vecf onLeft = onwards;
onLeft.rotate(-M_PIf * 0.2f);
float leftFree, leftBackFree;
getFreeDists(onLeft, leftFree, leftBackFree);
float toWallOnLeft = getToWall(me->pos, onLeft);
float fwdDists[3] = {min(toWallOnLeft, leftFree), min(toWallOnwards, onwardsFree), min(toWallOnRight, rightFree)};
//float fwdDists[3] = {leftFree, onwardsFree, rightFree};
float wishRightTurn = 0.f;
float bestDist = 0.f;
float bestDir = 0.f;
float worstDist = largeDist;
bool goodToGo = true;
for (int i = 0; i < 3; ++i)
{
worstDist = min(worstDist, fwdDists[i]);
if (bestDist < fwdDists[i])
{
bestDist = fwdDists[i];
bestDir = (float)i - 1.f;
}
if (fwdDists[i] < me->outerRadius)
goodToGo = false;
}
bool needHealth = (me->t->crew_health() < me->t->crew_max_health());
bool needArmor = (me->t->hull_durability() < me->t->hull_max_durability());
int angles = 256;
float wishAngle = 0.f;
TraceResult wishResult;
wishResult.objType = Nothing;
wishResult.dist = largeDist;
for (int angN = 0; angN <= angles; ++angN)
{
float rayAngle = M_PIf * (-1.f + 2.f * (float)angN / (float)angles);
if (fabsf(rayAngle) > M_PIf * 0.4f &&
fabsf(rayAngle) < M_PIf * 0.6f)
continue;
vecf dir = angleToDir(rayAngle + me->angle);
vecf trpos = me->pos + me->forwardNorm * me->outerRadius;
TraceResult res = Trace(trpos ,dir,me->innerRadius*0.5f);
//res.dist = abs(rayAngle) * 100.f;
if (res.objType == BonusHealth)
{
if (wishResult.objType != BonusHealth && needHealth)
{
wishResult = res;
wishAngle = rayAngle;
}
else if (wishResult.dist > res.dist)
{
wishResult = res;
wishAngle = rayAngle;
}
}
if (res.objType == BonusArmor && needArmor && !(wishResult.objType == BonusHealth && needHealth))
{
if (wishResult.dist > res.dist || wishResult.objType == Nothing )
{
wishResult = res;
wishAngle = rayAngle;
}
}
if (res.objType == BonusWeapon &&
!(wishResult.objType == BonusHealth && needHealth) &&
!(wishResult.objType == BonusArmor && needArmor))
{
if (wishResult.dist > res.dist || wishResult.objType == Nothing )
{
wishResult = res;
wishAngle = rayAngle;
}
}
}
// full throttle forward if it's actually ok
{
vecf dir = angleToDir(me->angle);
TraceResult resA = Trace(me->pos,dir,me->innerRadius);
TraceResult resB = Trace(me->pos,dir,me->innerRadius*0.8f);
if (resA.objType == resB.objType &&
fabsf(resA.dist - resB.dist) < me->innerRadius &&
resA.objType == wishResult.objType &&
fabsf(resA.dist - wishResult.dist) < me->innerRadius)
{
wishResult = resA;
wishAngle = 0.f;
}
}
//if (isDirectionForward)
{
if (wishResult.objType == Nothing)
{
moveToCorner();
}
if (wishResult.objType != Nothing)
{
isCurrentlyHuntingForBonus = true;
vecf wishDir(1.f, 0.f);
wishDir.rotate(wishAngle+me->angle);
wishDir = wishDir * (wishResult.dist + me->outerRadius * 2.f);
vecf wishPos = me->pos + wishDir;
driveTo(wishPos);
}
else
{
moveToCorner();
}
}
move.set_turret_turn(0.f);
move.set_fire_type(NONE);
int minAngleTo = 0;
float minAngleAbs = M_2PIf;
float minAngle = M_PIf;
float minDistanceSq = worldSize.lengthSqare();
float minHisTurretAngle = M_PIf;
bool isTargetOccluded = true;
bool isBeingLate = false;
for (int i = 0; i < aliveTankN; ++i)
{
bool isBeingLateForCurrentTank = true;
float angle = me->turretAngleTo(aliveTank[i]->targetPos);
float angleAbs = fabsf(angle);
if (fabsf(angle - me->w*(float)me->t->remaining_reloading_time()) > (float)(me->t->remaining_reloading_time()+1) * me->gunW)
isBeingLateForCurrentTank = true;
float distanceSq = (me->pos - aliveTank[i]->targetPos).lengthSqare();
if (aliveTank[i]->t->teammate())
continue;
float hisTurretAngle = fabsf(aliveTank[i]->turretAngleTo(me->pos));
bool isOccluded = true;
vecf shootDir = aliveTank[i]->targetPos - me->pos;
shootDir.normalise();
TraceResult res = Trace(me->pos, shootDir, 4.f);
if (res.objType == EnemyTank || res.dist > (me->pos-aliveTank[i]->targetPos).length())
isOccluded = false;
if (isTargetOccluded || !isOccluded)
{
if (isTargetOccluded || !isBeingLateForCurrentTank || isBeingLate)
{
//if (angleAbs < minAngleAbs ||
//(angleAbs == minAngleAbs && distanceSq < minDistanceSq) ||
//(isTargetOccluded && !isOccluded))
bool isCloser = (distanceSq < minDistanceSq);
bool isBetterAngle = angleAbs < minAngleAbs;
bool isBetterOcclusion = isTargetOccluded && !isOccluded;
if ((curTime < 1000 ? isCloser : isBetterAngle) || isBetterOcclusion)
{
minAngleTo = i;
minAngleAbs = angleAbs;
minAngle = angle;
minDistanceSq = distanceSq;
minHisTurretAngle = hisTurretAngle;
isTargetOccluded = isOccluded;
isBeingLate = isBeingLateForCurrentTank;
}
}
}
}
move.set_turret_turn(minAngle);
// help rotating guns with tracks
if (!isTargetOccluded && isBeingLate && !isDrivingHard)
{
if ((!isCurrentlyHuntingForBonus && fabsf(wishAngle) > 0.2f) ||
fabsf(wishAngle) > 0.2f && ((wishAngle > 0) == (minAngle > 0)) )
{
thr[0]=((minAngle < -0.02f) ? -1.f : 0.75f);
thr[1]=((minAngle > 0.02f) ? -1.f : 0.75f);
}
}
if (world.tick() < 800)
{
moveToCorner();
}
// avoid enemy bullets
int forwardEscapeCounter = 0;
int backwardsEscapeCounter = 0;
int leftRotateEscapeCounter = 0;
int rightRotateEscapeCounter = 0;
for (int i = 0; i < shellN; ++i)
{
MyTank &sh = shell[i];
/*if (sh.objType == HeavyShell)
{
int idx = (int)world.shells()[i].id();
printf("s %d vel %f disp %f\n\n",
idx, prevVel[idx].length(), (prevPos[idx]-sh.pos).length());
prevVel[(int)world.shells()[i].id()] = sh.vel;
prevPos[(int)world.shells()[i].id()] = sh.pos;
}*/
vecf toShell = sh.pos - me->pos;
//skip my shells
vecf toShellNorm = toShell;
toShellNorm.normalise();
if (sh.vel.dotproduct(toShell) > 0.1f)
continue;
vecf shellVelNorm = sh.vel;
shellVelNorm.normalise();
float rpShellToMe = (me->pos - sh.pos).dotproduct(me->rightNorm);
float rpShellVelNorm = shellVelNorm.dotproduct(me->rightNorm);
if (fabsf(rpShellVelNorm) < 1e-5f)
continue; // shell approaches slowly
float rpDistanceToCollision = rpShellToMe / rpShellVelNorm;
if (fabsf(rpDistanceToCollision) < me->outerRadius + me->innerRadius)
{
float meToShellAngle = me->forwardNorm.angleTo(sh.angle);
if (fabsf(meToShellAngle) < M_PIf * 0.5f)
{
if (meToShellAngle > 0.f)
leftRotateEscapeCounter ++;
else
rightRotateEscapeCounter ++;
}
else
{
if (meToShellAngle < 0.f)
leftRotateEscapeCounter ++;
else
rightRotateEscapeCounter ++;
}
continue;// shell goes to the front/end !
}
if ((rpShellToMe > 0) != (rpShellVelNorm > 0))
continue; // shell goes away from my line
float rpThickness = me->innerRadius + fabsf(rpShellVelNorm) * sh.outerRadius;
if (rpDistanceToCollision > 0.f)
rpDistanceToCollision -= rpThickness;
else
rpDistanceToCollision += rpThickness;
float distanceToCollision = fabsf(rpDistanceToCollision / rpShellVelNorm);
float shellSpd = sh.vel.length();
if (shellSpd < 1e-5f)
continue; // shell has stopped
float underLog =
(sh.objType != HeavyShell ?
1.f - 0.0050505*distanceToCollision / shellSpd :
1.f - 0.010204*distanceToCollision / shellSpd);
if (underLog < 1e-5f)
continue; // shells don't fly that far
float timeToCollision = (sh.objType != HeavyShell ? -199.5f * logf(underLog) : -100.0f * logf(underLog));
float fpMyPos = (me->pos + me->vel * timeToCollision).dotproduct(me->forwardNorm);
float fpCollisionPos = (sh.pos + shellVelNorm*distanceToCollision).dotproduct(me->forwardNorm);
float signedDistanceAtCollision = fpCollisionPos - fpMyPos;
if (fabsf(signedDistanceAtCollision) > me->outerRadius + shell->outerRadius)
continue;
if (signedDistanceAtCollision < 0.25f * me->innerRadius)
forwardEscapeCounter ++;
else
backwardsEscapeCounter ++;
}
// choose escape direction
if (forwardEscapeCounter > 0 || backwardsEscapeCounter > 0)
{
vecf backNorm = me->forwardNorm * -1.f;
TraceResult fwdRes = traceWall(me->pos, me->forwardNorm, 0.25f*me->innerRadius);
TraceResult fwdResD = traceTanks(me->pos, me->forwardNorm, me->innerRadius);
fwdRes.dist = min(fwdRes.dist, fwdResD.dist);
TraceResult backRes = traceWall(me->pos, backNorm, 0.25f*me->innerRadius);
TraceResult backResD = traceTanks(me->pos, backNorm, me->innerRadius);
backRes.dist = min(backRes.dist, backResD.dist);
float myFwdVel = me->forwardNorm.dotproduct(me->vel);
int escapeDir = 0;//1 - fwd, -1 - bwd, 0 - any
if (forwardEscapeCounter > 0)
{
// if (fwdRes.dist > me->innerRadius)
escapeDir = 1;
// else
// escapeDir = -1;
}
else
{
//if (backRes.dist > me->innerRadius)
escapeDir = -1;
//else
//escapeDir = 1;
}
if (escapeDir != 0)
{
thr[0] = ((escapeDir < 0) ? -1.f : 1.f);
thr[1] = ((escapeDir < 0) ? -1.f : 1.f);
}
}
else if (leftRotateEscapeCounter > 0 || rightRotateEscapeCounter > 0)
{
if (leftRotateEscapeCounter > rightRotateEscapeCounter)
{
thr[0]=(-1.f);
thr[1]=(0.75f);
}
else
{
thr[0]=(0.75f);
thr[1]=(-1.f);
}
}
if (self.remaining_reloading_time() == 0)
{
tempBoxListN = 0;
addDeadTanksToList(tempBoxList, tempBoxListN);
addBonusesToList(tempBoxList, tempBoxListN);
float shootAngle = me->gunRelAngle + me->angle;
Box bullet;
bullet.initHypotheticalBullet(me->pos, shootAngle);
ArrayTraceResult obstacle = traceArray(tempBoxList, tempBoxListN, bullet);
tempBoxListN = 0;
addStaticPredictionsToList(tempBoxList, tempBoxListN);
ArrayTraceResult target = traceArray(tempBoxList, tempBoxListN, bullet);
if (target.index >= 0 && (obstacle.index < 0 || obstacle.timeInterval.x > target.timeInterval.x))
move.set_fire_type(PREMIUM_PREFERRED);
}
if (world.tick() < 200)
{
moveToCorner();
}
if (world.tick() < 15)
{
move.set_fire_type(NONE);
}
// printf("%d\n", int(world.tick()));
static int escapingTime = -1;
escapingTime--;
if (forwardEscapeCounter || backwardsEscapeCounter || leftRotateEscapeCounter || rightRotateEscapeCounter)
if (escapingTime < 0)
{
escapingTime = 50.f;
oldThr[0]=thr[0];
oldThr[1]=thr[1];
}
if (curTime % 10 == 0)
oldThr[0]=thr[0];
oldThr[1]=thr[1];
move.set_left_track_power(oldThr[0]);
move.set_right_track_power(oldThr[1]);
}