Pull to refresh

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

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

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

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


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

Comments 10

Only those users with full accounts are able to leave comments. Log in, please.