v 0. Pasted by ud1 as cpp at 2012-12-03 02:08:00 MSK and set expiration to never.

Paste will expire never.

  1. // written by ud1 for russianaicup.ru, 2012
  2.  
  3. #include "MyStrategy.h"
  4.  
  5. #define _USE_MATH_DEFINES
  6. #include <cmath>
  7. #include <vector>
  8. #include <deque>
  9. #include <map>
  10.  
  11. //#define ENABLE_LOGGING 1
  12.  
  13. #ifdef ENABLE_LOGGING
  14. #include <iostream>
  15. #define LOG(x_) cout << g_self->teammate_index() << ":" << g_tick << " " << g_self->x() << " " << g_self->y() << "| " << x_ << std::endl
  16. #else
  17. #define LOG(x)
  18. #endif
  19.  
  20.  
  21.  
  22. using namespace model;
  23. using namespace std;
  24.  
  25.  
  26. World *g_world;
  27. vector<Tank> g_all_tanks;
  28. vector<Shell> g_all_shells;
  29. vector<Obstacle> g_all_obstacles;
  30. Tank *g_self;
  31. int g_team_size, g_team_alived;
  32. int g_tick = 0;
  33.  
  34. struct TankKey
  35. {
  36.     TankKey() {}
  37.     TankKey(const Tank &t)
  38.     {
  39.         player = t.player_name();
  40.         tank_ind = t.teammate_index();
  41.     }
  42.    
  43.     std::string player;
  44.     int tank_ind;
  45.    
  46.     bool operator < (const TankKey &o) const
  47.     {
  48.         if (tank_ind < o.tank_ind)
  49.             return true;
  50.        
  51.         if (tank_ind > o.tank_ind)
  52.             return true;
  53.        
  54.         return player < o.player;
  55.     }
  56.    
  57.     bool operator == (const TankKey &o) const
  58.     {
  59.         return player == o.player && tank_ind == o.tank_ind;
  60.     }
  61. };
  62.  
  63. std::map<TankKey, int> g_danger_frames;
  64. std::map<TankKey, TankKey> g_last_targets;
  65.  
  66. bool tankEq(const Tank &t1, const Tank &t2)
  67. {
  68.     return t1.player_name() == t2.player_name() && t1.teammate_index() == t2.teammate_index();
  69. }
  70.  
  71. double sqr(double v)
  72. {
  73.     return v*v;
  74. }
  75.  
  76. struct P
  77. {
  78.     P() {}
  79.     P(const Unit &u)
  80.     {
  81.         x = u.x();
  82.         y = u.y();
  83.     }
  84.     P(double x, double y) : x(x), y(y) {}
  85.    
  86.     double dist(P o) const
  87.     {
  88.         return sqrt(sqr(x - o.x) + sqr(y - o.y));
  89.     }
  90.    
  91.     double len2() const
  92.     {
  93.         return x*x + y*y;
  94.     }
  95.    
  96.     double len() const
  97.     {
  98.         return sqrt(x*x + y*y);
  99.     }
  100.    
  101.     bool operator == (const P &o)
  102.     {
  103.         return x == o.x && y == o.y;
  104.     }
  105.    
  106.     double x, y;
  107. };
  108.  
  109. #ifdef ENABLE_LOGGING
  110. ostream &operator << (ostream &str, const P&p)
  111. {
  112.     str << "(" << p.x << "," << p.y << ")";
  113.     return str;
  114. }
  115. #endif
  116.  
  117. P operator - (P p1, P p2)
  118. {
  119.     return P(p1.x - p2.x, p1.y - p2.y);
  120. }
  121.  
  122. P operator + (P p1, P p2)
  123. {
  124.     return P(p1.x + p2.x, p1.y + p2.y);
  125. }
  126.  
  127. P operator * (P p1, double v)
  128. {
  129.     return P(p1.x * v, p1.y * v);
  130. }
  131.  
  132. P operator / (P p1, double v)
  133. {
  134.     return P(p1.x / v, p1.y / v);
  135. }
  136.  
  137. P operator * (P p1, P p2)
  138. {
  139.     return P(p1.x * p2.x, p1.y * p2.y);
  140. }
  141.  
  142. double dot(P p1, P p2)
  143. {
  144.     return p1.x * p2.x + p1.y * p2.y;
  145. }
  146.  
  147. double cross(P p1, P p2)
  148. {
  149.     return p1.x * p2.y - p1.y * p2.x;
  150. }
  151.  
  152. double safetyRel(P p, const Tank &self, bool useTeemTanksDistanceRel);
  153.  
  154. P Sp(const Unit &t)
  155. {
  156.     return P(t.speed_x(), t.speed_y());
  157. }
  158.  
  159. P Sz(const Unit &t)
  160. {
  161.     return P(t.width(), t.height());
  162. }
  163.  
  164. struct TankData
  165. {
  166.     vector<Tank> tanks;
  167.    
  168.     void set(Tank tank)
  169.     {
  170.         for (size_t i = 0; i < tanks.size(); ++i)
  171.         {
  172.             if (tankEq(tanks[i], tank))
  173.             {
  174.                 tanks[i] = tank;
  175.                 return;
  176.             }
  177.         }
  178.         tanks.push_back(tank);
  179.     }
  180.    
  181.     Tank *get(Tank tank)
  182.     {
  183.         for (size_t i = 0; i < tanks.size(); ++i)
  184.         {
  185.             if (tankEq(tanks[i], tank))
  186.             {
  187.                 return &tanks[i];
  188.             }
  189.         }
  190.         return 0;
  191.     }
  192. } tankData;
  193.  
  194. P predictPos(const Tank &tank, double dt)
  195. {
  196.     double t[2] = {1e20, 1e20};
  197.     double half_width = tank.width() / 2.0;
  198.    
  199.     if (tank.speed_x() > 0.01)
  200.         t[0] = (g_world->width() - tank.x()) / tank.speed_x();
  201.     else if (tank.speed_x() < -0.01)
  202.         t[0] = -tank.x() / tank.speed_x();
  203.    
  204.     if (tank.speed_y() > 0.01)
  205.         t[1] = (g_world->height() - tank.y()) / tank.speed_y();
  206.     else if (tank.speed_y() < -0.01)
  207.         t[1] = -tank.y() / tank.speed_y();
  208.    
  209.     dt = min(dt, max(0.0, (min(t[0], t[1]) - half_width / P(tank.speed_x(), tank.speed_y()).len())));
  210.    
  211.     return P(tank) + P(tank.speed_x(), tank.speed_y()) * dt;
  212. }
  213.  
  214. struct FieldPoints
  215. {
  216.     std::vector<P> points;
  217.    
  218.     FieldPoints()
  219.     {
  220.         points.reserve(5);
  221.        
  222.         // По краям безопаснее
  223.         points.push_back(P(0.1, 0.1));
  224.         points.push_back(P(0.1, 0.9));
  225.         points.push_back(P(0.9, 0.1));
  226.         points.push_back(P(0.9, 0.9));
  227.         points.push_back(P(0.1, 0.3));
  228.         points.push_back(P(0.1, 0.7));
  229.         points.push_back(P(0.9, 0.3));
  230.         points.push_back(P(0.9, 0.7));
  231.         points.push_back(P(0.5, 0.1));
  232.         points.push_back(P(0.5, 0.9));
  233.        
  234.         /*points.push_back(P(0.25, 0.1));
  235.         points.push_back(P(0.5, 0.1));
  236.         points.push_back(P(0.75, 0.1));
  237.         points.push_back(P(0.25, 0.9));
  238.         points.push_back(P(0.5, 0.9));
  239.         points.push_back(P(0.75, 0.9));*/
  240.     }
  241. } fieldPoints;
  242.  
  243. #define ANGULAR_SP 2.0
  244. #define TURRENT_ANG_SP (1.0/10.0)
  245. #define REGULAR_SPEED 16.7
  246. #define PREMIUM_SPEED 13.3
  247. #define ENEMY_REL_ANG_TICS 10.0
  248. #define ENEMY_REL_MYANG_TICS 40.0
  249. #define ENEMY_REL_ANG (5.0*3.1415/180.0)
  250. #define BONUS_REL_ANG (45.0*3.1415/180.0)
  251. #define SAFETY_REL_ANG (20.0*3.1415/180.0)
  252. #define SAFETY_DURAL 0.5
  253. #define SAFETY_HEALTH 0.2
  254. #define SAFETY_DIST 300.0
  255. #define SHELL_WIDTH 10.0
  256. #define REAL_SHELL_WIDTH 7.5
  257. #define SHELL_SHELL_SAFE_DIST 10.0
  258. #define SHELL_SHELL_KEEP_DIST 20.0
  259. #define STACKED_AT_WALL_VEL 1.0
  260. #define STACKED_AT_WALL_ANG (10.0*3.1415/180.0)
  261. #define STACKED_AT_WALL_DIST 20.0
  262. #define MOVE_BACKWARD_FRAMES 70.0
  263. #define SPEED_HIST_SIZE 10
  264. #define MOVE_TO_TURN_DIST 200.0
  265. #define ENEMY_ONLY_FIRE_ANG (20.0*3.1415/180.0)
  266. #define ENEMY_ONLY_DIST_REL 2.0
  267. #define SHELL_DANGER_SELF_MIN_SPEED 0.8
  268. #define SHELL_DANGER_MIN_DIST 100.0
  269. #define DANGER_TANK_ANGLE (20.0*3.1415/180.0)
  270. #define SAFE_WALL_DIST 30.0
  271. #define DANGER_CHECK_LENBT 50.0
  272. #define BONUS_NEAR_RANGE 200.0
  273. #define SAFETY_POINT_RANGE 50.0
  274. #define ENEMY_REL_CREW_HEALTH_DURAL 0.3
  275. #define ENEMY_REL_ENEMY_DIST 300.0
  276. #define ENEMY_REL_RELTIME 0.5
  277. #define ENEMY_REL_SCORE 100
  278. #define BONUS_REL_EXP_DIST 300.0
  279. #define TARGET_POINT_MAX_SPEED_CHANGE 0.3
  280. #define SHELL_IMPACT_CENTER 0.2
  281. #define MAX_BONUS_DIST_REL 1.2
  282. #define FIRE_ACCURACY 8.0
  283. #define POTENTIAL_FIRE_TICS 40
  284. #define POTENTIAL_FIRE_MAX_DIST 700.0
  285. #define POTENTIAL_ANGLE (15.0*3.1415/180.0)
  286. #define POTENTIAL_FIRE_MIN_BONUS_DIST 300.0
  287. #define FIRE_TANK_DIST 300.0
  288. #define FIRE_TANK_DIST_DELTA 50.0
  289. #define FIRE_TANK_ANGLE_DELTA (20.0*3.1415/180.0)
  290. #define FIRE_TANK_HEAD_ANGLE_DELTA (45.0*3.1415/180.0)
  291. #define FIRE_TANK_BACK_DIST 100.0
  292. #define SAFETY_DIST_POW 300.0
  293. #define CLOSEST_BONUS_DIST 150.0
  294. #define MOVE_BACKWARD_AT_START_TICS 50
  295. #define FAST_MODE 0.5
  296. #define MOVE_SEGMENT_LENGTH 200.0
  297. #define MIN_DIST_TO_WALL 5.0
  298. #define MAX_PREMIUM_FIRE_DIST 600.0
  299. #define MAX_SIMULATION_TIME 100.0
  300. #define SHELL_SAFE_DIST 20.0
  301. #define ROT_POWER 0.5
  302. #define TEEMMATE_MIN_DIST 400.0
  303. #define TEEMMATE_MIN_DIST_FACTOR 200.0
  304. #define TEEMMATE_MAX_DIST 500.0
  305. #define TEEMMATE_MAX_DIST_FACTOR 200.0
  306. #define BORDER_MIN_DIST 100.0
  307. #define BORDER_MIN_DIST_FACTOR 30.0
  308. #define DO_NOT_FIRE_SHELL_MAX_DIST 300.0
  309. #define ENEMY_BODY_ANG_REL (15.0*3.1415/180.0)
  310. #define TEEMMATE_TANK_SHELL_BORDER 20.0
  311. #define TARGET_FIRE_PRECISION 1.5
  312. #define MAX_TARGET_TANGENT 70.0
  313. #define TANGENT_REL 20.0
  314.  
  315. P clampPoint(P p)
  316. {
  317.     if (p.x < BORDER_MIN_DIST)
  318.         p.x = BORDER_MIN_DIST;
  319.    
  320.     if (p.y < BORDER_MIN_DIST)
  321.         p.y = BORDER_MIN_DIST;
  322.    
  323.     if (p.x > (g_world->width() - BORDER_MIN_DIST))
  324.         p.x = (g_world->width() - BORDER_MIN_DIST);
  325.    
  326.     if (p.y > (g_world->height() - BORDER_MIN_DIST))
  327.         p.y = (g_world->height() - BORDER_MIN_DIST);
  328.    
  329.     return p;
  330. }
  331.  
  332. Player getTankPlayer(const Tank &tank)
  333. {
  334.     string playerName = tank.player_name();
  335.     vector<Player> players = g_world->players();
  336.     for (size_t i = 0; i < playerName.size(); ++i)
  337.     {
  338.         Player player = players[i];
  339.         if (player.name() == playerName)
  340.             return player;
  341.     }
  342.     return players[0];
  343. }
  344.  
  345. double truncD(double d, bool fast, bool pos)
  346. {
  347.     if (fast)
  348.     {
  349.         if (pos)
  350.         {
  351.             if (d > 1.0)
  352.                 return 1.0;
  353.    
  354.             if (d < FAST_MODE)
  355.                 return FAST_MODE;
  356.         }
  357.         else
  358.         {
  359.             if (d > -FAST_MODE)
  360.                 return -FAST_MODE;
  361.        
  362.             if (d < -1.0)
  363.                 return -1.0;
  364.         }
  365.     }
  366.     else
  367.     {
  368.         if (d > 1.0)
  369.             return 1.0;
  370.    
  371.         if (d < -1.0)
  372.             return -1.0;
  373.     }
  374.     return d;
  375. }
  376.  
  377. // Движение к точке на карте, при установленном параметре fast не делаем разворот и двигаемся задом (применяем в случае высокой опасности)
  378. void moveTo(const Tank &self, P p, model::Move& move, int enemy_side, bool fast)
  379. {
  380.     double a = self.GetAngleTo(p.x, p.y);
  381.     if (abs(a) < (M_PI_2) || (!fast && P(self).dist(p) > MOVE_TO_TURN_DIST))
  382.     {
  383.         if (abs(a) < (2.0*M_PI/3.0) || enemy_side == 0 || (a * enemy_side > 0.0))
  384.         {
  385.             if (a > 0.0)
  386.             {
  387.                 move.set_left_track_power(1.0);
  388.                 move.set_right_track_power(truncD(1.0 - a*ANGULAR_SP, fast, true));
  389.             }
  390.             else
  391.             {
  392.                 move.set_right_track_power(1.0);
  393.                 move.set_left_track_power(truncD(1.0 + a * ANGULAR_SP, fast, true));
  394.             }
  395.         }
  396.         else
  397.         {
  398.             //LOG("NEG TURN");
  399.             if (a < 0.0)
  400.             {
  401.                 move.set_left_track_power(1.0);
  402.                 move.set_right_track_power(truncD(1.0 + a*ANGULAR_SP, fast, true));
  403.             }
  404.             else
  405.             {
  406.                 move.set_right_track_power(1.0);
  407.                 move.set_left_track_power(truncD(1.0 - a * ANGULAR_SP, fast, true));
  408.             }
  409.         }
  410.     }
  411.     else
  412.     {
  413.         if (a > 0.0)
  414.         {
  415.             move.set_left_track_power(-1.0);
  416.             move.set_right_track_power(truncD(-1.0 + (M_PI - a)*ANGULAR_SP, fast, false));
  417.         }
  418.         else
  419.         {
  420.             move.set_right_track_power(-1.0);
  421.             move.set_left_track_power(truncD(-1.0 + (M_PI + a) * ANGULAR_SP, fast, false));
  422.         }
  423.     }
  424.    
  425.     //LOG("teem index " << self.teammate_index());
  426.     //LOG("p " << (p.x - self.x()) << " " << (p.y - self.y()) << " " << enemy_side);
  427.     //LOG("track " << move.left_track_power() << " " << move.right_track_power() << " " << a / (M_PI_2));
  428. }
  429.  
  430. struct MyUnit
  431. {
  432.     MyUnit() {}
  433.    
  434.     MyUnit(const Unit &t)
  435.     {
  436.         position = P(t);
  437.         velocity = Sp(t);
  438.         size = Sz(t);
  439.         angle = t.angle();
  440.         angular_speed = t.angular_speed();
  441.     }
  442.    
  443.     P position;
  444.     P velocity;
  445.     P size;
  446.     double angle, angular_speed;
  447. };
  448.  
  449. P aheadDir(const MyUnit &t)
  450. {
  451.     return P(cos(t.angle), sin(t.angle));
  452. }
  453.  
  454. P rightDir(const MyUnit &t)
  455. {
  456.     return P(-sin(t.angle), cos(t.angle));
  457. }
  458.  
  459. struct MyTank : public MyUnit
  460. {
  461.     MyTank() {}
  462.    
  463.     MyTank(const Tank &t) : MyUnit(t)
  464.     {
  465.         crew_health_rel = (double) t.crew_health() / (double) t.crew_max_health();
  466.         engine_power = t.engine_power();
  467.         rear_engine_power = t.engine_power() * t.engine_rear_power_factor();
  468.         mass = t.mass();
  469.         type = t.type();
  470.         turret_angle = t.turret_relative_angle();
  471.         tank = &t;
  472.     }
  473.    
  474.     double crew_health_rel;
  475.     double engine_power, rear_engine_power;
  476.     double mass;
  477.     double turret_angle;
  478.     TankType type;
  479.     const Tank *tank;
  480. };
  481.  
  482. P angleDir(double angle)
  483. {
  484.     return P(cos(angle), sin(angle));
  485. }
  486.  
  487. P fireDir(const MyTank &t)
  488. {
  489.     double angle = t.angle + t.turret_angle;
  490.     return angleDir(angle);
  491. }
  492.  
  493. struct MyShell : public MyUnit
  494. {
  495.     MyShell() {}
  496.     MyShell(const Shell &shell) : MyUnit(shell)
  497.     {
  498.         type = shell.type();
  499.     }
  500.    
  501.     ShellType type;
  502. };
  503.  
  504. struct MyMove
  505. {
  506.     MyMove()
  507.     {
  508.         left_track_power = 0.0;
  509.         right_track_power = 0.0;
  510.     }
  511.    
  512.     MyMove(const model::Move& move)
  513.     {
  514.         left_track_power = move.left_track_power();
  515.         right_track_power = move.right_track_power();
  516.     }
  517.    
  518.     double left_track_power, right_track_power;
  519. };
  520.  
  521. void accountWallCollision(MyTank &t, P p)
  522. {
  523.     double Im = t.size.len2()/12.0;
  524.     P tp = p - t.position;
  525.    
  526.     P impulse = P(0.0, 0.0);
  527.     if (p.x < 0)
  528.     {
  529.         double nm = 1.0 / (1.0 + sqr(tp.y) / Im);
  530.         impulse = impulse + P(1.0, 0.0) * (0.325 * max(0.0, -t.velocity.x - tp.y * t.angular_speed) + max(0.0, -t.velocity.x + tp.y * t.angular_speed)) * nm;
  531.     }
  532.    
  533.     if (p.y < 0)
  534.     {
  535.         double nm = 1.0 / (1.0 + sqr(tp.x) / Im);
  536.         impulse = impulse + P(0.0, 1.0) * (0.325 * max(0.0, -t.velocity.y + tp.x * t.angular_speed) + max(0.0, -t.velocity.y - tp.x * t.angular_speed)) * nm;
  537.     }
  538.    
  539.     if (p.x > g_world->width())
  540.     {
  541.         double nm = 1.0 / (1.0 + sqr(tp.y) / Im);
  542.         impulse = impulse + P(-1.0, 0.0) * (0.325 * max(0.0, t.velocity.x + tp.y * t.angular_speed) + max(0.0, t.velocity.x - tp.y * t.angular_speed)) * nm;
  543.     }
  544.    
  545.     if (p.y > g_world->height())
  546.     {
  547.         double nm = 1.0 / (1.0 + sqr(tp.x) / Im);
  548.         impulse = impulse + P(0.0, -1.0) * (0.325 * max(0.0, t.velocity.y - tp.x * t.angular_speed) + max(0.0, t.velocity.y + tp.x * t.angular_speed)) * nm;
  549.     }
  550.    
  551.     t.velocity = t.velocity + impulse;
  552.     t.angular_speed -= cross(impulse, tp) / Im;
  553. }
  554.  
  555. void accountWallCollision(MyTank &t)
  556. {
  557.     P ahead = aheadDir(t)*(t.size.x/2.0);
  558.     P right = rightDir(t)*(t.size.y/2.0);
  559.    
  560.     accountWallCollision(t, t.position + ahead + right);
  561.     accountWallCollision(t, t.position + ahead - right);
  562.     accountWallCollision(t, t.position - ahead + right);
  563.     accountWallCollision(t, t.position - ahead - right);
  564. }
  565.  
  566. // Моделирование положения танка
  567. MyTank predictTankPos(MyTank t, MyMove move)
  568. {
  569.     MyTank result = t;
  570.    
  571.     double health_factor = 0.5 * t.crew_health_rel + 0.5;
  572.     double left_force = move.left_track_power > 0.0 ?
  573.         move.left_track_power * t.engine_power :
  574.         move.left_track_power * t.rear_engine_power;
  575.        
  576.     left_force *= health_factor;
  577.        
  578.     double right_force = move.right_track_power > 0.0 ?
  579.         move.right_track_power * t.engine_power :
  580.         move.right_track_power * t.rear_engine_power;
  581.        
  582.     right_force *= health_factor;
  583.        
  584.     double force = 0.5 * (left_force + right_force);
  585.     double dT = 1.0/60.0;
  586.     double scale = 1.0/60.0;
  587.     double damping = 0.5;
  588.    
  589.     P v1 = t.velocity + aheadDir(t) * (force * dT * scale / t.mass);
  590.     result.velocity = v1 * (1.0 - damping / t.mass);
  591.    
  592.     double torque = 2.0 * (left_force - right_force);
  593.     double I = t.mass * t.size.len2()/12.0;
  594.     double rot_damp = 20.0 * t.mass;
  595.    
  596.     double w1 = t.angular_speed + torque * dT * scale / I;
  597.     result.angular_speed = w1 * (1.0 - rot_damp / I);
  598.    
  599.     result.size = t.size;
  600.     result.position = t.position;
  601.     result.angle = t.angle;
  602.    
  603.     accountWallCollision(result);
  604.    
  605.     result.position = t.position + result.velocity;
  606.     result.angle = t.angle + result.angular_speed;
  607.    
  608.     return result;
  609. }
  610.  
  611. // Моделирование положения снаряда
  612. MyShell predictShellPos(MyShell shell)
  613. {
  614.     MyShell result = shell;
  615.     double mass = shell.type == REGULAR ? 1.0 : 0.5;
  616.     double damping = 0.005;
  617.     result.velocity = result.velocity * (1.0 - damping / mass);
  618.     result.position = result.position + result.velocity;
  619.     return result;
  620. }
  621.  
  622. bool isUnitOnTheLeftSide(P p1, P p2, P c, MyUnit u2)
  623. {
  624.     P dir = p2 - p1;
  625.     P norm = P(-dir.y, dir.x);
  626.     double sign = dot(norm, (c - p1));
  627.    
  628.     P ahead = aheadDir(u2)*(u2.size.x/2.0);
  629.     P right = rightDir(u2)*(u2.size.y/2.0);
  630.  
  631.     if (dot(norm, u2.position + ahead + right - p1) * sign >= 0.0)
  632.         return false;
  633.    
  634.     if (dot(norm, u2.position + ahead - right - p1) * sign >= 0.0)
  635.         return false;
  636.    
  637.     if (dot(norm, u2.position - ahead + right - p1) * sign >= 0.0)
  638.         return false;
  639.    
  640.     if (dot(norm, u2.position - ahead - right - p1) * sign >= 0.0)
  641.         return false;
  642.    
  643.     return true;
  644. }
  645.  
  646. bool isUnitsNotIntersects_(MyUnit u1, MyUnit u2)
  647. {
  648.     P ahead = aheadDir(u1)*(u1.size.x/2.0);
  649.     P right = rightDir(u1)*(u1.size.y/2.0);
  650.    
  651.     if (isUnitOnTheLeftSide(u1.position + ahead - right, u1.position + ahead + right, u1.position, u2))
  652.         return true;
  653.    
  654.     if (isUnitOnTheLeftSide(u1.position + ahead + right, u1.position - ahead + right, u1.position, u2))
  655.         return true;
  656.    
  657.     if (isUnitOnTheLeftSide(u1.position - ahead + right, u1.position - ahead - right, u1.position, u2))
  658.         return true;
  659.    
  660.     if (isUnitOnTheLeftSide(u1.position - ahead - right, u1.position + ahead - right, u1.position, u2))
  661.         return true;
  662.    
  663.     return false;
  664. }
  665.  
  666. bool isPointOutsideField(P p)
  667. {
  668.     return p.x <= 0.0 || p.y <= 0.0 || p.x >= g_world->width() || p.y >= g_world->height();
  669. }
  670.  
  671. // Прямоугольник вышел за пределы поля
  672. bool isUnitIntersectsWalls(MyUnit u)
  673. {
  674.     P ahead = aheadDir(u)*(u.size.x/2.0);
  675.     P right = rightDir(u)*(u.size.y/2.0);
  676.    
  677.     return
  678.         isPointOutsideField(u.position + ahead + right) ||
  679.         isPointOutsideField(u.position + ahead - right) ||
  680.         isPointOutsideField(u.position - ahead + right) ||
  681.         isPointOutsideField(u.position - ahead - right);
  682. }
  683.  
  684. // Два прямоугольника не пересекаются
  685. bool isUnitsNotIntersects(MyUnit u1, MyUnit u2)
  686. {
  687.     return (isUnitsNotIntersects_(u1, u2) || isUnitsNotIntersects_(u2, u1));
  688. }
  689.  
  690. P aheadDir(const Unit &t)
  691. {
  692.     return P(cos(t.angle()), sin(t.angle()));
  693. }
  694.  
  695. P rightDir(const Unit &t)
  696. {
  697.     return P(-sin(t.angle()), cos(t.angle()));
  698. }
  699.  
  700. void turnRightInPlace(const Tank &self, model::Move& move)
  701. {
  702.     move.set_left_track_power(1.0 * self.engine_rear_power_factor());
  703.     move.set_right_track_power(-1.0);
  704.     //LOG("TURN RIGHT IN PLACE");
  705. }
  706.  
  707. void turnLeftInPlace(const Tank &self, model::Move& move)
  708. {
  709.     move.set_left_track_power(-1.0);
  710.     move.set_right_track_power(1.0 * self.engine_rear_power_factor());
  711.     //LOG("TURN LEFT IN PLACE");
  712. }
  713.  
  714. void turnToCenter(const Tank &self, model::Move& move)
  715. {
  716.     P center = P(g_world->width() / 2.0, g_world->height() / 2.0);
  717.     double angle = self.GetAngleTo(center.x, center.y);
  718.     if (angle > 0.0)
  719.     {
  720.         turnRightInPlace(self, move);
  721.     }
  722.     else
  723.     {
  724.         turnLeftInPlace(self, move);
  725.     }
  726. }
  727.  
  728. void moveBackward(model::Move& move)
  729. {
  730.     move.set_left_track_power(-1.0);
  731.     move.set_right_track_power(-1.0);
  732.     //LOG("MOVE BACKWARD");
  733. }
  734.  
  735. void moveForward(model::Move& move)
  736. {
  737.     move.set_left_track_power(1.0);
  738.     move.set_right_track_power(1.0);
  739.     //LOG("MOVE FORWARD");
  740. }
  741.  
  742. void turnRightForward(model::Move& move)
  743. {
  744.     move.set_left_track_power(1.0);
  745.     move.set_right_track_power(ROT_POWER);
  746.     //LOG("MOVE RIGHT FORWARD");
  747. }
  748.  
  749. void turnLeftForward(model::Move& move)
  750. {
  751.     move.set_left_track_power(ROT_POWER);
  752.     move.set_right_track_power(1.0);
  753.     //LOG("MOVE LEFT FORWARD");
  754. }
  755.  
  756. void turnRightBackward(model::Move& move)
  757. {
  758.     move.set_left_track_power(-1.0);
  759.     move.set_right_track_power(-ROT_POWER);
  760.     //LOG("MOVE RIGHT BACKWARD");
  761. }
  762.  
  763. void turnLeftBackward(model::Move& move)
  764. {
  765.     move.set_left_track_power(-ROT_POWER);
  766.     move.set_right_track_power(-1.0);
  767.     //LOG("MOVE LEFT BACKWARD");
  768. }
  769.  
  770. void turnRightForward0(model::Move& move)
  771. {
  772.     move.set_left_track_power(1.0);
  773.     move.set_right_track_power(0.0);
  774.     //LOG("MOVE RIGHT FORWARD");
  775. }
  776.  
  777. void turnLeftForward0(model::Move& move)
  778. {
  779.     move.set_left_track_power(0.0);
  780.     move.set_right_track_power(1.0);
  781.     //LOG("MOVE LEFT FORWARD");
  782. }
  783.  
  784. void turnRightBackward0(model::Move& move)
  785. {
  786.     move.set_left_track_power(-1.0);
  787.     move.set_right_track_power(0.0);
  788.     //LOG("MOVE RIGHT BACKWARD");
  789. }
  790.  
  791. void turnLeftBackward0(model::Move& move)
  792. {
  793.     move.set_left_track_power(0.0);
  794.     move.set_right_track_power(-1.0);
  795.     //LOG("MOVE LEFT BACKWARD");
  796. }
  797.  
  798. void turn(bool forward, bool right, model::Move& move)
  799. {
  800.     if (forward)
  801.     {
  802.         if (right)
  803.             turnRightForward(move);
  804.         else
  805.             turnLeftForward(move);
  806.     }
  807.     else
  808.     {
  809.         if (right)
  810.             turnRightBackward(move);
  811.         else
  812.             turnLeftBackward(move);
  813.     }
  814. }
  815.  
  816. double distToWall(const Tank &from, P dir)
  817. {
  818.     double half_width = from.width() / 2.0;
  819.    
  820.     double res = 1e20;
  821.     if (dir.x > 1e-6)
  822.         res = min(res, (g_world->width() - from.x()) / dir.x);
  823.    
  824.     if (dir.x < -1e-6)
  825.         res = min(res, -from.x() / dir.x);
  826.    
  827.     if (dir.y > 1e-6)
  828.         res = min(res, (g_world->height() - from.y()) / dir.y);
  829.    
  830.     if (dir.y < -1e-6)
  831.         res = min(res, -from.y() / dir.y);
  832.    
  833.     return max (res - half_width, 0.0);
  834. }
  835.  
  836. // Встаем перпендикулярно танку, для повышения шансов уклонения от снаряда
  837. void turnNormalToTank(const Tank &self, const Tank &enemy, model::Move& move)
  838. {
  839.     LOG("TURN NORMAL TO TANK " << enemy.x() << " " << enemy.y());
  840.     double angle = self.GetAngleTo(enemy);
  841.    
  842.     if ((angle > (M_PI_2 - FIRE_TANK_ANGLE_DELTA) && angle < (M_PI_2 + FIRE_TANK_ANGLE_DELTA)) || (angle > (-M_PI_2 - FIRE_TANK_ANGLE_DELTA) && angle < (-M_PI_2 + FIRE_TANK_ANGLE_DELTA)))
  843.     {
  844.         // already oriented
  845.         double cur_safety = safetyRel(self, enemy, true);
  846.         P back_point = clampPoint(P(self) - aheadDir(self) * FIRE_TANK_BACK_DIST);
  847.         double back_point_safety = safetyRel(back_point, enemy, true);
  848.         P front_point = clampPoint(P(self) + aheadDir(self) * FIRE_TANK_BACK_DIST);
  849.         double front_point_safety = safetyRel(front_point, enemy, true);
  850.        
  851.         P sp;
  852.         double safety;
  853.         bool back;
  854.         if (back_point_safety > front_point_safety)
  855.         {
  856.             sp = back_point;
  857.             safety = back_point_safety;
  858.             back = true;
  859.         }
  860.         else
  861.         {
  862.             sp = front_point;
  863.             safety = front_point_safety;
  864.             back = false;
  865.         }
  866.        
  867.         if (safety > cur_safety)
  868.         {
  869.             if (back)
  870.                 moveBackward(move);
  871.             else
  872.                 moveForward(move);
  873.         }
  874.     }
  875.     else
  876.     {
  877.         if (distToWall(self, rightDir(self)) < MIN_DIST_TO_WALL || distToWall(self, rightDir(self)*(-1.0)) < MIN_DIST_TO_WALL)
  878.         {
  879.             bool forward;
  880.             if (distToWall(self, aheadDir(self)) < MIN_DIST_TO_WALL)
  881.                 forward = false;
  882.             else if (distToWall(self, aheadDir(self)*(-1.0)) < MIN_DIST_TO_WALL)
  883.                 forward = true;
  884.             else
  885.                 forward = abs(self.GetAngleTo(enemy)) > M_PI_2;
  886.                
  887.             bool right;
  888.             if (distToWall(self, rightDir(self)) < MIN_DIST_TO_WALL)
  889.                 right = false;
  890.             else if (distToWall(self, rightDir(self)*(-1.0)) < MIN_DIST_TO_WALL)
  891.                 right = true;
  892.             else
  893.                 right = self.GetAngleTo(enemy) > 0.0;
  894.            
  895.             turn(forward, right, move);
  896.         }
  897.         else
  898.         {
  899.             if (angle > 0.0)
  900.             {
  901.                 if (angle < M_PI_2)
  902.                     turnLeftInPlace(self, move);
  903.                 else
  904.                     turnRightInPlace(self, move);
  905.             }
  906.             else
  907.             {
  908.                 if (angle > -M_PI_2)
  909.                     turnRightInPlace(self, move);
  910.                 else
  911.                     turnLeftInPlace(self, move);
  912.             }
  913.         }
  914.     }
  915. }
  916.  
  917. #define FIRE_TANK_ANGLE_PRECISION (5.0*3.1415/180.0)
  918.  
  919. // Поворачиваемся лицом к танку, чтобы отъехать от него подальше.
  920. void turnToTank(const Tank &self, const Tank &enemy, model::Move& move)
  921. {
  922.     LOG("TURN TO TANK " << enemy.x() << " " << enemy.y());
  923.     double angle = self.GetAngleTo(enemy) + M_PI * 1.25;
  924.     int r = angle / (M_PI / 2.0) + 0.5;
  925.     double dest_angle = r * (M_PI / 2.0) - M_PI * 1.25;
  926.    
  927.    
  928.     if ((angle < dest_angle) && ((dest_angle - angle) > FIRE_TANK_ANGLE_PRECISION))
  929.     {
  930.         if (distToWall(self, rightDir(self)*(-1.0)) < MIN_DIST_TO_WALL)
  931.             turnRightForward(move);
  932.         else
  933.             turnRightInPlace(self, move);
  934.     }
  935.     else if ((angle > dest_angle) && ((angle - dest_angle) > FIRE_TANK_ANGLE_PRECISION))
  936.     {
  937.         if (distToWall(self, rightDir(self)) < MIN_DIST_TO_WALL)
  938.             turnLeftForward(move);
  939.         else
  940.             turnLeftInPlace(self, move);
  941.     }
  942.     else
  943.     {
  944.         double cur_safety = safetyRel(self, enemy, true);
  945.         P back_point = P(self) - aheadDir(self) * FIRE_TANK_BACK_DIST;
  946.         double back_point_safety = safetyRel(back_point, enemy, true);
  947.         if (back_point_safety > cur_safety)
  948.             moveBackward(move);
  949.     }
  950. }
  951.  
  952. void turnFireTank(const Tank &self, const Tank &enemy, model::Move& move)
  953. {
  954.     double dist = P(self).dist(P(enemy));
  955.     if (dist < (FIRE_TANK_DIST - FIRE_TANK_DIST_DELTA))
  956.     {
  957.         turnToTank(self, enemy, move);
  958.     }
  959.     else if (dist > (FIRE_TANK_DIST + FIRE_TANK_DIST_DELTA))
  960.     {
  961.         turnNormalToTank(self, enemy, move);
  962.     }
  963.     else
  964.     {
  965.         double angle = abs(self.GetAngleTo(enemy));
  966.         if (angle < M_PI / 4.0)
  967.             turnToTank(self, enemy, move);
  968.         else
  969.             turnNormalToTank(self, enemy, move);
  970.     }
  971. }
  972.  
  973. void turretTo(const Tank &self, P p, model::Move& move)
  974. {
  975.     double a = self.GetTurretAngleTo(p.x, p.y);
  976.     move.set_turret_turn(a / self.turret_turn_speed()*TURRENT_ANG_SP);
  977. }
  978.  
  979. double firePrediction(const Tank &self, P p)
  980. {
  981.     return abs(self.GetTurretAngleTo(p.x, p.y)) * (P(self).dist(p));
  982. }
  983.  
  984. bool obstacleExist(P p, P from, const Tank *except_tank1, const Tank *except_tank2, bool withBonuses, double halfWindow, bool dontCheckTeemmateTanks);
  985.  
  986. bool velocityChangeForward(const Tank &t)
  987. {
  988.     Tank *old_t = tankData.get(t);
  989.     if (!old_t)
  990.         return false;
  991.    
  992.     P dv = Sp(t) - Sp(*old_t);
  993.     P ahead = aheadDir(t);
  994.     return dot(ahead, dv) > 0.0;
  995. }
  996.  
  997. P getTargetPoint(const Tank &self, const Tank &enemy, double dt, double &tangent)
  998. {
  999.     MyTank enemy_bk = MyTank(enemy);
  1000.     MyTank enemy_fw = MyTank(enemy);
  1001.     MyTank enemy_l = MyTank(enemy);
  1002.     MyTank enemy_r = MyTank(enemy);
  1003.     MyMove mv_bk, mv_fw, mv_r, mv_l;
  1004.     mv_bk.left_track_power = mv_bk.right_track_power = -1.0;
  1005.     mv_fw.left_track_power = mv_fw.right_track_power = 0.9;
  1006.     mv_r.left_track_power = 0.9;
  1007.     mv_r.right_track_power = 0.0;
  1008.     mv_l.left_track_power = 0.0;
  1009.     mv_l.right_track_power = 0.9;
  1010.    
  1011.     if (g_danger_frames[TankKey(enemy)] == (g_tick - 1))
  1012.     {
  1013.         // Данный танк находится под угрозой столкновения с пулей, смотрим изменение его скорости с последнего тика
  1014.         bool v_change_fw = velocityChangeForward(enemy);
  1015.        
  1016.         if (v_change_fw)
  1017.         {
  1018.             // Решаем, что танк решил поехать вперед
  1019.             mv_bk.left_track_power = mv_bk.right_track_power = 0.0;
  1020.         }
  1021.         else
  1022.         {
  1023.             mv_fw.left_track_power = mv_fw.right_track_power = -0.0;
  1024.         }
  1025.     }
  1026.    
  1027.     for (double t = 0.0; t <= dt; ++t)
  1028.     {
  1029.         enemy_bk = predictTankPos(enemy_bk, mv_bk);
  1030.         enemy_fw = predictTankPos(enemy_fw, mv_fw);
  1031.         enemy_r = predictTankPos(enemy_r, mv_r);
  1032.         enemy_l = predictTankPos(enemy_l, mv_l);
  1033.     }
  1034.    
  1035.     P dp = enemy_bk.position - enemy_fw.position;
  1036.     P mid = (enemy_bk.position + enemy_fw.position) * 0.5;
  1037.     P mid_dir = mid - P(self);
  1038.     mid_dir = mid_dir / (mid_dir.len() + 1e-10);
  1039.     tangent = abs(cross(mid_dir, dp));
  1040.    
  1041.     dp = enemy_r.position - enemy_l.position;
  1042.     tangent = max(tangent, abs(cross(mid_dir, dp)));
  1043.    
  1044.     if (tangent > MAX_TARGET_TANGENT)
  1045.         return mid;
  1046.    
  1047.     if (enemy.remaining_reloading_time() < dt)
  1048.         mid = mid - fireDir(enemy) * 20.0;
  1049.    
  1050.     double dw = abs((enemy.width() - enemy.height())/2.0);
  1051.    
  1052.     if (tangent < dw)
  1053.     {
  1054.         double enemyAng = enemy.angle();
  1055.         P fwd = P(cos(enemyAng), sin(enemyAng));
  1056.         P p1 = mid + fwd * dw;
  1057.         P p2 = mid - fwd * dw;
  1058.        
  1059.         if (P(self).dist(p1) < P(self).dist(p2))
  1060.             return p1;
  1061.        
  1062.         return p2;
  1063.     }
  1064.    
  1065.     return mid;
  1066. }
  1067.  
  1068. double getShellFlyingTime(ShellType type, double distance)
  1069. {
  1070.     if (type == REGULAR)
  1071.     {
  1072.         double l = 1.0 - distance * 0.005 / (1000.0/60.0);
  1073.         if (l < 0.0)
  1074.             return -1.0;
  1075.         return log(l) / log(0.995);
  1076.     }
  1077.     else
  1078.     {
  1079.         double l = 1.0 - distance * 0.01 / (800.0/60.0);
  1080.         if (l < 0.0)
  1081.             return -1.0;
  1082.         return log(l) / log(0.99);
  1083.     }
  1084.     return -1.0;
  1085. }
  1086.  
  1087. bool isAlive(const Tank &t)
  1088. {
  1089.     return t.crew_health() > 0 && t.hull_durability() > 0;
  1090. }
  1091.  
  1092. bool isEnemyOfTeemTank(const Tank &self, const TankKey &enemy)
  1093. {
  1094.     if (g_team_size <= 1)
  1095.         return false;
  1096.    
  1097.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1098.     {
  1099.         Tank &t = g_all_tanks[i];
  1100.         if (t.teammate() && isAlive(t) && !tankEq(t, self))
  1101.         {
  1102.             if (g_last_targets.count(TankKey(t)) && g_last_targets[TankKey(t)] == enemy)
  1103.                 return true;
  1104.         }
  1105.     }
  1106.    
  1107.     return false;
  1108. }
  1109.  
  1110. #define TANGET_MAX 120.0
  1111. #define TANGET_MAX_REL 40.0
  1112.  
  1113. std::map<TankKey, double> g_rel_tangents;
  1114.  
  1115. double getMinRelTangent()
  1116. {
  1117.     double res = 1e20;
  1118.     for (std::map<TankKey, double> ::iterator it = g_rel_tangents.begin(); it != g_rel_tangents.end(); ++it)
  1119.     {
  1120.         res = min(res, it->second);
  1121.     }
  1122.     return res;
  1123. }
  1124.  
  1125. double getTankProjLen(const Tank &self, const Tank &enemy)
  1126. {
  1127.    
  1128.     P se = P(enemy) - P(self);
  1129.     P ahead = aheadDir(enemy) * (enemy.width());
  1130.     P right = rightDir(enemy) * (enemy.height());
  1131.     P norm = P(-se.y, se.x);
  1132.     norm = norm / (norm.len() + 1e-10);
  1133.     return max(abs(dot(ahead + right, norm)), abs(dot(ahead - right, norm)));
  1134. }
  1135.  
  1136. // функция определения врага
  1137. double enemyRel(const Tank &self, const Tank &enemy)
  1138. {
  1139.     double enemy_crew_health = (double)enemy.crew_health() / (double)enemy.crew_max_health();
  1140.     double enemy_dural = (double)enemy.hull_durability() / (double)enemy.hull_max_durability();
  1141.     double enemy_reload = (double)enemy.remaining_reloading_time()/(double)enemy.reloading_time();
  1142.     //double score = getTankPlayer(enemy).score();
  1143.    
  1144.     //double ang = abs(enemy.GetAngleTo(self));
  1145.     double dist = P(self).dist(P(enemy));
  1146.     double shell_fly_time = getShellFlyingTime(model::REGULAR, max(0.0, dist - self.virtual_gun_length()));
  1147.     double tangent;
  1148.     getTargetPoint(self, enemy, shell_fly_time, tangent);
  1149.     double rel_tangent = g_rel_tangents[TankKey(enemy)] = tangent / getTankProjLen(self, enemy);
  1150.    
  1151.     double teem_factor = g_team_size > 1 ? 1.5 : 1.0;
  1152.    
  1153.     double res = (tangent + TANGENT_REL) * (min(enemy_crew_health, enemy_dural) + ENEMY_REL_CREW_HEALTH_DURAL/teem_factor) * (enemy_reload + ENEMY_REL_RELTIME);// / (score + ENEMY_REL_SCORE/teem_factor);
  1154.  
  1155.     if (rel_tangent > 0.8)
  1156.         res *= 2.0;
  1157.    
  1158.     if (rel_tangent > 1.0)
  1159.         res *= 2.0;
  1160.    
  1161.     if (rel_tangent > 1.2)
  1162.         res *= 2.0;
  1163.    
  1164.     if (isEnemyOfTeemTank(self, TankKey(enemy)))
  1165.         res /= 1.5;
  1166.    
  1167.     if (tangent > TANGET_MAX)
  1168.         res *= pow(2, (tangent - TANGET_MAX) / TANGET_MAX_REL);
  1169.    
  1170.     //res *= pow(2.0, dist / ENEMY_REL_ENEMY_DIST);
  1171.    
  1172.     /*if (ang < ENEMY_BODY_ANG_REL || ang > (M_PI - ENEMY_BODY_ANG_REL))
  1173.         res /= 4.0;*/
  1174.    
  1175.     if (enemy.premium_shell_count() > 0)
  1176.         res /= 2.0;
  1177.    
  1178.     if (enemy.hull_durability() <= 20 || enemy.crew_health() <= 20)
  1179.         res /= 2.0;
  1180.    
  1181.     if (obstacleExist(P(enemy), P(self), &enemy, &self, true, REAL_SHELL_WIDTH / 2.0, false))
  1182.         res *= 4.0;
  1183.    
  1184.     Tank *oldData = tankData.get(enemy);
  1185.     if (oldData)
  1186.     {
  1187.         double old_speed = P(oldData->speed_x(), oldData->speed_y()).len();
  1188.         double speed = P(enemy.speed_x(), enemy.speed_y()).len();
  1189.         double dl = abs(old_speed - speed) / (speed + 1e-10);
  1190.         if (dl > 1.0)
  1191.             res *= 8.0;
  1192.         else if (dl > 0.5)
  1193.             res *= 3.0;
  1194.     }
  1195.    
  1196.     double angSpRight = self.angular_speed() + self.turret_turn_speed();
  1197.     if (abs(angSpRight) < 1e-6)
  1198.         angSpRight = 1e-6;
  1199.    
  1200.     double angSpLeft = self.angular_speed() - self.turret_turn_speed();
  1201.     if (abs(angSpLeft) < 1e-6)
  1202.         angSpLeft = -1e-6;
  1203.    
  1204.     double s2e = self.GetTurretAngleTo(enemy);
  1205.     if (abs(s2e) < ENEMY_REL_ANG)
  1206.     {
  1207.         s2e = 0.0;
  1208.     }
  1209.     else
  1210.     {
  1211.         if (s2e > 0)
  1212.             s2e = abs(s2e / angSpRight);
  1213.         else
  1214.             s2e = abs(s2e / angSpLeft);
  1215.     }
  1216.  
  1217.     angSpRight = enemy.angular_speed() + enemy.turret_turn_speed();
  1218.     if (abs(angSpRight) < 1e-6)
  1219.         angSpRight = 1e-6;
  1220.    
  1221.     angSpLeft = enemy.angular_speed() - enemy.turret_turn_speed();
  1222.     if (abs(angSpLeft) < 1e-6)
  1223.         angSpLeft = -1e-6;
  1224.    
  1225.     double e2s = enemy.GetTurretAngleTo(self);
  1226.     if (abs(e2s) < ENEMY_REL_ANG)
  1227.     {
  1228.         e2s = 0.0;
  1229.     }
  1230.     else
  1231.     {
  1232.         if (e2s > 0)
  1233.             e2s = abs(e2s / angSpRight);
  1234.         else
  1235.             e2s = abs(e2s / angSpLeft);
  1236.     }
  1237.    
  1238.     //double enemy_ang = abs(enemy.GetAngleTo(self));
  1239.     //double enemy_ang_fact = ((enemy_ang < M_PI / 4.0) || (enemy_ang > 3.0 * M_PI / 4.0)) ? 3.0 : 1.0;
  1240.    
  1241.     double enemy_rel_tics = ENEMY_REL_MYANG_TICS * (self.remaining_reloading_time() / self.reloading_time() + 0.5)/1.5;
  1242.     double enemy_rel_ang_tics = dist < 500.0 ? ENEMY_REL_ANG_TICS : 3.0*ENEMY_REL_ANG_TICS;
  1243.     return res * (enemy_rel_tics + s2e) * (enemy_rel_ang_tics + e2s)/ enemy_rel_tics/enemy_rel_ang_tics;// / enemy_ang_fact;
  1244. }
  1245.  
  1246. // Чем больше, тем хуже
  1247. double bonusRel(const Tank &self, Bonus bonus)
  1248. {
  1249.     double safRel = P(self).dist(P(bonus));
  1250.     if (safRel < 150.0)
  1251.         safRel = 150;
  1252.    
  1253.     safRel *= pow(2, safRel / BONUS_REL_EXP_DIST);
  1254.     if (obstacleExist(P(bonus), self, &self, 0, false, self.width(), false))
  1255.         safRel *= 4;
  1256.    
  1257.     double s2b = abs(self.GetAngleTo(bonus));
  1258.    
  1259.     if (s2b < (M_PI_2))
  1260.         return (BONUS_REL_ANG + s2b) * safRel;
  1261.     else
  1262.         return (BONUS_REL_ANG + (M_PI - s2b)) / self.engine_rear_power_factor() * safRel;
  1263. }
  1264.  
  1265. bool isDangerous(const Tank &t)
  1266. {
  1267.     return !t.teammate() && isAlive(t);
  1268. }
  1269.  
  1270. int getNumberOfDangerousTanks(P p);
  1271.  
  1272. double bonusRel2(const Tank &self, const Bonus &bonus)
  1273. {
  1274.     double rel = bonusRel(self, bonus);
  1275.     int dt = getNumberOfDangerousTanks(P(bonus));
  1276.     if (dt > 0)
  1277.         rel *= pow(1.2, dt);
  1278.    
  1279.     double res = rel;
  1280.     double dist = P(self).dist(P(bonus));
  1281.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1282.     {
  1283.         Tank &t = g_all_tanks[i];
  1284.         if (isDangerous(t))
  1285.         {
  1286.             double edist = P(t).dist(P(bonus));
  1287.            
  1288.             if (edist < dist)
  1289.             {
  1290.                 res = max(res, rel * sqr(dist / (edist)));
  1291.             }
  1292.         }
  1293.     }
  1294.    
  1295.     double health = (double)self.crew_health() / (double)self.crew_max_health();
  1296.     double dural = (double)self.hull_durability() / (double)self.hull_max_durability();
  1297.     int ammo = self.premium_shell_count();
  1298.    
  1299.     if (bonus.type() == MEDIKIT)
  1300.     {
  1301.         if(health < 0.25)
  1302.             res /= 6.0;
  1303.         else if (health < 0.5)
  1304.             res /= 2.0;
  1305.         else if (health > 0.95)
  1306.             res *= 4.0;
  1307.         else if (health > 0.99)
  1308.             res *= 10.0;
  1309.     }
  1310.     else if (bonus.type() == REPAIR_KIT)
  1311.     {
  1312.         if(dural < 0.25)
  1313.             res /= 6.0;
  1314.         else if (dural < 0.5)
  1315.             res /= 2.0;
  1316.         else if (dural > 0.95)
  1317.             res *= 4.0;
  1318.         else if (dural > 0.99)
  1319.             res *= 10.0;
  1320.     }
  1321.     else if (bonus.type() == AMMO_CRATE)
  1322.     {
  1323.         if (ammo == 0)
  1324.             res /= 2.0;
  1325.         else if (ammo == 1)
  1326.             res /= 1.5;
  1327.     }
  1328.    
  1329.     return res;
  1330. }
  1331.  
  1332. // Чем меньше, тем опаснее
  1333. double safetyPointRel(P p, const Tank &self, const Tank &t, bool dontCheckTeemmateTanks)
  1334. {
  1335.     double dist = P(t).dist(p);
  1336.     double res = dist;
  1337.     if (res < 200.0)
  1338.         res = 200.0;
  1339.    
  1340.     if (res < 500.0)
  1341.         res /= 2.0;
  1342.     else if (res > 800.0)
  1343.         res *= 2.0;
  1344.    
  1345.     //res *= pow(2.0, res / SAFETY_DIST_POW);
  1346.    
  1347.     double t2p = abs(t.GetTurretAngleTo(p.x, p.y));
  1348.     double next_t2p = t2p;
  1349.     Tank *oldData = tankData.get(t);
  1350.     if (oldData)
  1351.     {
  1352.         double dalpha = (t.angle() + t.turret_relative_angle()) - (oldData->angle() + oldData->turret_relative_angle());
  1353.         while (dalpha > M_PI)
  1354.             dalpha -= 2.0 * M_PI;
  1355.        
  1356.         while (dalpha < -M_PI)
  1357.             dalpha += 2.0 * M_PI;
  1358.        
  1359.         next_t2p -= dalpha * 10.0;
  1360.        
  1361.         while (next_t2p > M_PI)
  1362.             next_t2p -= 2.0 * M_PI;
  1363.        
  1364.         while (next_t2p < -M_PI)
  1365.             next_t2p += 2.0 * M_PI;
  1366.     }
  1367.    
  1368.     // Танк не столь опасен, если есть танк более близкий к нему чем я
  1369.     bool closer_tank_exists = false;
  1370.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1371.     {
  1372.         Tank &e = g_all_tanks[i];
  1373.         if (!tankEq(self, e) && isAlive(e) && e.player_name() != t.player_name() && P(e).dist(P(t)) * 1.3 < dist)
  1374.         {
  1375.             closer_tank_exists = true;
  1376.             break;
  1377.         }
  1378.     }
  1379.    
  1380.     if (closer_tank_exists)
  1381.         res *= 4.0;
  1382.    
  1383.     if (obstacleExist(p, t, &self, &t, false, t.width(), dontCheckTeemmateTanks))
  1384.         res *= 5.0;
  1385.    
  1386.     if (t.premium_shell_count() > 0)
  1387.         res /= 3.0;
  1388.    
  1389.     double enemy_crew_health = (double)t.crew_health() / (double)t.crew_max_health();
  1390.     double enemy_dural = (double)t.hull_durability() / (double)t.hull_max_durability();
  1391.  
  1392.     return res * (next_t2p + SAFETY_REL_ANG) / (enemy_crew_health + SAFETY_HEALTH) / (enemy_dural + SAFETY_DURAL);
  1393. }
  1394.  
  1395. int getNumberOfDangerousTanks(P p)
  1396. {
  1397.     int res = 0;
  1398.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1399.     {
  1400.         Tank &t = g_all_tanks[i];
  1401.         if (isDangerous(t))
  1402.         {
  1403.             double t2p = abs(t.GetTurretAngleTo(p.x, p.y));
  1404.             double dist = p.dist(P(t));
  1405.            
  1406.             // Танк не столь опасен, если есть танк более близкий к нему чем я
  1407.             bool closer_tank_exists = false;
  1408.             for (size_t j = 0; j < g_all_tanks.size(); ++j)
  1409.             {
  1410.                 Tank &e = g_all_tanks[j];
  1411.                 if (!tankEq(t, e) && isAlive(e) && e.player_name() != t.player_name() && P(e).dist(P(t)) * 1.3 < dist)
  1412.                 {
  1413.                     closer_tank_exists = true;
  1414.                     break;
  1415.                 }
  1416.             }
  1417.            
  1418.             if (t2p < DANGER_TANK_ANGLE || !closer_tank_exists)
  1419.                 ++res;
  1420.         }
  1421.     }
  1422.     return res;
  1423. }
  1424.  
  1425. int getNumberOfDangerousTanks(P p1, P p2)
  1426. {
  1427.     P dir = p1 - p2;
  1428.     double len = dir.len();
  1429.     dir = dir / len;
  1430.    
  1431.     int res = 0;
  1432.     while (len > 0.0)
  1433.     {
  1434.         res = max(res, getNumberOfDangerousTanks(p2));
  1435.         len -= DANGER_CHECK_LENBT;
  1436.         p2 = p2 + dir * DANGER_CHECK_LENBT;
  1437.     }
  1438.     return res;
  1439. }
  1440.  
  1441. double teemTanksDistanceRel(P p, const Tank &self)
  1442. {
  1443.     double result = 1.0;
  1444.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1445.     {
  1446.         Tank &t = g_all_tanks[i];
  1447.         if (t.teammate() && !tankEq(self, t) && isAlive(t))
  1448.         {
  1449.             double dist = P(self).dist(t);
  1450.             if (dist < TEEMMATE_MIN_DIST)
  1451.             {
  1452.                 result *= pow(2.0, (TEEMMATE_MIN_DIST - dist) / TEEMMATE_MIN_DIST_FACTOR);
  1453.             }
  1454.            
  1455.             if (dist > TEEMMATE_MAX_DIST)
  1456.             {
  1457.                 result *= pow(2.0, (dist - TEEMMATE_MAX_DIST) / TEEMMATE_MAX_DIST_FACTOR);
  1458.             }
  1459.         }
  1460.     }
  1461.     //LOG("teemTanksDistanceRel " << result);
  1462.     return result;
  1463. }
  1464.  
  1465. double bordersRel(P p)
  1466. {
  1467.     double result = 1.0;
  1468.    
  1469.     if (p.x < BORDER_MIN_DIST)
  1470.         result *= pow(2.0, (BORDER_MIN_DIST - p.x) / BORDER_MIN_DIST_FACTOR);
  1471.    
  1472.     if (p.y < BORDER_MIN_DIST)
  1473.         result *= pow(2.0, (BORDER_MIN_DIST - p.y) / BORDER_MIN_DIST_FACTOR);
  1474.    
  1475.     if ((g_world->width() - p.x) < BORDER_MIN_DIST)
  1476.         result *= pow(2.0, (BORDER_MIN_DIST - (g_world->width() - p.x)) / BORDER_MIN_DIST_FACTOR);
  1477.    
  1478.     if ((g_world->height() - p.y) < BORDER_MIN_DIST)
  1479.         result *= pow(2.0, (BORDER_MIN_DIST - (g_world->height() - p.y)) / BORDER_MIN_DIST_FACTOR);
  1480.    
  1481.     return result;
  1482. }
  1483.  
  1484. // Чем меньше, тем опаснее
  1485. double safetyRel(P p, const Tank &self, bool useTeemTanksDistanceRel)
  1486. {
  1487.     double res = 0.0;
  1488.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1489.     {
  1490.         Tank &t = g_all_tanks[i];
  1491.         if (isDangerous(t))
  1492.         {
  1493.             double cur_rel = safetyPointRel(p, self, t, true);
  1494.             if (cur_rel < 1e-20)
  1495.                 cur_rel = 1e-20;
  1496.             res += 1.0/cur_rel;
  1497.         }
  1498.     }
  1499.    
  1500.     if (res > 0.0)
  1501.         res = 1.0 / res;
  1502.     else
  1503.         res = 1e20;
  1504.    
  1505.     if (useTeemTanksDistanceRel)
  1506.         res /= teemTanksDistanceRel(p, self);
  1507.    
  1508.     //LOG("R " << res);
  1509.     double dist = p.dist(P(self));
  1510.    
  1511.     return res / (dist + SAFETY_DIST) / pow(2.0, max(0.0, (dist - SAFETY_DIST)) / 200.0) / bordersRel(p);
  1512. }
  1513.  
  1514. #define ENEMY_TANK_CLOSE_POS 150.0
  1515.  
  1516. P getSafetyPoint(Tank &self)
  1517. {
  1518.     vector<P> points;
  1519.     points.reserve(fieldPoints.points.size() + 6*4);
  1520.     for (size_t i = 0; i < fieldPoints.points.size(); ++i)
  1521.         points.push_back(fieldPoints.points[i] * P(g_world->width(), g_world->height()));
  1522.    
  1523.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1524.     {
  1525.         Tank &t = g_all_tanks[i];
  1526.        
  1527.         if (isDangerous(t))
  1528.         {
  1529.             P ahead = aheadDir(t);
  1530.             points.push_back(P(t) + ahead * ENEMY_TANK_CLOSE_POS);
  1531.             points.push_back(P(t) - ahead * ENEMY_TANK_CLOSE_POS);
  1532.             points.push_back(P(t) - fireDir(t) * ENEMY_TANK_CLOSE_POS);
  1533.         }
  1534.         else if (!isAlive(t))
  1535.         {
  1536.             P ahead = aheadDir(t);
  1537.             P right = rightDir(t);
  1538.             points.push_back(P(t) + ahead * ENEMY_TANK_CLOSE_POS);
  1539.             points.push_back(P(t) - ahead * ENEMY_TANK_CLOSE_POS);
  1540.             points.push_back(P(t) + right * ENEMY_TANK_CLOSE_POS);
  1541.             points.push_back(P(t) - right * ENEMY_TANK_CLOSE_POS);
  1542.         }
  1543.     }
  1544.    
  1545.     double rel = 0.0;
  1546.    
  1547.     P result = P(g_world->width() / 2.0, g_world->height() / 2.0);
  1548.     for (size_t i = 0; i < points.size(); ++i)
  1549.     {
  1550.         P p = clampPoint(points[i]);
  1551.        
  1552.         double curRel = safetyRel(p, self, true);
  1553.         LOG("SAF " << p << " " << curRel);
  1554.         if (curRel > rel)
  1555.         {
  1556.             rel = curRel;
  1557.             result = p;
  1558.         }
  1559.     }
  1560.    
  1561.     return result;
  1562. }
  1563.  
  1564. // Расстояние от точки до отрезка со знаком
  1565. double distFromLine(P p, P from, P c, bool &ok)
  1566. {
  1567.     P sp = p - from;
  1568.     double sp_len = sp.len();
  1569.     sp = sp / sp_len;
  1570.     P so = c - from;
  1571.     P proj = sp * (dot(sp, so));
  1572.     if (dot(sp, proj) > 0.0 && proj.len() < sp_len)
  1573.     {
  1574.         ok = true;
  1575.         return cross(sp, so);
  1576.     }
  1577.     else
  1578.     {
  1579.         ok = false;
  1580.         return 1e20;
  1581.     }
  1582. }
  1583.  
  1584. bool checkObstacle(Unit &unit, P p, P from, double halfWindow, double border = 0.0)
  1585. {
  1586.     P center = P(unit);
  1587.     P fwd = aheadDir(unit) * (unit.width() / 2.0 + border);
  1588.     P right = rightDir(unit) * (unit.height() / 2.0 + border);
  1589.    
  1590.     P p1 = center + fwd + right;
  1591.     P p2 = center + fwd - right;
  1592.     P p3 = center - fwd + right;
  1593.     P p4 = center - fwd - right;
  1594.    
  1595.     double d[4];
  1596.     bool ok[4];
  1597.     d[0] = distFromLine(p, from, p1, ok[0]);
  1598.     d[1] = distFromLine(p, from, p2, ok[1]);
  1599.     d[2] = distFromLine(p, from, p3, ok[2]);
  1600.     d[3] = distFromLine(p, from, p4, ok[3]);
  1601.    
  1602.     int pos = 0, neg = 0;
  1603.     for (int i = 0; i < 4; ++i)
  1604.     {
  1605.         if (!ok[i])
  1606.             continue;
  1607.        
  1608.         if (d[i] > 0.0)
  1609.             pos++;
  1610.         else
  1611.             neg++;
  1612.        
  1613.         if (pos > 0 && neg > 0) // разные знаки - прямая проходит сквозь прямоугольник
  1614.             return true;
  1615.        
  1616.         if (abs(d[i]) <= halfWindow)
  1617.             return true;
  1618.     }
  1619.    
  1620.     return false;
  1621. }
  1622.  
  1623. // Проверка наличия препятствий на пути летящего снаряда или танка
  1624. bool obstacleExist(P p, P from, const Tank *except_tank1, const Tank *except_tank2, bool withBonuses, double halfWindow, bool dontCheckTeemmateTanks)
  1625. {
  1626.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1627.     {
  1628.         Tank t = g_all_tanks[i];
  1629.         if ((!except_tank1 || !tankEq(*except_tank1, t)) && (!except_tank2 || !tankEq(*except_tank2, t)))
  1630.         {
  1631.             if (dontCheckTeemmateTanks && t.teammate() && isAlive(t))
  1632.                 continue;
  1633.            
  1634.             double border = t.teammate() ? TEEMMATE_TANK_SHELL_BORDER : 0.0;
  1635.             if (checkObstacle(t, p, from, halfWindow, border))
  1636.                 return true;
  1637.         }
  1638.     }
  1639.    
  1640.     if (withBonuses)
  1641.     {
  1642.         vector<Bonus> bonuses = g_world->bonuses();
  1643.         for (size_t i = 0; i < bonuses.size(); ++i)
  1644.         {
  1645.             Bonus b = bonuses[i];
  1646.             if (checkObstacle(b, p, from, halfWindow))
  1647.                 return true;
  1648.         }
  1649.     }
  1650.    
  1651.     vector<Obstacle> obstacles = g_world->obstacles();
  1652.     for (size_t i = 0; i < obstacles.size(); ++i)
  1653.     {
  1654.         Obstacle o = obstacles[i];
  1655.         if (checkObstacle(o, p, from, halfWindow))
  1656.             return true;
  1657.     }
  1658.    
  1659.     return false;
  1660. }
  1661.  
  1662. struct SpeedHistory
  1663. {
  1664.     vector<deque<double> > hist;
  1665.    
  1666.     void set(int tank, double v)
  1667.     {
  1668.         while(hist.size() < tank + 1)
  1669.             hist.push_back(deque<double>());
  1670.        
  1671.         deque<double> &h = hist[tank];
  1672.        
  1673.         if (h.size() > SPEED_HIST_SIZE)
  1674.             h.pop_front();
  1675.        
  1676.         h.push_back(v);
  1677.     }
  1678.    
  1679.     double getMaxSpeed(int tank)
  1680.     {
  1681.         while(hist.size() < tank + 1)
  1682.             hist.push_back(deque<double>());
  1683.        
  1684.         deque<double> &h = hist[tank];
  1685.         double res = 0.0;
  1686.         for (size_t i = 0; i < h.size(); ++i)
  1687.             res = max(res, h[i]);
  1688.        
  1689.         return res;
  1690.     }
  1691. } speedHistory;
  1692.  
  1693. bool stackedAtWall(Tank self)
  1694. {
  1695.     if (speedHistory.getMaxSpeed(self.teammate_index()) > STACKED_AT_WALL_VEL)
  1696.     {
  1697.         return false;
  1698.     }
  1699.    
  1700.     double ang = self.GetAngleTo(10000.0, self.y());
  1701.     if ((abs(ang) < STACKED_AT_WALL_ANG) && self.x() > (g_world->width() - self.width() / 2.0 - STACKED_AT_WALL_DIST))
  1702.         return true;
  1703.    
  1704.     ang = self.GetAngleTo(-10000.0, self.y());
  1705.     if ((abs(ang) < STACKED_AT_WALL_ANG) && self.x() < (self.width() / 2.0 + STACKED_AT_WALL_DIST))
  1706.         return true;
  1707.    
  1708.     ang = self.GetAngleTo(self.x(), -10000.0);
  1709.     if ((abs(ang) < STACKED_AT_WALL_ANG) && self.y() < (self.width() / 2.0 + STACKED_AT_WALL_DIST))
  1710.         return true;
  1711.    
  1712.     ang = self.GetAngleTo(self.x(), 10000.0);
  1713.     if ((abs(ang) < STACKED_AT_WALL_ANG) && self.y() > (g_world->height() - self.width() / 2.0 - STACKED_AT_WALL_DIST))
  1714.         return true;
  1715.    
  1716.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1717.     {
  1718.         Tank &t = g_all_tanks[i];
  1719.         if (!tankEq(self, t))
  1720.         {
  1721.             if (abs(self.GetAngleTo(t)) < (M_PI / 3.0) && P(self).dist(P(t)) < ((self.width() + t.width()) * 0.75))
  1722.             {
  1723.                 LOG("stackedAtWall by TANK");
  1724.                 return true;
  1725.             }
  1726.         }
  1727.     }
  1728.    
  1729.     return false;
  1730. }
  1731.  
  1732. struct MoveBackwardFrame
  1733. {
  1734.     vector<int> tankFrame;
  1735.    
  1736.     int get(int tank)
  1737.     {
  1738.         while(tankFrame.size() < tank + 1)
  1739.             tankFrame.push_back(0);
  1740.        
  1741.         return tankFrame[tank];
  1742.     }
  1743.    
  1744.     void set(int tank, int frame)
  1745.     {
  1746.         while(tankFrame.size() < tank + 1)
  1747.             tankFrame.push_back(0);
  1748.        
  1749.         tankFrame[tank] = frame;
  1750.     }
  1751. } moveBackwardFrame;
  1752.  
  1753. struct OldBonus
  1754. {
  1755.     vector<P> bonus;
  1756.    
  1757.     P get(int tank)
  1758.     {
  1759.         while(bonus.size() < tank + 1)
  1760.             bonus.push_back(P(0.0, 0.0));
  1761.        
  1762.         return bonus[tank];
  1763.     }
  1764.    
  1765.     void set(int tank, P b)
  1766.     {
  1767.         while(bonus.size() < tank + 1)
  1768.             bonus.push_back(P(0.0, 0.0));
  1769.        
  1770.         bonus[tank] = b;
  1771.     }
  1772. } oldBonus;
  1773.  
  1774. enum ShellDangerMoveDirType
  1775. {
  1776.         SD_NONE,
  1777.         SD_FW,
  1778.         SD_BK,
  1779.         SD_RF,
  1780.         SD_LF,
  1781.         SD_RB,
  1782.         SD_LB,
  1783.         SD_R,
  1784.         SD_L,
  1785.         SD_RF0,
  1786.         SD_LF0,
  1787.         SD_RB0,
  1788.         SD_LB0
  1789.        
  1790. };
  1791.  
  1792. const char *ShellDangerMoveDirTypeNames[] = {"SD_NONE", "SD_FW", "SD_BK", "SD_RF", "SD_LF", "SD_RB", "SD_LB", "SD_R", "SD_L", "SD_RF0", "SD_LF0", "SD_RB0", "SD_LB0"};
  1793.  
  1794. struct ShellDangerMoveDirData
  1795. {
  1796.     ShellDangerMoveDirType type;
  1797.     int frame;
  1798. };
  1799.  
  1800. struct ShellDangerMoveDir
  1801. {
  1802.     vector<ShellDangerMoveDirData> dir;
  1803.    
  1804.     ShellDangerMoveDirType get(int tank, int frame)
  1805.     {
  1806.         ShellDangerMoveDirData d;
  1807.         d.frame = frame;
  1808.         d.type = SD_NONE;
  1809.        
  1810.         while(dir.size() < tank + 1)
  1811.             dir.push_back(d);
  1812.        
  1813.         d = dir[tank];
  1814.         if(d.frame >= frame)
  1815.             return d.type;
  1816.         return SD_NONE;
  1817.        
  1818.     }
  1819.    
  1820.     void set(int tank, ShellDangerMoveDirType type, int frame, int frames)
  1821.     {
  1822.         ShellDangerMoveDirData d;
  1823.         d.frame = frame;
  1824.         d.type = SD_NONE;
  1825.        
  1826.         while(dir.size() < tank + 1)
  1827.             dir.push_back(d);
  1828.        
  1829.         d.frame = frame + frames;
  1830.         d.type = type;
  1831.        
  1832.         dir[tank] = d;
  1833.        
  1834.         //LOG("SHELL DANGER SET " << frames << " " << d.frame);
  1835.     }
  1836. } shellDangerMoveDir;
  1837.  
  1838. double angleOf(P p)
  1839. {
  1840.     return atan2(p.y, p.x);
  1841. }
  1842.  
  1843. // Чтоб не стукаться при езде в другие танки
  1844. double safetyRelWithFarPointObstacleCheck(P p, const Tank &self)
  1845. {
  1846.     double cur_safety = safetyRel(p, self, true);
  1847.    
  1848.     P dir = p - P(self);
  1849.     double len = dir.len();
  1850.     if (len < 1e-6)
  1851.         return cur_safety;
  1852.    
  1853.     dir = dir / len;
  1854.     double check_distance = 1.5 * MOVE_SEGMENT_LENGTH;
  1855.     P far_point = p + dir * MOVE_SEGMENT_LENGTH;
  1856.    
  1857.     MyUnit space;
  1858.     space.position = (P(self) + far_point) / 2.0;
  1859.     space.angle = angleOf(dir);
  1860.     space.size = P(P(self).dist(far_point), self.height());
  1861.    
  1862.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  1863.     {
  1864.         Tank &t = g_all_tanks[i];
  1865.         if (!tankEq(self, t) && !isUnitsNotIntersects(space, t))
  1866.         {
  1867.             LOG("far point " << far_point.x << " " << far_point.y);
  1868.             LOG("space " << space.position.x << " " << space.position.y << " " << space.size.x << " " << space.size.y);
  1869.             if (t.teammate())
  1870.             {
  1871.                 LOG("TEEMATE COLISION DANGER");
  1872.                 return cur_safety / 20.0;
  1873.             }
  1874.             else
  1875.             {
  1876.                 LOG("ENEMY COLISION DANGER");
  1877.                 if ((double) self.hull_durability() / (double) self.hull_max_durability() < 0.8)
  1878.                     return cur_safety / 5.0;
  1879.                 return cur_safety;
  1880.             }
  1881.         }
  1882.     }
  1883.    
  1884.     return cur_safety;
  1885. }
  1886.  
  1887.  
  1888.  
  1889. // enemy_side = 1 - враг справа
  1890. // Езда к цели с возможным отклонением от прямой вправо/влево
  1891. void moveToByCurve(Tank self, P p, model::Move& move, int enemy_side, bool fast)
  1892. {
  1893.     P dir = p - P(self);
  1894.     double dist = dir.len();
  1895.     if (dist < 2.0 * MOVE_SEGMENT_LENGTH)
  1896.     {
  1897.         moveTo(self, p, move, enemy_side, fast);
  1898.         return;
  1899.     }
  1900.    
  1901.     dir = dir / dist;
  1902.     P left = P(dir.y, -dir.x) * MOVE_SEGMENT_LENGTH;
  1903.    
  1904.     P points[5];
  1905.     size_t points_count = 1;
  1906.     points[0] = P(self) + dir * MOVE_SEGMENT_LENGTH;
  1907.    
  1908.     if (enemy_side <= 0)
  1909.         points[points_count++] = points[0] + left * 0.4;
  1910.  
  1911.     if (enemy_side <= 0)
  1912.         points[points_count++] = points[0] + left * 0.8;
  1913.    
  1914.     if (enemy_side >= 0)
  1915.         points[points_count++] = points[0] - left * 0.8;
  1916.    
  1917.     if (enemy_side >= 0)
  1918.         points[points_count++] = points[0] - left * 0.4;
  1919.    
  1920.     P target_point = points[0];
  1921.     double safety = safetyRelWithFarPointObstacleCheck(points[0], self);
  1922.     for (int i = 1; i < points_count; ++i)
  1923.     {
  1924.         if (!obstacleExist(points[i], self, &self, 0, false, self.width() / 2.0, false))
  1925.         {
  1926.             double cur_safety = safetyRelWithFarPointObstacleCheck(points[i], self);
  1927.            
  1928.             if (cur_safety > safety)
  1929.             {
  1930.                 safety = cur_safety;
  1931.                 target_point = points[i];
  1932.             }
  1933.         }
  1934.     }
  1935.    
  1936.     //LOG("moveToByCurve " << p.x << " " << p.y << " " << target_point.x << " " << target_point.y << " " << self.x() << " " << self.y());
  1937.     moveTo(self, target_point, move, enemy_side, fast);
  1938. }
  1939.  
  1940. struct SimulationResult
  1941. {
  1942.     SimulationResult()
  1943.     {
  1944.         wall_collision = false;
  1945.         other_tank_collision = false;
  1946.         shell_collision = true;
  1947.         ricochet = false;
  1948.         obstacle_collision = false;
  1949.         keep_dist_shells = 0;
  1950.         impact_dist = 0.0;
  1951.         t = 0;
  1952.     }
  1953.    
  1954.     bool collided() const
  1955.     {
  1956.         return other_tank_collision || shell_collision || obstacle_collision;
  1957.     }
  1958.    
  1959.     bool wall_collision, other_tank_collision, shell_collision, obstacle_collision, ricochet;
  1960.     int keep_dist_shells;
  1961.     double impact_dist, cosa;
  1962.     MyTank my_tank;
  1963.     double t;
  1964. };
  1965.  
  1966. int getNumberOfDangerousShells(const std::vector<MyShell> shells, const MyTank &my_tank, bool *obstacle_exists, int &keep_dist_shells, double &impact_dist)
  1967. {
  1968.     int shell_danger = 0;
  1969.     keep_dist_shells = 0;
  1970.     for (size_t i = 0; i < shells.size(); ++i)
  1971.     {
  1972.         const MyShell &shell = shells[i];
  1973.         P ss = my_tank.position - shell.position;
  1974.         P rel_vel = shell.velocity - my_tank.velocity;
  1975.         P rel_vel_norm = rel_vel / (rel_vel.len() + 1e-10);
  1976.            
  1977.         P ss_norm = ss / ss.len();
  1978.            
  1979.         double cosa = dot(ss_norm, rel_vel_norm);
  1980.         if (cosa < 0)
  1981.             continue;
  1982.            
  1983.         double sina = sqrt(1.0 - sqr(cosa));
  1984.         double shellImpactDistFromCenter = ss.len() * sina;
  1985.         impact_dist = shellImpactDistFromCenter;
  1986.        
  1987.         double danger_dist = (my_tank.size.len() + SHELL_WIDTH)/2.0 + SHELL_SHELL_SAFE_DIST;
  1988.         double keep_dist = (my_tank.size.len() + SHELL_WIDTH)/2.0 + SHELL_SHELL_KEEP_DIST;
  1989.        
  1990.         if (shellImpactDistFromCenter <= keep_dist)
  1991.         {
  1992.             bool obstacle_exist;
  1993.             if (obstacle_exists)
  1994.             {
  1995.                 obstacle_exist = obstacle_exists[i];
  1996.             }
  1997.             else
  1998.             {
  1999.                 P shell_target = shell.position + shell.velocity * ss.len();
  2000.                 obstacle_exist = obstacleExist(shell_target, shell.position, my_tank.tank, 0, true, REAL_SHELL_WIDTH / 2.0, true);
  2001.             }
  2002.            
  2003.             if (!obstacle_exist)
  2004.             {
  2005.                 if (shellImpactDistFromCenter <= danger_dist)
  2006.                 {
  2007.                     ++shell_danger;
  2008.                 }
  2009.                 else
  2010.                 {
  2011.                     ++keep_dist_shells;
  2012.                 }
  2013.             }
  2014.             else
  2015.             {
  2016.                 //LOG("shellImpactDistFromCenter1 " << shellImpactDistFromCenter << " " << obstacle_exist << " " << shell.position.x << " " << shell.position.y);
  2017.             }
  2018.         }
  2019.         else
  2020.         {
  2021.             //LOG("shellImpactDistFromCenter2 " << shellImpactDistFromCenter << " " << shell.position.x << " " << shell.position.y);
  2022.         }
  2023.     }
  2024.     //LOG("getNumberOfDangerousShells " << shell_danger << " " << keep_dist_shells);
  2025.     return shell_danger;
  2026. }
  2027.  
  2028. MyTank getTankAfterFire(const MyTank &t)
  2029. {
  2030.     MyTank res = t;
  2031.     double dv;
  2032.     switch (t.type)
  2033.     {
  2034.         case model::MEDIUM:
  2035.             dv = 1.58333;
  2036.             break;
  2037.         case model::HEAVY:
  2038.             dv = 0.812500;
  2039.             break;
  2040.         case model::TANK_DESTROYER:
  2041.             dv = 1.074074;
  2042.             break;
  2043.     }
  2044.    
  2045.     res.velocity = res.velocity - fireDir(t) * dv;
  2046.     res.position = res.position + res.velocity;
  2047.     return res;
  2048. }
  2049.  
  2050. // Проверка будет ли при попадании снаряда рикошет
  2051. bool isRicochet(const MyTank &self, const MyShell shell, double &cosa)
  2052. {
  2053.     cosa = 1.0;
  2054.    
  2055.     if (shell.type == model::PREMIUM)
  2056.         return false;
  2057.    
  2058.     P ahead_dir = aheadDir(self);
  2059.     P right_dir = rightDir(self);
  2060.     P back_dir = ahead_dir * (-1.0);
  2061.     P left_dir = right_dir * (-1.0);
  2062.    
  2063.     P fl = ahead_dir * (self.size.x / 2.0) + left_dir * (self.size.y / 2.0);
  2064.     fl = fl / fl.len();
  2065.     P fr = ahead_dir * (self.size.x / 2.0) + right_dir * (self.size.y / 2.0);
  2066.     fr = fr / fr.len();
  2067.     P bl = back_dir * (self.size.x / 2.0) + left_dir * (self.size.y / 2.0);
  2068.     bl = bl / bl.len();
  2069.     P br = back_dir * (self.size.x / 2.0) + right_dir * (self.size.y / 2.0);
  2070.     br = br /br.len();
  2071.    
  2072.     P shell_ahead = aheadDir(shell);
  2073.     P sf = shell.position + shell_ahead * (shell.size.x / 2.0) - self.position;
  2074.     P sl = sf - rightDir(shell) * (shell.size.y / 2.0);
  2075.     P sr = sf + rightDir(shell) * (shell.size.y / 2.0);
  2076.     sl = sl / (sl.len() + 1e-10);
  2077.     sr = sr / (sr.len() + 1e-10);
  2078.    
  2079.     bool q1 = dot(ahead_dir, shell.velocity) > 0.0;
  2080.     bool q2 = dot(right_dir, shell.velocity) > 0.0;
  2081.    
  2082.     if (q1 && q2)
  2083.     {
  2084.         if (dot(back_dir, sl) > dot(back_dir, bl))
  2085.             cosa = dot(ahead_dir, shell_ahead);
  2086.         else if (dot(back_dir, sr) < dot(back_dir, bl))
  2087.             cosa = dot(right_dir, shell_ahead);
  2088.     }
  2089.     else if (q1 && !q2)
  2090.     {
  2091.         if (dot(back_dir, sr) > dot(back_dir, br))
  2092.             cosa = dot(ahead_dir, shell_ahead);
  2093.         else if (dot(back_dir, sl) < dot(back_dir, br))
  2094.             cosa = dot(left_dir, shell_ahead);
  2095.     }
  2096.     else if (!q1 && q2)
  2097.     {
  2098.         if (dot(ahead_dir, sr) > dot(ahead_dir, fl))
  2099.             cosa = dot(back_dir, shell_ahead);
  2100.         else if (dot(ahead_dir, sl) < dot(ahead_dir, fl))
  2101.             cosa = dot(right_dir, shell_ahead);
  2102.     }
  2103.     else // if (!q1 && !q2)
  2104.     {
  2105.         if (dot(ahead_dir, sl) > dot(ahead_dir, fr))
  2106.             cosa = dot(back_dir, shell_ahead);
  2107.         else if (dot(ahead_dir, sr) < dot(ahead_dir, fr))
  2108.             cosa = dot(left_dir, shell_ahead);
  2109.     }
  2110.     return cosa < 0.5;
  2111. }
  2112.  
  2113. // точное моделирование согласно физике игры
  2114. SimulationResult simulate(const Tank &self, double lin_t, MyMove move, P impact_point)
  2115. {
  2116.     MyTank t = MyTank(self);
  2117.     size_t my_tank_ind = 0;
  2118.     MyMove empty_move;
  2119.    
  2120.     MyTank tanks[9];
  2121.     bool obstacle_exists[20];
  2122.    
  2123.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2124.     {
  2125.         Tank &t = g_all_tanks[i];
  2126.         tanks[i] = MyTank(g_all_tanks[i]);
  2127.         if (tankEq(t, self))
  2128.         {
  2129.             my_tank_ind = i;
  2130.         }
  2131.     }
  2132.    
  2133.     vector<Shell> all_shells = g_world->shells();
  2134.     std::vector<MyShell> shells;
  2135.     shells.reserve(all_shells.size());
  2136.     for (size_t i = 0; i < all_shells.size(); ++i)
  2137.     {
  2138.         const Shell &s = all_shells[i];
  2139.         shells.push_back(MyShell(s));
  2140.  
  2141.         obstacle_exists[i] = false;
  2142.        
  2143.         P ss = P(self) - P(s);
  2144.         double shell_dist = ss.len();
  2145.         if (shell_dist > 1.0)
  2146.         {
  2147.             P rel_vel = P(s.speed_x(), s.speed_y()) - P(self.speed_x(), self.speed_y());
  2148.             P rel_vel_norm = rel_vel / (rel_vel.len() + 1e-10);
  2149.            
  2150.             P ss_norm = ss / shell_dist;
  2151.            
  2152.             double cosa = dot(ss_norm, rel_vel_norm);
  2153.             if (cosa > 0.0)
  2154.             {
  2155.                
  2156.                 double sina = sqrt(1.0 - sqr(cosa));
  2157.                 double shellImpactDistFromCenter = shell_dist * sina;
  2158.                 if (shellImpactDistFromCenter <= ((P(self.width(), self.height()).len() + SHELL_WIDTH)/2.0 + SHELL_SHELL_KEEP_DIST))
  2159.                 {
  2160.                     P shell_target = P(s) + aheadDir(s)*shell_dist;
  2161.                     obstacle_exists[i] = obstacleExist(shell_target, all_shells[i], &self, 0, true, REAL_SHELL_WIDTH / 2.0, true);
  2162.                 }
  2163.             }
  2164.         }
  2165.     }
  2166.    
  2167.     double max_t = lin_t;
  2168.    
  2169.     LOG("SIMULATION " << max_t << " " << move.left_track_power << " " << move.right_track_power);
  2170.    
  2171.     SimulationResult result;
  2172.     for (double t = 1.0; t <= 1.5*max_t; ++t)
  2173.     {
  2174.         result.t = t;
  2175.        
  2176.         for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2177.         {
  2178.             MyMove &tank_move = (i == my_tank_ind) ? move : empty_move;
  2179.             tanks[i] = predictTankPos(tanks[i], tank_move);
  2180.         }
  2181.        
  2182.         result.my_tank = tanks[my_tank_ind];
  2183.        
  2184.         if (isUnitIntersectsWalls(tanks[my_tank_ind]))
  2185.         {
  2186.             result.wall_collision = true;
  2187.             LOG("SIM wall_collision " << t);
  2188.         }
  2189.        
  2190.         for (size_t i = 0; i < g_all_obstacles.size(); ++i)
  2191.         {
  2192.             Obstacle &o = g_all_obstacles[i];
  2193.             if (!isUnitsNotIntersects(tanks[my_tank_ind], tanks[i]))
  2194.             {
  2195.                 result.obstacle_collision = true;
  2196.                 LOG("SIM obstacle_collision " << t);
  2197.                 return result;
  2198.             }
  2199.         }
  2200.        
  2201.         for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2202.         {
  2203.             if (my_tank_ind != i)
  2204.             {
  2205.                 if (!isUnitsNotIntersects(tanks[my_tank_ind], tanks[i]))
  2206.                 {
  2207.                     result.other_tank_collision = true;
  2208.                     LOG("SIM other_tank_collision " << t);
  2209.                     return result;
  2210.                 }
  2211.             }
  2212.         }
  2213.        
  2214.         for (size_t i = 0; i < all_shells.size(); ++i)
  2215.         {
  2216.             shells[i] = predictShellPos(shells[i]);
  2217.            
  2218.             if (!isUnitsNotIntersects(tanks[my_tank_ind], shells[i]))
  2219.             {
  2220.                 result.shell_collision = true;
  2221.                 result.ricochet = isRicochet(tanks[my_tank_ind], shells[i], result.cosa);
  2222.                 LOG("SIM shell_collision " << t << " ric " << result.ricochet << " cosa " << result.cosa);
  2223.                 return result;
  2224.             }
  2225.         }
  2226.        
  2227.         MyTank &my_tank = tanks[my_tank_ind];
  2228.         int keep_dist_shells;
  2229.         double impact_dist;
  2230.         int shell_danger = getNumberOfDangerousShells(shells, my_tank, obstacle_exists, keep_dist_shells, impact_dist);
  2231.        
  2232.         if (shell_danger == 0)
  2233.         {
  2234.             LOG("SIM safety " << t);
  2235.             result.keep_dist_shells = keep_dist_shells;
  2236.             result.impact_dist = impact_dist;
  2237.             result.shell_collision = false;
  2238.             return result;
  2239.         }
  2240.     }
  2241.    
  2242.     return result;
  2243. }
  2244.  
  2245. int deg5(double a)
  2246. {
  2247.     return (int) (a / M_PI * (180.0 / 5.0));
  2248. }
  2249.  
  2250. bool lessSR(const SimulationResult &l, const SimulationResult &r)
  2251. {
  2252.     if (!l.collided() && r.collided())
  2253.         return true;
  2254.    
  2255.     if (l.collided() && !r.collided())
  2256.         return false;
  2257.    
  2258.     if (l.ricochet && !r.ricochet)
  2259.         return true;
  2260.    
  2261.     if (!l.ricochet && r.ricochet)
  2262.         return false;
  2263.    
  2264.     if (deg5(l.cosa) < deg5(r.cosa))
  2265.         return true;
  2266.    
  2267.     if (deg5(l.cosa) > deg5(r.cosa))
  2268.         return false;
  2269.    
  2270.     if (l.t < r.t)
  2271.         return true;
  2272.    
  2273.     if (l.t > r.t)
  2274.         return false;
  2275.    
  2276.     if (l.keep_dist_shells < r.keep_dist_shells)
  2277.         return true;
  2278.    
  2279.     if (l.keep_dist_shells > r.keep_dist_shells)
  2280.         return false;
  2281.    
  2282.     return l.impact_dist > r.impact_dist;
  2283. }
  2284.  
  2285. SimulationResult simulate_one(const Tank &self, double impact_time, P shell_impact_pos, SimulationResult &last_res, ShellDangerMoveDirType &result, ShellDangerMoveDirType type, double left_track_power, double right_track_power)
  2286. {
  2287.     MyMove move;
  2288.     move.left_track_power = left_track_power;
  2289.     move.right_track_power = right_track_power;
  2290.     SimulationResult res = simulate(self, impact_time, move, shell_impact_pos);
  2291.    
  2292.     if (lessSR(res, last_res))
  2293.     {
  2294.         result = type;
  2295.         return res;
  2296.     }
  2297.     else
  2298.     {
  2299.         return last_res;
  2300.     }
  2301. }
  2302.  
  2303. ShellDangerMoveDirType avoidShell(const Tank &self, double impact_time, P shell_impact_pos)
  2304. {
  2305.     ShellDangerMoveDirType result = SD_NONE;
  2306.     double sim_t = impact_time;
  2307.     if (sim_t > MAX_SIMULATION_TIME)
  2308.         sim_t = MAX_SIMULATION_TIME;
  2309.     MyMove move;
  2310.    
  2311.     SimulationResult res;
  2312.     res.shell_collision = true;
  2313.     res.t = sim_t;
  2314.     res.keep_dist_shells = 0;
  2315.    
  2316.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_FW, 1.0, 1.0);
  2317.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_BK, -1.0, -1.0);
  2318.    
  2319.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_RF, 1.0, ROT_POWER);
  2320.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_LF, ROT_POWER, 1.0);
  2321.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_RB, -1.0, -ROT_POWER);
  2322.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_LB, -ROT_POWER, -1.0);
  2323.    
  2324.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_RF0, 1.0, 0.0);
  2325.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_LF0, 0.0, 1.0);
  2326.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_RB0, -1.0, 0.0);
  2327.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_LB0, 0.0, -1.0);
  2328.    
  2329.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_L, -1.0, self.engine_rear_power_factor());
  2330.     res = simulate_one(self, impact_time, shell_impact_pos, res, result, SD_R, self.engine_rear_power_factor(), -1.0);
  2331.    
  2332.     LOG("SIMULATION RESULT " << ShellDangerMoveDirTypeNames[result] << " " << res.t);
  2333.     return result;
  2334. }
  2335.  
  2336. #define MAX_TANKS 9
  2337. bool my_tanks_fired[MAX_TANKS];
  2338. FireType my_tanks_fire_type[MAX_TANKS];
  2339.  
  2340. MyShell newShell(const Tank &self, FireType type)
  2341. {
  2342.     MyShell res;
  2343.     double angle = self.angle() + self.turret_relative_angle();
  2344.     res.angle = angle;
  2345.     P dir = P(cos(angle), sin(angle));
  2346.     res.position = P(self) + dir * self.virtual_gun_length();
  2347.     res.size = P(22.5, 7.5)*1.1;
  2348.     res.angular_speed = 0.0;
  2349.     res.velocity = dir * ((type == model::PREMIUM_FIRE ? 800.0 : 1000.0) / 60.0);
  2350.     res.type = (type == model::PREMIUM_FIRE ? PREMIUM : REGULAR);
  2351.     return res;
  2352. }
  2353.  
  2354. bool newShellCollideOtherMyShell(MyShell ms)
  2355. {
  2356.     vector<MyShell> my_shells;
  2357.     for (size_t i = 0; i < g_all_shells.size(); ++i)
  2358.     {
  2359.         Shell &s = g_all_shells[i];
  2360.         if (s.player_name() == g_self->player_name())
  2361.             my_shells.push_back(MyShell(s));
  2362.     }
  2363.    
  2364.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2365.     {
  2366.         Tank &t = g_all_tanks[i];
  2367.         if (t.teammate() && isAlive(t) && my_tanks_fired[t.teammate_index()])
  2368.             my_shells.push_back(newShell(t, my_tanks_fire_type[t.teammate_index()]));
  2369.     }
  2370.    
  2371.     if (my_shells.empty())
  2372.         return false;
  2373.    
  2374.     while (1)
  2375.     {
  2376.         if (!isUnitIntersectsWalls(ms))
  2377.         {
  2378.             bool all_out = true;
  2379.            
  2380.             for (size_t i = 0; i < my_shells.size(); ++i)
  2381.             {
  2382.                 MyShell &o = my_shells[i];
  2383.                 if (!isUnitIntersectsWalls(o))
  2384.                 {
  2385.                     all_out = false;
  2386.                     if (!isUnitsNotIntersects(ms, o))
  2387.                         return true;
  2388.                 }
  2389.                
  2390.                 my_shells[i] = predictShellPos(o);
  2391.             }
  2392.            
  2393.             ms = predictShellPos(ms);
  2394.            
  2395.             if (all_out)
  2396.                 return false;
  2397.         }
  2398.         else
  2399.         {
  2400.             return false;
  2401.         }
  2402.     }
  2403. }
  2404.  
  2405. int g_wait_for_fire_tanksN;
  2406.  
  2407. void newTick()
  2408. {
  2409.     for (size_t i = 0; i < MAX_TANKS; ++i)
  2410.         my_tanks_fired[i] = false;
  2411.    
  2412.     if(g_tick <= 1)
  2413.     {
  2414.         for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2415.         {
  2416.             Tank &t = g_all_tanks[i];
  2417.             g_danger_frames[TankKey(t)] = 0;
  2418.         }
  2419.     }
  2420.    
  2421.     g_wait_for_fire_tanksN = 0;
  2422. }
  2423.  
  2424. void postFrameNewTick()
  2425. {
  2426.     vector<MyShell> shells;
  2427.     for (size_t i = 0; i < g_all_shells.size(); ++i)
  2428.         shells.push_back(MyShell(g_all_shells[i]));
  2429.    
  2430.     if (shells.empty())
  2431.         return;
  2432.    
  2433.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2434.     {
  2435.         Tank &t = g_all_tanks[i];
  2436.         if (isAlive(t))
  2437.         {
  2438.             int keep_dist_shells;
  2439.             double impact_dist;
  2440.             int sn = getNumberOfDangerousShells(shells, MyTank(t), 0, keep_dist_shells, impact_dist);
  2441.             if (sn > 0)
  2442.                 g_danger_frames[TankKey(t)] = g_tick;
  2443.         }
  2444.     }
  2445. }
  2446.  
  2447. #define RUN_AWAY_DIST 200.0
  2448.  
  2449. void runAway(const Tank &self, model::Move& move)
  2450. {
  2451.     P pts[6];
  2452.     P ahead = aheadDir(self) * RUN_AWAY_DIST;
  2453.     P right = rightDir(self) * (RUN_AWAY_DIST * 0.5);
  2454.     P center = P(self);
  2455.    
  2456.     pts[0] = clampPoint(center + ahead - right);
  2457.     pts[1] = clampPoint(center + ahead);
  2458.     pts[2] = clampPoint(center + ahead + right);
  2459.    
  2460.     pts[3] = clampPoint(center - (ahead - right)*self.engine_rear_power_factor());
  2461.     pts[4] = clampPoint(center - (ahead)*self.engine_rear_power_factor());
  2462.     pts[5] = clampPoint(center - (ahead + right)*self.engine_rear_power_factor());
  2463.    
  2464.     P res = pts[0];
  2465.     double safety = 0.0;
  2466.    
  2467.     for (size_t i = 0; i < 6; ++i)
  2468.     {
  2469.         double sf = safetyRel(pts[i], self, true);
  2470.         if (sf > safety)
  2471.         {
  2472.             safety = sf;
  2473.             res = pts[i];
  2474.         }
  2475.     }
  2476.    
  2477.     LOG("RUN AWAY TO" << res);
  2478.     moveTo(self, res, move, 0, true);
  2479. }
  2480.  
  2481. #define FIRE_THRESHOLD 1.0
  2482.  
  2483. void MyStrategy::Move(Tank self, World world, model::Move& move)
  2484. {
  2485.     g_rel_tangents.clear();
  2486.     if (g_last_targets.count(TankKey(self)))
  2487.         g_last_targets.erase(TankKey(self));
  2488.    
  2489.     if (!isAlive(self))
  2490.         return;
  2491.    
  2492.     g_world = &world;
  2493.     g_all_tanks = world.tanks();
  2494.     g_all_obstacles = world.obstacles();
  2495.     g_all_shells = world.shells();
  2496.     g_self = &self;
  2497.    
  2498.     if (g_team_size > 1)
  2499.     {
  2500.         g_team_alived = 0;
  2501.        
  2502.         for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2503.         {
  2504.             Tank &t = g_all_tanks[i];
  2505.             if (t.teammate() && isAlive(t))
  2506.                 g_team_alived++;
  2507.         }
  2508.     }
  2509.     else
  2510.     {
  2511.         g_team_alived = 1;
  2512.     }
  2513.    
  2514.     bool new_tick = false;
  2515.     if (g_tick != world.tick())
  2516.     {
  2517.         g_tick = world.tick();
  2518.         new_tick = true;
  2519.         newTick();
  2520.     }
  2521.     int frame = world.tick();
  2522.    
  2523.     speedHistory.set(self.teammate_index(), P(self.speed_x(), self.speed_y()).len());
  2524.     if (!moveBackwardFrame.get(self.teammate_index()))
  2525.         moveBackwardFrame.set(self.teammate_index(), MOVE_BACKWARD_AT_START_TICS);
  2526.    
  2527.     Tank *enemy = 0;
  2528.     double rel = 1e20;
  2529.     Tank *potential_fire_tank = 0;
  2530.     int potential_fire_tics = 0;
  2531.    
  2532.     if (frame == 700)
  2533.         potential_fire_tics = 0;
  2534.    
  2535.     for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2536.     {
  2537.         Tank &t = g_all_tanks[i];
  2538.         if (isDangerous(t))
  2539.         {
  2540.             // Определение врага по которому будем целиться
  2541.             double cur_rel = enemyRel(self, t);
  2542.             if (cur_rel < rel)
  2543.             {
  2544.                 rel = cur_rel;
  2545.                 enemy = &t;
  2546.             }
  2547.            
  2548.             // Определение врага, который может выстрелить в нас в течении ближайших нескольких тиков
  2549.             if (t.remaining_reloading_time() <= POTENTIAL_FIRE_TICS && P(t).dist(self) < POTENTIAL_FIRE_MAX_DIST)
  2550.             {
  2551.                 double angle1, angle2;
  2552.                 if (t.angular_speed() > 0.0)
  2553.                 {
  2554.                     angle2 = t.GetTurretAngleTo(self);
  2555.                     angle1 = angle2 - t.angular_speed() * POTENTIAL_FIRE_TICS;
  2556.                 }
  2557.                 else
  2558.                 {
  2559.                     angle1 = t.GetTurretAngleTo(self);
  2560.                     angle2 = angle1 - t.angular_speed() * POTENTIAL_FIRE_TICS;
  2561.                 }
  2562.                
  2563.                 bool intersects = !((angle2 < -POTENTIAL_ANGLE) || (angle1 > POTENTIAL_ANGLE));
  2564.                 if (intersects && !obstacleExist(self, t, &self, &t, false, SHELL_WIDTH / 2.0, true))
  2565.                 {
  2566.                     if (potential_fire_tank)
  2567.                     {
  2568.                         if (potential_fire_tics > (t.remaining_reloading_time() + 10) || (P(*potential_fire_tank).dist(self) < 400.0 && P(t).dist(self) > 400.0 && P(t).dist(self) < 800.0))
  2569.                         {
  2570.                             potential_fire_tics = t.remaining_reloading_time();
  2571.                             potential_fire_tank = &t;
  2572.                         }
  2573.                     }
  2574.                     else
  2575.                     {
  2576.                         potential_fire_tics = t.remaining_reloading_time();
  2577.                         potential_fire_tank = &t;
  2578.                     }
  2579.                 }
  2580.             }
  2581.         }
  2582.     }
  2583.    
  2584.     if (enemy)
  2585.         g_last_targets.insert(std::make_pair(TankKey(self), TankKey(*enemy)));
  2586.    
  2587.     //if (potential_fire_tank)
  2588.         //LOG("POTENTIAL FIRE TANK " << potential_fire_tank->x() << " " << potential_fire_tank->y() << " " << potential_fire_tank->player_name());
  2589.    
  2590.     vector<Bonus> all_bonuses = world.bonuses();
  2591.     Bonus *bonus = 0;
  2592.     Bonus *closest_bonus = 0;
  2593.     Bonus *old_bonus = 0;
  2594.     rel = 1e20;
  2595.     // Определение бонуса за которым мы поедем
  2596.     for (size_t i = 0; i < all_bonuses.size(); ++i)
  2597.     {
  2598.         Bonus &b = all_bonuses[i];
  2599.        
  2600.         if (oldBonus.get(self.teammate_index()) == P(b))
  2601.             old_bonus = &b;
  2602.        
  2603.         for (size_t j = 0; j < g_all_tanks.size(); ++j)
  2604.         {
  2605.             Tank &t = g_all_tanks[j];
  2606.             if (t.teammate() && !tankEq(self, t) && isAlive(t))
  2607.             {
  2608.                 if (oldBonus.get(t.teammate_index()) == P(b) && P(self).dist(b) * 2.0 < P(t).dist(b))
  2609.                     continue;
  2610.             }
  2611.         }
  2612.  
  2613.         double cur_rel = bonusRel2(self, b);
  2614.         if (!bonus || cur_rel < rel)
  2615.         {
  2616.             rel = cur_rel;
  2617.             bonus = &b;
  2618.         }
  2619.        
  2620.         if (b.type() == MEDIKIT || b.type() == REPAIR_KIT)
  2621.         {
  2622.             if (!closest_bonus)
  2623.             {
  2624.                 closest_bonus = &b;
  2625.             }
  2626.             else
  2627.             {
  2628.                 if (P(*closest_bonus).dist(P(self)) > P(b).dist(P(self)))
  2629.                     closest_bonus = &b;
  2630.             }
  2631.         }
  2632.     }
  2633.    
  2634.     if (bonus && bonus->type() == MEDIKIT && self.crew_health() == self.crew_max_health())
  2635.         bonus = 0;
  2636.    
  2637.     if (bonus && bonus->type() == REPAIR_KIT && self.hull_durability() == self.hull_max_durability())
  2638.         bonus = 0;
  2639.    
  2640.     if (bonus && bonus->type() == AMMO_CRATE && self.premium_shell_count() >= 5)
  2641.         bonus = 0;
  2642.    
  2643.     // Добавление некой инерционности, чтоб не дергаться с одного бонуса на другой
  2644.     if (!bonus && closest_bonus)
  2645.     {
  2646.         double closest_bonus_dist = P(*closest_bonus).dist(P(self));
  2647.         if (closest_bonus_dist < CLOSEST_BONUS_DIST)
  2648.         {
  2649.             for (size_t i = 0; i < g_all_tanks.size(); ++i)
  2650.             {
  2651.                 Tank &t = g_all_tanks[i];
  2652.                 if (isDangerous(t))
  2653.                 {
  2654.                     if ((t.crew_health() < t.crew_max_health() && closest_bonus->type() == MEDIKIT) || (t.hull_durability() < t.hull_max_durability() && closest_bonus->type() == REPAIR_KIT))
  2655.                     {
  2656.                         double edist = P(t).dist(*closest_bonus);
  2657.                         if (edist > closest_bonus_dist * 1.4 && edist < closest_bonus_dist * 2.0)
  2658.                         {
  2659.                             bonus = closest_bonus; // Перехват бо