Pull to refresh

Создаем робота на ХабраWars

Lumber room
Всем привет!
Наверно многие уже слышали про проект под названием HabraWars, если коротко — это игра для программеров, в которой вы сами пишите искусственный интеллект для собственного робота на JS.

Я думаю что это будет не первая моя статья на данную тему, хотя бы потому, что здесь я не собираюсь раскрывать всю тему, а лишь ее часть. Но сначала, я думаю, нужно сформировать некую концепцию робота… Сразу говорю, что в js я далеко не спец и вообще мои знания этого языка, на мой взгляд, довольно скудны… Итак, как-же должен выглядеть наш робот изнутри, а изнутри у него должна быть логика, как бы это очевидно не звучало, но логика это довольно сложная штука, она будет управлять роботом, задавая вопросы(типа: «Летит ли в меня(робота) снаряд») и основываясь на ответах вызывать функции, отвечающие за действия робота… Но начну я не с логики(я сам еще не начинал даже ее писать:)), а начну с функций, отвечающих за выполнение действий, порученных логикой.

Значит так, начнем. В этом посте будет обсуждаться именно атака нашего робота(в следующем наверно увороты от пуль, читай защита). Накидываем для начала такой сухой код:
  1. this.enemyId = 0;
  2.  
  3. this.action = function(my_state, enemies, shots, params){
  4.   if(!this.enemyId){// Если мы еще не выбрали врага, то есть это первая итерация данной функции
  5.     /*
  6.     Получаем ID ближайшего врага и записываем его в переменную, которая не изменится при построении
  7.     следующего фрейма игры...
  8.     */
  9.     this.enemyId = this.getNearestEnemy(
  10.       enemies,
  11.       {x: my_state.x, y: my_state.y},
  12.       my_state.angle
  13.     );
  14.   }
  15.   for(var key in enemies){
  16.     if(enemies[key]['id'] == this.enemyId) var enemy = enemies[key];
  17.   }
  18.   if(typeof(enemy) == 'undefined'){// Если наш враг уже умер, то нужно находить нового...
  19.     this.enemyId = this.getNearestEnemy(
  20.       enemies,
  21.       {x: my_state.x, y: my_state.y},
  22.       my_state.angle
  23.     );
  24.     
  25.     for(var key in enemies){
  26.       if(enemies[key]['id'] == this.enemyId) var enemy = enemies[key];
  27.     }
  28.   }
  29.   /*
  30.   Этот метод(который ниже) мы создадим чтобы он нам говорил в какую сторону
  31.   нужно отклониться, чтобы нацелиться на врага, он будет возвращать
  32.   -1 если нужно повернуться по часовой стрелке, и 1 если нужно повернуться
  33.   против часовой стрелки... ну или 0 если мы уже нацелены...
  34.   напомню для тех кто забыл школьный курс геометрии: ноль градусов
  35.   располжены там где 3 часа на стенных часах, соответственно 90 градусов
  36.   на 12-ти часах...
  37.   */
  38.   var angle = this.getAngleDeflection(
  39.     {x: my_state.x, y: my_state.y},
  40.     {x: enemy.x, y: enemy.y},
  41.     my_state.angle
  42.   );
  43.   /*
  44.   Дистанцию до врага можно вычислить по формуле архимеда: c^2 = a^2 + b^2 - сумма
  45.   квадратов катетов равна квадрату гипотенузы... => c = sqrt(a^2 + b^2)
  46.   */
  47.   var distanceToEnemy = Math.sqrt(
  48.     Math.pow(enemy.x - my_state.x, 2)
  49.     + Math.pow(enemy.y - my_state.y, 2)
  50.   );
  51.   return [
  52.     0.5,
  53.     angle,
  54.     this.fire(
  55.       {x: my_state.x, y: my_state.y},
  56.       {x: enemy.x, y: enemy.y},
  57.       my_state.angle,
  58.       distanceToEnemy
  59.     ),
  60.     distanceToEnemy
  61.   ];
  62. };
* This source code was highlighted with Source Code Highlighter.


Надеюсь с комментами в коде все понятно… Итак мы имеем несколько методов, которые еще не описаны, но использованы, значит нужно их написать, вот что мы щас будем писать:
  • getNearestEnemy(enemies, myCoords, myAngle)
  • getAngleDeflection(myCoords, enemyCoords, myAngle)
  • fire(myCoords, enemyCoords, myAngle, distance)


Описываем метод getNearestEnemy(enemies, myCoords, myAngle):
  1. this.getNearestEnemy = function(enemies, myCoords, myAngle){
  2.   var howLong = Array(); // В этом массиве будут содержаться ID врагов и его дальность от нас
  3.   var distance = Array(); // В этом массиве будут содержаться ID и дальность самого близеого к нам врага
  4.   for(var i = 0; i < enemies.length; i++){
  5.     howLong[i] = Array();
  6.     /*
  7.     Получаем направление в градусах от нас до врага
  8.     */
  9.     var direction = get_direction(myCoords.x, myCoords.y, enemies[i]['x'], enemies[i]['y']);
  10.     /*
  11.     Теперь разницу между нашем направлением и направлением в сторону врага. Эта разница тоже имеет значение,
  12.     т.к. на поворот тоже затрачивается время
  13.     */
  14.     direction = angle_size(direction, myAngle);
  15.     /*
  16.     Теперь суммируем расстояние и разницу углов...
  17.     */
  18.     howLong[i]['distance'] = Math.sqrt(
  19.       Math.pow(enemies[i]['x'] - myCoords.x, 2)
  20.       + Math.pow(enemies[i]['y'] - myCoords.y, 2)
  21.     ) + direction;
  22.     howLong[i]['id'] = enemies[i]['id'];
  23.   }
  24.   for(var i = 0; i < howLong.length; i++){// Здесь отбриаем ID самого ближайшего робота
  25.     if(!i) distance = howLong[i];
  26.     else if(distance['distance'] > howLong[i]['distance']) distance = howLong[i];
  27.   }
  28.   return distance['id'];
  29. };
* This source code was highlighted with Source Code Highlighter.


Теперь метод getAngleDeflection(myCoords, enemyCoords, myAngle):
  1. this.getAngleDeflection = function(myCoords, enemyCoords, myAngle){// Ну здесь вроде все и так понятно)
  2.   return get_angle_control(
  3.     myAngle,
  4.     get_direction(myCoords['x'], myCoords['y'], enemyCoords['x'], enemyCoords['y'])
  5.   );
  6. };
* This source code was highlighted with Source Code Highlighter.


И последний метод — fire(myCoords, enemyCoords, myAngle, distance), он нам говорит стрелять или нет(например если энергия у нас 100 процентов но робот еще не успел нацелиться то стрелять НЕ надо):
  1. this.fire = function(myCoords, enemyCoords, myAngle, distance){
  2.   var direction = get_direction(myCoords['x'], myCoords['y'], enemyCoords['x'], enemyCoords['y']);
  3.   var angle = angle_size(direction, myAngle); // Приравниваем переменной разность углов
  4.   if(distance != 0 && angle != 0){// Если какая-то из переменных равна нулю, то теоретически стрелять можно
  5.     /*
  6.     10 делим на произведение расстояния и разности углов, получаем коэффициент "нацеленности"
  7.     */
  8.     if(10/(distance*angle) > 0.6) return true;
  9.     else return false;
  10.   }else return true;
  11. }
* This source code was highlighted with Source Code Highlighter.


Все готово, теперь можно собрать нашего робота по кусочкам:
  1. this.getNearestEnemy = function(enemies, myCoords, myAngle){
  2.   var howLong = Array();
  3.   var distance = Array();
  4.   for(var i = 0; i < enemies.length; i++){
  5.     howLong[i] = Array();
  6.     var direction = get_direction(myCoords.x, myCoords.y, enemies[i]['x'], enemies[i]['y']);
  7.     direction = angle_size(direction, myAngle);
  8.     howLong[i]['distance'] = Math.sqrt(
  9.       Math.pow(enemies[i]['x'] - myCoords.x, 2)
  10.       + Math.pow(enemies[i]['y'] - myCoords.y, 2)
  11.     ) + direction;
  12.     howLong[i]['id'] = enemies[i]['id'];
  13.   }
  14.   for(var i = 0; i < howLong.length; i++){
  15.     if(!i) distance = howLong[i];
  16.     else if(distance['distance'] > howLong[i]['distance']) distance = howLong[i];
  17.   }
  18.   return distance['id'];
  19. };
  20.  
  21. this.getAngleDeflection = function(myCoords, enemyCoords, myAngle){
  22.   return get_angle_control(
  23.     myAngle,
  24.     get_direction(myCoords['x'], myCoords['y'], enemyCoords['x'], enemyCoords['y'])
  25.   );
  26. };
  27.  
  28. this.fire = function(myCoords, enemyCoords, myAngle, distance){
  29.   var direction = get_direction(myCoords['x'], myCoords['y'], enemyCoords['x'], enemyCoords['y']);
  30.   var angle = angle_size(direction, myAngle);
  31.   if(distance != 0 && angle != 0){
  32.     if(10/(distance*angle) > 0.6) return true;
  33.     else return false;
  34.   }else return true;
  35. }
  36.  
  37. this.enemyId = 0;
  38.  
  39. this.action = function(my_state, enemies, shots, params){
  40.   if(!this.enemyId){
  41.     this.enemyId = this.getNearestEnemy(
  42.       enemies,
  43.       {x: my_state.x, y: my_state.y},
  44.       my_state.angle
  45.     );
  46.   }
  47.   for(var key in enemies){
  48.     if(enemies[key]['id'] == this.enemyId) var enemy = enemies[key];
  49.   }
  50.   if(typeof(enemy) == 'undefined'){
  51.     this.enemyId = this.getNearestEnemy(
  52.       enemies,
  53.       {x: my_state.x, y: my_state.y},
  54.       my_state.angle
  55.     );
  56.     
  57.     for(var key in enemies){
  58.       if(enemies[key]['id'] == this.enemyId) var enemy = enemies[key];
  59.     }
  60.   }
  61.   var angle = this.getAngleDeflection(
  62.     {x: my_state.x, y: my_state.y},
  63.     {x: enemy.x, y: enemy.y},
  64.     my_state.angle
  65.   );
  66.   var distanceToEnemy = Math.sqrt(
  67.     Math.pow(enemy.x - my_state.x, 2)
  68.     + Math.pow(enemy.y - my_state.y, 2)
  69.   );
  70.   return [
  71.     0.5,
  72.     angle,
  73.     this.fire(
  74.       {x: my_state.x, y: my_state.y},
  75.       {x: enemy.x, y: enemy.y},
  76.       my_state.angle,
  77.       distanceToEnemy
  78.     ),
  79.     distanceToEnemy
  80.   ];
  81. };
* This source code was highlighted with Source Code Highlighter.


Конечно это не всё и можно еще много чего придумать, например частенько если робот стреляет из далека, то пуля не долетает, это можно исправить зная направление движения врага…
Tags:
Hubs:
Total votes 15: ↑12 and ↓3 +9
Views 395
Comments Comments 10