![](https://habrastorage.org/storage2/c68/9bd/154/c689bd1546c1757791e1f6d664f04eec.jpg)
Небольшая история про участие в одном из IT-конкурсов Mail.ru group.
Пришло мне письмо: «Приглашаем на Russian AI Cup и CodeTanks!». Давно уже хотелось поучаствовать, пошел регистрироваться. Форма на удивление была несложной, а вот полученное письмо не обрадовало ((((
![](https://habrastorage.org/storage2/074/af5/dc1/074af5dc1345bcf64198095e3cc43974.jpg)
В этот день всё закончилось на регистрации, т.к. времени было 0:27, а утром на работу. Решил на выходных посидеть поизучать и написать алгоритм.
Пришли выходные, начал читать мануалы, все пересказывать не буду. Кто в теме и так всё знают, а кто нет, то хватит краткого экскурса. Есть поле заданного размера на который помещается 6 танков, каждый со своей стратегией (алгоритмом). Вот, как раз стратегии и пишут участники соревнований. В стратегии есть много объектов и параметров на поле, которые можно отслеживать: пули, танки, бонусы, координаты объектов, мощность двигателя на каждую гусеницу и мн. др.
![](https://habrastorage.org/storage2/d2f/f0b/47d/d2ff0b47dac456cfd55baae5ef99b408.jpg)
Из встроенных математических функций есть только 2 — это дистанция между объектами и угол между прямой проходящей, через центр одного объекта и прямой соединяющей центры объектов угол между которыми надо высчитать. Все остальное ложится на руки того, кто пишет стратегию.
Основной смысл набрать как можно больше очков, а не остаться живым. Очки дают за попадание в другие танки и их уничтожение.
Первый алгоритм который я придумал был очень простым. Ездить по полю, собирать ближайшие бонусы, которые появляются на поле, и стрелять в танки, угол поворота башни до которого минимален.
size_t selected_tank = all_tanks.size();
for(size_t i = 0; i < all_tanks.size(); ++i) { // перебираем танк из списка
Tank tank = all_tanks[i];
if (!tank.teammate() && tank.crew_health()) { // в свои танки стрелять не будем :) и в убитые тоже
double angle_to_enemy = fabs(self.GetTurretAngleTo(tank)); // найдем модуль угла от башни до танка
if (angle_to_enemy < min_angle_to_enemy) { // выберем минимум
min_angle_to_enemy = angle_to_enemy;
selected_tank = i;
}
}
}
Алгоритм такой был не супер хорош, но уже показал неплохие результаты.
![](http://habrastorage.org/storage2/037/1b4/28a/0371b428a06f030b298c4f9ee5da31a7.jpg)
Все было бы хорошо, если была бы стабильность в играх, но как видно по графику — то первые, то последние места.
Началась оптимизация этого алгоритма. В первой оптимизации я добавил прямолинейное движение, если такового не было больше, чем 40 тиков и выстрел супер пулями, если мишень находится ближе, чем 500 пикселей.
if (self.GetDistanceTo(all_tanks[selected_tank]) < MIN_DISTANCE_FOR_PREMIUM_AMMO || self.premium_shell_count() > 2 || all_tanks[selected_tank].crew_health() < 35 || all_tanks[selected_tank].hull_durability() < 35 ){
move.set_fire_type(PREMIUM_PREFERRED); // если угол невелик, то выстрелим
}
else{
move.set_fire_type(REGULAR_FIRE); // если угол невелик, то выстрелим
}
Эти изменения положительных результатов не дали, наоборот результаты стали хуже.
После анализа нескольких боёв было принято решение отталкиваться от краёв поля, для этого я привязался к центру поля таким образом, что разбил его на 4 четверти.
![](http://habrastorage.org/storage2/ad3/df8/c6d/ad3df8c6db9bef2779cb02600dc6a5b5.jpg)
И в зависимости от того в какой четверти я нахожусь применять определенные правила для выезда из углов и отъезда от краёв.
if( self.GetDistanceTo(640,400) < 50 || last_tick_stright_move + 60 < world.tick() && all_shels.size()){
move.set_left_track_power(1.0); // если угол не больше 30 градусов
move.set_right_track_power(1.0); // поедем максимально быстро вперед
if( last_tick_stright_move + 80 < world.tick()){
last_tick_stright_move = world.tick();
counter_tick_straight_move++;
}
} else if (angle_to_center > MIN_ANGLE) { // если угол сильно положительный,
move.set_left_track_power(1*mod_l); // то будем разворачиваться,
move.set_right_track_power(0.5*mod_r); // поставив противоположные силы гусеницам.
} else if (angle_to_center < -MIN_ANGLE) { // если угол сильно отрицательный,
move.set_left_track_power(0.5*mod_l); // будем разворачиваться
move.set_right_track_power(1*mod_r); // в противоположную сторону.
} else {
move.set_left_track_power(1.0*mod_l); // если угол не больше 30 градусов
move.set_right_track_power(1.0*mod_r); // поедем максимально быстро вперед
counter_tick_straight_move++;
if(counter_tick_straight_move > 30){
last_tick_stright_move = world.tick();
counter_tick_straight_move=0;
}
}
Тем самым победил проблему с тем, что бы танк у краёв не тратил много времени на разворот.
![](http://habrastorage.org/storage2/2ea/4e0/9bb/2ea4e09bb7ddd12fba4f5056cb2dd41e.jpg)
(на графике падение — это 1 оптимизация, а после него взлёт это вторая)
Опять небольшой анализ. И стало понятно, что от краёв то я уворачиваюсь, но пока это делаю ни на что другое не реагирую. И тут возникла кардинально новая идея. Забыть про старый алгоритм и написать новый, секреты которого я опубликую после окончания соревнования, но графики готов показать сейчас.
![](http://habrastorage.org/storage2/d94/a87/529/d94a87529c58dbc364650c9c3801a88a.jpg)
Последний рост, как раз связан с заливкой новой стратегии. Из 14 игр была 1 игра в которой я занял 4 место, 2 игры, в которых я занял 3 место, 1 игра в которой я занял 2 место и 10 игр в которых я стал лидером, но к моему разочарованию я понял, что в выбранном алгоритме рейтингования игроков есть просчет. И тут с написания алгоритмов я
перешёл на изучение выбранной системы рейтингов. Из документации выяснилось, что они используют систему рейтингов Эло, побежал читать.
Ага оказывается эта система используется для расчёта относительной силы игроков в играх, в которых участвуют двое, например шахматы. Копаем глубже, цитаты не буду приводить — многабукаф. Но смысл заключается в том, что эта система не учитывает резко изменившуюся спортивную форму игрока в конкретный момент, а это значит, что резко улучшив алгоритм управления танком вы все равно будете топтаться около ближайших соперников, а учитывая, что рейтинг увеличивается при том, что танк набирает больше, чем спрогнозировала система, а прогноз у системы всегда будет максимальным т.к. все противники по силе находятся рядом, то и рейтинг сильно расти не будет.
Самое обидное то, что это я прочитал за пару часов до начала первого раунда соревнований, а в него из песочницы проходило всего 900 алгоритмов. И я тут же быстренько метнулся создавать новый аккаунт, куда залил свой алгоритм, но не хватило 1 боя!, что бы попасть в проходные 900 мест.
При этом как всегда администрация, на посты в их блог ничего толкового не отвечала. И спустя 3 дня, в тот момент когда я писал эту статью, они выпускают обновления к своей системе рейтингования! Где они признают мягко скажем, несовершенство выбранной системы:
Что это меняет для участника? Как было замечено, если вы провели множество боев с плохой стратегией, то даже послав новую очень крутую стратегию, то сразу больших плюсов в рейтинг вы не получите и вам придется постепенно возвращаться в число лучших игроков, что, на самом деле, происходит достаточно медленно. Новая функция позволит вам
увеличить разброс изменений рейтинга.
Но отбор то лучших 900 алгоритмов то уже прошел! Плюс в этом же посте они изменяют среду существования танков, и их боевые характеристики, что конечно же вызывает шквал, как положительных так и отрицательных эмоций у участников…
В выводе стоит поблагодарить организаторов за то, что они предоставили шанс поучаствовать в хороших соревнованиях, и обматерить тех, кто выбирал систему рейтингования игроков. Однако сделав скидку на то, кто является организатором, то на таких не ругаются, а просто относятся с пониманием. Так что просто спасибо.
Публикуется по просьбе shulyakovskiy