Наверно многие уже слышали про проект под названием HabraWars, если коротко — это игра для программеров, в которой вы сами пишите искусственный интеллект для собственного робота на JS.
Я думаю что это будет не первая моя статья на данную тему, хотя бы потому, что здесь я не собираюсь раскрывать всю тему, а лишь ее часть. Но сначала, я думаю, нужно сформировать некую концепцию робота… Сразу говорю, что в js я далеко не спец и вообще мои знания этого языка, на мой взгляд, довольно скудны… Итак, как-же должен выглядеть наш робот изнутри, а изнутри у него должна быть логика, как бы это очевидно не звучало, но логика это довольно сложная штука, она будет управлять роботом, задавая вопросы(типа: «Летит ли в меня(робота) снаряд») и основываясь на ответах вызывать функции, отвечающие за действия робота… Но начну я не с логики(я сам еще не начинал даже ее писать:)), а начну с функций, отвечающих за выполнение действий, порученных логикой.
Значит так, начнем. В этом посте будет обсуждаться именно атака нашего робота(в следующем наверно увороты от пуль, читай защита). Накидываем для начала такой сухой код:
- this.enemyId = 0;
-
- this.action = function(my_state, enemies, shots, params){
- if(!this.enemyId){// Если мы еще не выбрали врага, то есть это первая итерация данной функции
- /*
- Получаем ID ближайшего врага и записываем его в переменную, которая не изменится при построении
- следующего фрейма игры...
- */
- this.enemyId = this.getNearestEnemy(
- enemies,
- {x: my_state.x, y: my_state.y},
- my_state.angle
- );
- }
- for(var key in enemies){
- if(enemies[key]['id'] == this.enemyId) var enemy = enemies[key];
- }
- if(typeof(enemy) == 'undefined'){// Если наш враг уже умер, то нужно находить нового...
- this.enemyId = this.getNearestEnemy(
- enemies,
- {x: my_state.x, y: my_state.y},
- my_state.angle
- );
-
- for(var key in enemies){
- if(enemies[key]['id'] == this.enemyId) var enemy = enemies[key];
- }
- }
- /*
- Этот метод(который ниже) мы создадим чтобы он нам говорил в какую сторону
- нужно отклониться, чтобы нацелиться на врага, он будет возвращать
- -1 если нужно повернуться по часовой стрелке, и 1 если нужно повернуться
- против часовой стрелки... ну или 0 если мы уже нацелены...
- напомню для тех кто забыл школьный курс геометрии: ноль градусов
- располжены там где 3 часа на стенных часах, соответственно 90 градусов
- на 12-ти часах...
- */
- var angle = this.getAngleDeflection(
- {x: my_state.x, y: my_state.y},
- {x: enemy.x, y: enemy.y},
- my_state.angle
- );
- /*
- Дистанцию до врага можно вычислить по формуле архимеда: c^2 = a^2 + b^2 - сумма
- квадратов катетов равна квадрату гипотенузы... => c = sqrt(a^2 + b^2)
- */
- var distanceToEnemy = Math.sqrt(
- Math.pow(enemy.x - my_state.x, 2)
- + Math.pow(enemy.y - my_state.y, 2)
- );
- return [
- 0.5,
- angle,
- this.fire(
- {x: my_state.x, y: my_state.y},
- {x: enemy.x, y: enemy.y},
- my_state.angle,
- distanceToEnemy
- ),
- distanceToEnemy
- ];
- };
* 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):
- this.getNearestEnemy = function(enemies, myCoords, myAngle){
- var howLong = Array(); // В этом массиве будут содержаться ID врагов и его дальность от нас
- var distance = Array(); // В этом массиве будут содержаться ID и дальность самого близеого к нам врага
- for(var i = 0; i < enemies.length; i++){
- howLong[i] = Array();
- /*
- Получаем направление в градусах от нас до врага
- */
- var direction = get_direction(myCoords.x, myCoords.y, enemies[i]['x'], enemies[i]['y']);
- /*
- Теперь разницу между нашем направлением и направлением в сторону врага. Эта разница тоже имеет значение,
- т.к. на поворот тоже затрачивается время
- */
- direction = angle_size(direction, myAngle);
- /*
- Теперь суммируем расстояние и разницу углов...
- */
- howLong[i]['distance'] = Math.sqrt(
- Math.pow(enemies[i]['x'] - myCoords.x, 2)
- + Math.pow(enemies[i]['y'] - myCoords.y, 2)
- ) + direction;
- howLong[i]['id'] = enemies[i]['id'];
- }
- for(var i = 0; i < howLong.length; i++){// Здесь отбриаем ID самого ближайшего робота
- if(!i) distance = howLong[i];
- else if(distance['distance'] > howLong[i]['distance']) distance = howLong[i];
- }
- return distance['id'];
- };
* This source code was highlighted with Source Code Highlighter.Теперь метод getAngleDeflection(myCoords, enemyCoords, myAngle):
- this.getAngleDeflection = function(myCoords, enemyCoords, myAngle){// Ну здесь вроде все и так понятно)
- return get_angle_control(
- myAngle,
- get_direction(myCoords['x'], myCoords['y'], enemyCoords['x'], enemyCoords['y'])
- );
- };
* This source code was highlighted with Source Code Highlighter.И последний метод — fire(myCoords, enemyCoords, myAngle, distance), он нам говорит стрелять или нет(например если энергия у нас 100 процентов но робот еще не успел нацелиться то стрелять НЕ надо):
- this.fire = function(myCoords, enemyCoords, myAngle, distance){
- var direction = get_direction(myCoords['x'], myCoords['y'], enemyCoords['x'], enemyCoords['y']);
- var angle = angle_size(direction, myAngle); // Приравниваем переменной разность углов
- if(distance != 0 && angle != 0){// Если какая-то из переменных равна нулю, то теоретически стрелять можно
- /*
- 10 делим на произведение расстояния и разности углов, получаем коэффициент "нацеленности"
- */
- if(10/(distance*angle) > 0.6) return true;
- else return false;
- }else return true;
- }
* This source code was highlighted with Source Code Highlighter.Все готово, теперь можно собрать нашего робота по кусочкам:
- this.getNearestEnemy = function(enemies, myCoords, myAngle){
- var howLong = Array();
- var distance = Array();
- for(var i = 0; i < enemies.length; i++){
- howLong[i] = Array();
- var direction = get_direction(myCoords.x, myCoords.y, enemies[i]['x'], enemies[i]['y']);
- direction = angle_size(direction, myAngle);
- howLong[i]['distance'] = Math.sqrt(
- Math.pow(enemies[i]['x'] - myCoords.x, 2)
- + Math.pow(enemies[i]['y'] - myCoords.y, 2)
- ) + direction;
- howLong[i]['id'] = enemies[i]['id'];
- }
- for(var i = 0; i < howLong.length; i++){
- if(!i) distance = howLong[i];
- else if(distance['distance'] > howLong[i]['distance']) distance = howLong[i];
- }
- return distance['id'];
- };
-
- this.getAngleDeflection = function(myCoords, enemyCoords, myAngle){
- return get_angle_control(
- myAngle,
- get_direction(myCoords['x'], myCoords['y'], enemyCoords['x'], enemyCoords['y'])
- );
- };
-
- this.fire = function(myCoords, enemyCoords, myAngle, distance){
- var direction = get_direction(myCoords['x'], myCoords['y'], enemyCoords['x'], enemyCoords['y']);
- var angle = angle_size(direction, myAngle);
- if(distance != 0 && angle != 0){
- if(10/(distance*angle) > 0.6) return true;
- else return false;
- }else return true;
- }
-
- this.enemyId = 0;
-
- this.action = function(my_state, enemies, shots, params){
- if(!this.enemyId){
- this.enemyId = this.getNearestEnemy(
- enemies,
- {x: my_state.x, y: my_state.y},
- my_state.angle
- );
- }
- for(var key in enemies){
- if(enemies[key]['id'] == this.enemyId) var enemy = enemies[key];
- }
- if(typeof(enemy) == 'undefined'){
- this.enemyId = this.getNearestEnemy(
- enemies,
- {x: my_state.x, y: my_state.y},
- my_state.angle
- );
-
- for(var key in enemies){
- if(enemies[key]['id'] == this.enemyId) var enemy = enemies[key];
- }
- }
- var angle = this.getAngleDeflection(
- {x: my_state.x, y: my_state.y},
- {x: enemy.x, y: enemy.y},
- my_state.angle
- );
- var distanceToEnemy = Math.sqrt(
- Math.pow(enemy.x - my_state.x, 2)
- + Math.pow(enemy.y - my_state.y, 2)
- );
- return [
- 0.5,
- angle,
- this.fire(
- {x: my_state.x, y: my_state.y},
- {x: enemy.x, y: enemy.y},
- my_state.angle,
- distanceToEnemy
- ),
- distanceToEnemy
- ];
- };
* This source code was highlighted with Source Code Highlighter.Конечно это не всё и можно еще много чего придумать, например частенько если робот стреляет из далека, то пуля не долетает, это можно исправить зная направление движения врага…

