Мой третий, по-моему, конкурс на CodingGame — WonDev Woman. К сожалению, из-за завала на работе времени на участие практически не было, но в двух словах, что он из себя представлял и как я там продвигался:
В чём состояло соревнование, как водится, заранее известно не было. Было известно только название и дата. Ясен пень, конкурс решили привязать к выходу фильма о Чудо-Женщине. Но если предыдущее соревнование (про пиратов) было тематически хоть как-то связано с фильмом (надо было управлять кораблями на поле из шестигранников), то это, похоже, не связано вообще никак.
Кратко, суть соревнования вот в чём. На поле есть боты, они ходят по очереди и могут пойти на соседнюю клетку (находиться на месте нельзя) и поднять высоту соседней клетки (не строить нельзя). И то и другое делается в восьми направлениях. Очки даются за переход на клетку с высотой 3, причём на одну и ту же клетку можно наступить несколько раз и получить очки за каждый. Так как строить надо каждый ход и клетки 4 высоты считаются непроходимыми, вполне реально застроить себя в угол. Или запереть противника, при определённой удаче. Если боты близко, можно мешать противнику получать очки строя поверх его клеток третьей высоты.
Вступительная лига начинается с одного бота у каждого игрока и простого поля. Задача - нашагать больше очков чем противник. Задача облегчается тем что каждый ход тебе приходит список валидных команд для бота - то есть, остаётся только выбрать какая из них наиболее выгодна. Я начал с совсем простой эвристики типа "чем больше общая высота вокруг - тем лучше, если шаг приводит нас на клетку 3 высоты - то вообще хорошо, если оказываемся заперты - то плохо". Пара довольно простых функций применяла возможную команду к текущему полю, и считала оценку получившейся конфигурации. Потом все команды сортировались по оценке и выполнялась наибольшая. В первой вариации бот ещё и пытался оценить насколько повышается общая высота у противника и минимизировать ещё и её, но это я в итоге выбросил и так и не включил обратно. На такой ерунде получилось обыграть других начинашек и выйти в первую деревянную лигу.
В первой деревянной тот же бот показывал плохие результаты (нижняя четверть таблицы), поэтому пришлось взяться за дело по настоящему
Если первый вариант писался просто в одном файле как одна большая функция, сейчас я решил, во-первых, разбить его на нормальные классы, и, во-вторых, - покрыть тестами. Для тестирования был взят jasmine, для сборки и запуска - karma. В самом соревновании код пишется для последней (скорее всего) node.js, поэтому можно использовать всевозможные плюшки ES6 типа нативных классов, лямбда-функций и подобного добра. Чтобы не заморачиваться с версиями karma-плагинов и просто запускать тесты в любом из браузеров я взял в качестве транспилера babel и воткнул его в karma.conf, чтобы все новомодные фишки языка перед запуском теста были преобразованы в более классические конструкции.
Соответственно, из-за необходимости запускать бота как с настоящими данными соревнования, так и с фейковыми для тестов, пришлось немного рефакторить код - ридер данных стал отдельной сущностью, которая передаётся в бота при инстанцировании класса. То есть, для запуска в соревновательной среде было так:
let myReader = new Reader();
let myBot = new Bot(myReader);
А для тестирования с фейковыми данными было так:
let myReader = new FakeReader(myFakeData);
let myBot = new Bot(myReader);
Всё для того чтобы сделать логику бота независимой от читалки входных данных. Это сработало, с помошью тестов я отловил пару (довольно глупых) ошибок, но бот всё равно не поднимался выше 200-какого-то места. После этого пришла пора смотреть реплеи игр чтобы понять что же не так.
В CodinGame можно просматривать повторы игр, которые сыграл твой бот. Более того, можно видеть его отладочный вывод одновременно с игрой. Первое что бросилось в глаза при просмотре — иногда бот просто не переходил на соседнюю клетку с третьей высотой. Я написал пару соответствующих тестов, бот исправно приоритизировал такие ходы. В попытке понять что же происходит я полез в описания условий конкурса и понял наконец в чём дело. Вся закавыка была в том что я не учитывал что переходить можно максимум на один уровень вверх или вниз. Если бот стоит на земле (высота 0) и рядом есть клетка с высотой 3, перехода на неё просто не будет в списке доступных для бота команд. Код оценки я немного поправил (состояния приводящие к отдельно стоящим башням без подхода получали пенальти по очкам). После этого получилось наконец выйти во вторую деревянную лигу.
Во второй деревянной, как водится в CodinGame, были добавлены дополнительные правила. Во-первых, под контролем каждого игрока стало по два бота. Во-вторых, вместо обычного движения стало можно толкать бота противника, чтобы занять его место. Третье нововведение описано не было, но я сам обнаружил его после нескольких игр. Ходы где ты строишь поверх бота противника были в списке валидных, но попытка это сделать приводила к дисквалификации бота и почти наверняка - к проигрышу. Это заставило добавлять в оценочную функцию проверки на то не стоит ли кто на месте будущей стройки. По хорошему конечно надо было переписать функцию оценки не просто по клеткам вокруг бота а с учётом путей достижимости по высоте (поиск в ширину), но на это времени уже не нашлось.