Lab Rat Race: упражнение в генетических алгоритмах


113

Это Fortnightly Challenge # 3. Тема: Генетические алгоритмы

Этот вызов - эксперимент. Мы хотели посмотреть, что мы можем сделать, с помощью генетических алгоритмов. Не все может быть оптимальным, но мы старались сделать его доступным. Если это сработает, кто знает, что мы можем увидеть в будущем. Может быть, генетический король горы?

Спецификация довольно длинная! Мы попытались разделить спецификацию на Основы - необходимый минимум, чтобы начать играть с фреймворком и представить ответ - и подробности The Gory - полную спецификацию со всеми подробностями о контроллере, на основе которых вы мог написать свой.
Если у вас есть какие-либо вопросы, присоединяйтесь к нам в чате!

Вы исследователь в поведенческой психологии. Это вечер пятницы, и вы и ваши коллеги решили повеселиться и использовать своих лабораторных крыс для небольшой крысиных гонок. На самом деле, прежде чем мы будем слишком эмоционально привязаны к ним, давайте назовем их образцами .

Вы создали небольшую гоночную трассу для образцов, и чтобы сделать ее более интересной, вы разместили несколько дорожек, ловушек и телепортеров по всей трассе. Теперь ваши образцы все еще крысы ... они понятия не имеют, что такое ловушка или телепорт. Все, что они видят, это вещи разных цветов. У них также нет никакой памяти - все, что они могут сделать, - это принимать решения, основываясь на их текущем окружении. Я предполагаю, что естественный отбор выберет образцы, которые знают, как избежать ловушки из тех, которые этого не делают (эта гонка займет некоторое время ...). Да начнется игра!

Пример изображения платы в использовании

† 84 465 особей были повреждены при постановке этой задачи.

Основы

Это однопользовательская игра (вы и ваши коллеги не хотели смешивать население, поэтому каждый из них создал свой собственный гоночный трек). Ипподром представляет собой прямоугольную сетку высотой 15 ячеек и шириной 50 ячеек. Вы начинаете с 15 образцов на случайных (не обязательно отличных) ячейках на левом краю (где x = 0 ). Ваши образцы должны попытаться достичь цели, которой является любая ячейка при x ≥ 49 и 0 ≤ y ≤ 14 (образцы могут отклониться от пути вправо). Каждый раз, когда это происходит, вы получаете очко. Вы также начинаете игру с 1 очком. Вы должны попытаться максимизировать свои очки после 10 000 ходов.

Несколько образцов могут занимать одну и ту же ячейку и не будут взаимодействовать.

На каждом ходу каждый образец видит сетку 5x5 своего окружения (с самим собой в центре). Каждая ячейка этой сетки будет содержать цвет -1к 15. -1представляет клетки, которые находятся за пределами. Ваш образец умирает, если он выходит за пределы. Что касается других цветов, они представляют собой пустые клетки, ловушки, стены и телепорты. Но ваш образец не знает, какой цвет представляет, и вы тоже. Однако есть некоторые ограничения:

  • 8 цветов будут представлять пустые ячейки.
  • 4 цвета будут представлять телепорт. Телепортер отправит образец в определенную ячейку в своем районе 9x9. Это смещение будет одинаковым для всех телепортеров одного цвета.
  • 2 цвета будут представлять стены. Двигаться в стену - все равно, что стоять на месте.
  • 2 цвета будут представлять ловушку. Ловушка указывает, что одна из 9 клеток в ее непосредственной близости является летальной (не обязательно сама клетка ловушки). Это смещение будет одинаковым для всех ловушек одного цвета.

Теперь об этом естественном отборе ... у каждого экземпляра есть геном, представляющий собой число из 100 битов. Новые образцы будут созданы путем скрещивания двух существующих образцов, а затем с незначительным изменением генома. Чем успешнее образец, тем больше у него шансов на воспроизведение.

Итак, вот ваша задача: вы напишите одну функцию, которая получает в качестве входных данных сетку цветов 5x5, которую видит образец, а также его геном. Ваша функция вернет ход (Δx, Δy) для образца, где Δx и Δy будут каждый из {-1, 0, 1}. Вы не должны сохранять какие-либо данные между вызовами функций. Это включает в себя использование ваших собственных генераторов случайных чисел. Ваша функция будет обеспечена засеянным ГСЧ, который вы можете использовать по своему усмотрению.

Оценка вашего представления будет средним геометрическим числом баллов по 50 случайным трекам. Мы обнаружили, что эта оценка подвержена значительным отклонениям. Поэтому эти оценки будут предварительными . Как только этот вызов исчезнет, ​​будет объявлен крайний срок. По истечении крайнего срока, 100 досок будут выбраны случайным образом, и все заявки будут перенесены на эти 100 досок. Не стесняйтесь ставить оценку в вашем ответе, но мы будем оценивать каждую подачу самостоятельно, чтобы никто не обманывал.

Мы предоставили программы контроллеров на нескольких языках. В настоящее время вы можете написать свою заявку на Python (2 или 3), Ruby , C ++ , C # или Java . Контроллер генерирует доски, запускает игру и обеспечивает основу для генетического алгоритма. Все, что вам нужно сделать, это обеспечить функцию перемещения.

Подождите, так что же мне делать с геномом?

Задача состоит в том, чтобы понять это!

Поскольку у образцов нет памяти, все, что у вас есть в данном ходу, - это сетка цветов 5х5, которая ничего для вас не значит. Таким образом, вам придется использовать геном, чтобы достичь цели. Общая идея заключается в том, что вы используете части генома для хранения информации о цветах или компоновке сетки, а ваш бот основывает свои решения на дополнительной информации, хранящейся в геноме.

Теперь, конечно, вы не можете хранить там что-либо вручную. Таким образом, фактическая информация, хранящаяся там, изначально будет полностью случайной. Но генетический алгоритм скоро выберет те образцы, чей геном содержит правильную информацию, и убьет те, у которых неправильная информация. Ваша цель - найти соответствие между битами генома и вашим полем зрения движением, которое позволяет быстро найти путь к цели и которое последовательно развивается в выигрышной стратегии.

Этого должно быть достаточно, чтобы вы начали. Если вы хотите, вы можете пропустить следующий раздел и выбрать нужный контроллер из списка контроллеров внизу (который также содержит информацию о том, как использовать этот конкретный контроллер).

Читайте дальше, если хотите все ...

Гори Детали

Эта спецификация завершена. Все контроллеры должны выполнять эти правила.

Вся случайность использует равномерное распределение, если не указано иное.

Генерация треков:

  • Дорожка представляет собой прямоугольную сетку, шириной X = 53 ячейки и высотой Y = 15 ячеек. Ячейки с x ≥ 49 являются целевыми ячейками (где x основано на нуле).
  • Каждая клетка имеет один цвет и может быть или не быть летальной - клетки не являются летальными, если это не указано одним из типов клеток ниже.
  • Существует 16 различных цветов ячеек, помеченных от 0до 15, значение которых будет меняться от игры к игре. Кроме того, -1представляет клетки, которые находятся за пределами - это смертельно .
  • Выберите 8 случайных цветов . Это будут пустые ячейки (которые не имеют никакого эффекта).
  • Выберите еще 4 случайных цвета . Это телепорты. Для двух из этих цветов выберите ненулевое смещение в окрестности 9x9 (от (-4, -4) до (4,4) за исключением (0,0)). Для других двух цветов инвертируйте эти смещения. Если образец наступает на телепорт, он немедленно перемещается на это смещение.
  • Выберите еще 2 случайных цвета . Это ловушки. Для каждого из этих цветов выберите смещение в окрестности 3х3 (от (-1, -1) до (1,1)). Ловушка указывает на то, что ячейка с таким смещением смертельна . Примечание. Сама ловушка не обязательно смертельна.
  • В 2 остальных цветами являются стенами, которые препятствуют движению. Попытка перейти на клетку стены превратит движение в неподвижность. Сами клеточные стенки смертельны .
  • Для каждой нецелевой ячейки сетки выберите случайный цвет. Для каждой клетки цели выберите случайный пустой цвет.
  • Для каждой ячейки на левом краю дорожки определите, можно ли достичь цели в течение 100 ходов (в соответствии с приведенными ниже правилами порядка хода ). Если это так, эта ячейка является допустимой стартовой ячейкой . Если начальных ячеек меньше 10, откажитесь от дорожки и создайте новую.
  • Создайте 15 экземпляров, каждый со случайным геном и возрастом 0 . Поместите каждый образец в случайную начальную ячейку.

Порядок поворота:

  1. Следующие шаги будут выполнены, по порядку, для каждого образца. Образцы не взаимодействуют и не видят друг друга и могут занимать одну и ту же клетку.
    1. Если возраст образца составляет 100 лет , он умирает. В противном случае увеличьте его возраст на 1.
    2. Образцу предоставляется его поле зрения - сетка цветов 5x5, центрированная на образце, и он возвращает движение в своей окрестности 3x3. Выход за пределы этого диапазона приведет к прекращению работы контроллера.
    3. Если целевой ячейкой является стена, то ход изменяется на (0,0).
    4. Если целевая ячейка является телепортом, образец перемещается по смещению телепорта. Примечание. Этот шаг выполняется один раз , а не итеративно.
    5. Если ячейка, занятая в настоящее время образцом (возможно, после использования одного телепорта), смертельна, образец погибает. Это единственный раз, когда образцы умирают (кроме шага 1.1. Выше). В частности, новый образец, который появляется на смертельной клетке, не погибнет немедленно, но имеет шанс сначала покинуть опасную клетку.
    6. Если образец занимает целевую ячейку, наберите одно очко, переместите образец в случайную начальную ячейку и установите его возраст равным 0.
  2. Если на доске осталось менее двух экземпляров, игра заканчивается.
  3. Создайте 10 новых экземпляров с возрастом 0 . Каждый геном определяется (индивидуально) по правилам разведения ниже. Поместите каждый образец в случайную начальную ячейку.

Разведение:

  • Когда создается новый образец, выбирайте двух разных родителей наугад, с уклоном в сторону образцов, которые продвинулись дальше вправо. Вероятность того, что образец будет выбран, пропорциональна его текущей оценке пригодности . Оценка пригодности образца

    1 + x + 50 * сколько раз он достиг цели

    где x - горизонтальный индекс на основе 0. Образцы, созданные в том же порядке, не могут быть выбраны в качестве родителей.

  • Из двух родителей выберите случайного, чтобы взять первый бит генома.

  • Теперь, когда вы идете по геному, поменяйте родителей с вероятностью 0,05 и продолжайте брать биты у получающегося родителя.
  • Мутируйте полностью собранный геном: для каждого бита переверните его с вероятностью 0,01 .

Подсчет очков:

  • Одна игра длится 10000 ходов.
  • Игроки начинают игру с 1 очком (чтобы разрешить использование среднего геометрического).
  • Каждый раз, когда образец достигает цели, игрок получает очко.
  • На данный момент каждый игрок подаст заявку на 50 игр, каждая из которых имеет свой случайный трек.
  • Вышеуказанный подход приводит к большей дисперсии, чем желательно. Как только этот вызов исчезнет, ​​будет объявлен крайний срок. По истечении крайнего срока, 100 досок будут выбраны случайным образом, и все заявки будут перенесены на эти 100 досок.
  • Общий счет игрока является средним геометрическим значением очков этих отдельных игр.

Контроллеры

Вы можете выбрать любой из следующих контроллеров (так как они функционально эквивалентны). Мы протестировали все из них, но если вы обнаружили ошибку, хотите улучшить код или производительность, или добавили такую ​​функцию, как графический вывод, отправьте сообщение об ошибке или отправьте запрос на GitHub! Также вы можете добавить новый контроллер на другом языке!

Нажмите на название языка для каждого контроллера, чтобы перейти к нужному каталогу на GitHub, который содержит README.mdточные инструкции по использованию.

Если вы не знакомы с git и / или GitHub, вы можете загрузить весь репозиторий в виде ZIP-файла с первой страницы (см. Кнопку на боковой панели).

питон

  • Наиболее тщательно проверено. Это наша эталонная реализация.
  • Работает как с Python 2.6+, так и с Python 3.2+!
  • Это очень медленно. Мы рекомендуем запустить его с PyPy для существенного ускорения.
  • Поддержка графического вывода с использованием либо pygameили tkinter.

Рубин

  • Протестировано с Ruby 2.0.0. Должен работать с более новыми версиями.
  • Это также довольно медленно, но Ruby может быть удобен для прототипирования идеи для представления.

C ++

  • Требуется C ++ 11.
  • Опционально поддерживает многопоточность.
  • Безусловно, самый быстрый контроллер в связке.

C #

  • Использует LINQ, поэтому он требует .NET 3.5.
  • Скорее медленно.

Ява

  • Не особенно медленно. Не особенно быстро.

Предварительная таблица лидеров

Все оценки являются предварительными. Тем не менее, если что-то не так или устарело, пожалуйста, дайте мне знать. Наш пример представления приведен для сравнения, но не для утверждения.

  Score   | # Games | User               | Language   | Bot           
===================================================================================
2914.13   |   2000  | kuroi neko         | C++        | Hard Believers
1817.05097|   1000  | TheBestOne         | Java       | Running Star
1009.72   |   2000  | kuroi neko         | C++        | Blind faith
 782.18   |   2000  | MT0                | C++        | Cautious Specimens
 428.38   |         | user2487951        | Python     | NeighborsOfNeighbors
 145.35   |   2000  | Wouter ibens       | C++        | Triple Score
 133.2    |         | Anton              | C++        | StarPlayer
 122.92   |         | Dominik Müller     | Python     | SkyWalker
  89.90   |         | aschmack           | C++        | LookAheadPlayer
  74.7    |         | bitpwner           | C++        | ColorFarSeeker
  70.98   |   2000  | Ceribia            | C++        | WallGuesser
  50.35   |         | feersum            | C++        | Run-Bonus Player
  35.85   |         | Zgarb              | C++        | Pathfinder
 (34.45)  |   5000  | Martin Büttner     | <all>      | ColorScorePlayer
   9.77   |         | DenDenDo           | C++        | SlowAndSteady
   3.7    |         | flawr              | Java       | IAmARobotPlayer
   1.9    |         | trichoplax         | Python     | Bishop
   1.04   |   2000  | fluffy             | C++        | Gray-Color Lookahead

кредиты

Эта задача была огромным совместным усилием:

  • Натан Меррил: Написал контроллеры Python и Java. Превратила концепцию испытаний из King-of-the-Hill в крысиные бега.
  • Трихоплакс: тестирование. Работал на контроллере Python.
  • feersum: Написал C ++ контроллер.
  • VisualMelon: написал C # контроллер.
  • Мартин Бюттнер: Концепция. Написал Ruby контроллер. Тестируем. Работал на контроллере Python.
  • Т Авраам: тестирование. Протестировал Python и рассмотрел C # и C ++ контроллер.

Все вышеперечисленные пользователи (и, возможно, еще пара, о которых я забыл) внесли свой вклад в общий дизайн задачи.

Обновление контроллера C ++

Если вы используете C ++ с Visual Studio и многопоточностью, вы должны получить последнее обновление из-за ошибки с заполнением генератора случайных чисел, которая позволяет создавать дубликаты плат.


3
Не мог ли кто-то просто создать генетический генетический алгоритм, чтобы найти оптимальный генетический алгоритм для этой проблемы?
mbomb007

1
@ anon3202 Ну, это, конечно, даст вам больше информации о расположении трека, так как вы сможете определить, где вы находитесь. По сути, мы хотели сохранить интерфейс для ботов простым и сделать его чисто локальной проблемой, где вам потребуется геном, чтобы узнать, какое локальное решение наиболее выгодно для вашего глобального прогресса.
Мартин Эндер

1
@matovitch См. раздел 5 раздела « Порядок хода» в Gory Details (полная спецификация):'In particular, a new specimen which spawns on a lethal cell will not die immediately, but has a chance to move off the dangerous cell first.'
trichoplax

1
Я настроил код C ++, чтобы показать примерные средние значения, stddev, stderr и 99-процентный conf-интервал (до вашего «геометрического» log / exp), и сделал потрясающее открытие. В ответе «Слепая вера» было «Среднее значение выборки 116529 + - 2.78337e + 010 (99%) stddev = 7.77951e + 010» после 50 прогонов. «Снижение доверительного интервала до 50% заметно не улучшит ситуацию. Среднее геометрическое было более стабильным: «Среднее 159.458 + - 117262 (99%) stddev = 32.6237» (до его обновления в 800 баллов)
Mooing Duck

1
Я провел некоторый эксперимент со скоростью мутации, и я думаю, что вызов был бы более интересным (и контроллеры запускались бы намного быстрее), если бы вероятность была повышена с 0,01 до 0,0227, что дает ДНК только 10% шансов пройти мутация без изменений вместо 37% с текущим значением. Это позволяет избежать нелепых взрывов населения (что, в свою очередь, экономит много вычислительного времени) и предотвращает множество сбоев из-за недостаточного разнообразия. Индивидуальные оценки ниже, но, поскольку больше пробегов дают победителей, глобальное среднее значение имеет тенденцию к росту.

Ответы:


37

Слепая вера - C ++ - кажется, набирает более 800 (!) Баллов за 2000

Геном цветового кодирования с загадочной обратной связью и эффективным сдерживающим фактором

#include "./gamelogic.cpp"

#define NUM_COLORS 16

// color meanings for our rats
typedef enum { good, bad, trap } colorType_t;
struct colorInfo_t {
    colorType_t type;
    coord_t offset; // trap relative location
    colorInfo_t() : type(good) {} // our rats are born optimists
};

// all 8 possible neighbours, carefully ordered
coord_t moves_up  [] = { { 1, 0 }, { 1,  1 }, { 1, -1 }, { 0,  1 }, { 0, -1 }, { -1, 0 }, { -1,  1 }, { -1, -1 } };  // toward the goal, going up   first
coord_t moves_down[] = { { 1, 0 }, { 1, -1 }, { 1,  1 }, { 0, -1 }, { 0,  1 }, { -1, 0 }, { -1, -1 }, { -1,  1 } };  // toward the goal, going down first

// map of the surroundings
struct map_t {
    static const size_t size = 5;
    static const int max = size / 2;
    static const int min = -max;
    colorType_t map[size*size];
    colorType_t & operator() (int x, int y) { return map[(x + max)*size + y + max]; }
    colorType_t & operator() (coord_t pos) { return operator()(pos.x, pos.y); }
    bool is_inside(int x, int y) { return abs(x) <= max && abs(y) <= max; }
    bool is_inside(coord_t pos) { return is_inside(pos.x,pos.y); }
};

// trap mapping info
struct trap_t {
    coord_t detector;
    colorInfo_t color;
    trap_t(int x, int y, colorInfo_t & color) : color(color) { detector.x = x; detector.y = y; }
    trap_t() {}
};

coord_t blindFaith(dna_t d, view_t v)
{
    colorInfo_t color[NUM_COLORS]; // color informations

    // decode colors
    for (size_t c = 0; c != 16; c++)
    {
        size_t base = c * 4;
        if (d[base])
        {
            color[c].type = d[base+1] ? good : bad;
        }
        else // decode trap location
        {
            color[c].type = trap;
            int offset = d[base+1] + 2 * d[base+2] + 4 * d[base+3];
            color[c].offset = moves_up[offset]; // the order is irrelevant as long as all 8 neighbours are listed
        }
    }

    // build a map of the surrounding cells
    map_t map;
    unsigned signature = 0;
    int i = 0;
    for (int x = map.min; x <= map.max; x++)
    for (int y = map.min; y <= map.max; y++)
    {
        int c = v(x, y);
        map(x, y) = (c == -1) ? bad : color[c].type;
        if (c != -1) signature ^= v(x, y) << ((i++) % 28);
    }

    // map traps
    for (int x = map.min; x <= map.max; x++)
    for (int y = map.min; y <= map.max; y++)
    {
        if (map(x, y) != trap) continue;
        const colorInfo_t & trap = color[v(x, y)];
        int bad_x = x + trap.offset.x;
        int bad_y = y + trap.offset.y;
        if (!map.is_inside(bad_x, bad_y)) continue;
        map(bad_x, bad_y) = bad;
        map(x, y) = good;
    }

    // pick a vertical direction according to surroundings signature
    int go_up = d[64 + signature % (DNA_BITS - 64)];

    // try to move to a good cell nearer the goal
    for (const coord_t &move : go_up ? moves_up : moves_down) if (map(move.x, move.y) == good) return move;

    // try not to increase fitness of this intellectually impaired specimen
    return{ -1, 0 };
}

int main() {
    time_t start = time(NULL);
    double score = runsimulation(blindFaith);
    slog << "Geometric mean score: " << score << " in " << time(NULL) - start << " seconds";
}

Пример результатов:

Scores: 15 4113306 190703 1 1 44629 118172 43594 63023 2 4 1 1 205027 1 455951 4194047 1 5 279 1 3863570 616483 17797 42584 1 37442 1 37 1 432545 5 94335 1 1 187036 1 4233379 1561445 1 1 1 1 35246 1 150154 1 1 1 1 90141 6 1 1 1 26849 1 161903 4 123972 1 55 988 7042063 694 4711342 90514 3726251 2 1 383389 1 593029 12088 1 149779 69144 21218 290963 17829 1072904 368771 84 872958 30456 133784 4843896 1 2 37 381780 14 540066 3046713 12 5 1 92181 5174 1 156292 13 1 1 29940 66678 125975 52714 1 5 3 1 101267 69003 1 1 10231 143110 282328 4 71750 324545 25 1 22 102414 1 3884626 4 28202 64057 1 1 1 1 70707 4078970 1623071 5047 1 1 549040 1 1 66 3520283 1 6035495 1 79773 1 1 1 218408 1 1 15 33 589875 310455 112274 1 1 4 1 3716220 14 180123 1 2 12785 113116 12 2 1 59286 822912 2244520 1840950 147151 1255115 1 49 2 182262 109717 2 9 1049697 59297 1 11 64568 1 57093 52588 63990 331081 54110 1 1 1537 3 38043 1514692 360087 1 260395 19557 3583536 1 4 152302 2636569 12 1 105991 374793 14 3934727 1 2 182614 1 1675472 121949 11 5 283271 207686 175468 1 1 173240 1 138778 1 1 59964 3290382 1 4 1757946 1 23520 1 2 94 1 124577 497071 1749760 39238 1 301144 3 1 2871836 1 1 10486 1 11 8 1 111421 11 1807900 1 587479 1 42725 116006 3 1 6 5441895 1 1 22 52465 952 1 18 1 1 46878 2 1 1 1994 4 593858 123513 4692516 820868 4247357 1 1 2 1 2 8770 2 1 95371 4897243 2 22741 1 1 1 1 325142 6 33650 4 51 102993 1 182664 1 4040608 18153 2045673 462339 1 1 617575 2 2551800 3 7760 1 108012 76167 143362 1148457 1 53460 1 71503 1 1 1 1 81482 3208 62286 69 139 1 3503941 1 253624 101903 3081954 80123 84701 9 16 1 1070688 71604 613064 2076 15009 9 1 1 1 199731 1 2 1 63132 1 1843855 27808 1 3569689 273144 1 460524 2703719 22443 10876 51242 1 6972678 4591939 1 140506 43981 45076 2 1 91301 5 1 1874615 1758284 608 13 1 96545 75161 1 618144 4 2056133 1 1 2 57401 1394307 6 188116 83545 1 41883 1 1 467189 371722 1 1122993 1 17912 159499 1 5 3355398 33 1 2 246304 1 2 168349 1 50292 12 141492 2723076 3 1 6 3060433 223360 171472 106409 1 2 1 102729 8814 1 285154 1 11 1 65 930 2 689644 3271116 1 5 4 60 77447 1 1 1477538 256023 100403 2480335 1 39888 1 1 70052 66090 1 250 1 2 8 115371 1523106 1424 168148 1 1 1 42938 17 1 364285 185080 1 1 36 4903764 13 51987 1106 276212 67460 1 251257 2 6867732 1 1 1890073 1 1 8 5 2118932 210 0 3792346 5209168 1 1 1 1 51 1 4621148 1 37 337073 3506096 1 1 1 1 458964 2 16 52930 1 15375 267685 1 1 1259646 14930 3248678 527105 1 103 24 1 3252685 6009 1 1 176340 3971529 121 1722808 1 31483 194232 2314706 95952 3625407 3 216755 56 1 8 1 1 1 1 885 229 9056 172027 31516 2526805 1 76076 1589061 1 1 8 90812 1 21 72036 1681271 2 212431 1581814 85993 79967 4 7 514708 1070070 1 71698 1 23478 15882 94453 1 27382 495493 277308 12127 91928 248593 1 1 1 26540 1709344 2119856 1 1 48867 107017 251374 64041 15924 15 87474 8 1 23 9 48 1 1 1 51793 2 61029 84803 15 689851 1 1 873503 10 140084 420034 87087 82223 1 163273 12 1 5 570463 19 26665 1 170311 1 39983 1 475306 1 2 36417 746105 11 141345 1 3 1 30 3 1 1 1 1 1312289 408117 1 42210 273871 561592 1 1 1 1 4448568 48448 7 378508 1 351858 278331 1 79515 1169309 3670107 14711 4686395 1156554 33 2528441 24537 76 335390 63545 122108 76675 21929 34 1 861361 83000 417781 1 90487 1 1 85116 7 2 1 60129 647991 79 1 2755780 726845 244217 50007 187212 1 3674051 286071 44068 3 307427 26973 1 26059 1957457 230783 58102 545318 1 4 172542 168365 1 89402 1 4 1 1 1 1 2 3 16 62935 5643183 117961 109942 85762 5 117376 118883 1 61 23893 122536 70185 1 64252 208409 179269 55381 1579240 3434491 1 4964284 3356245 3 21 2197119 346542 44340 59976 772220 5590844 199721 90858 63785 125989 57219 129737 81836 1 3671 16810 1 4151040 1 15 40108 1 443679 3224921 2 27498 2 3 146529 169409 19 1 1 1 1 41627 1 3 2722438 1 2013730 1 1649406 1 1 6943 125772 58652 1 1 1 2413522 1 2 48 36067 253807 2 146464 1 248 07 3359223 139896 395985 65241 43988 594638 69033 275085 1 17973 1 1 1 594835 1 1 4468341 3496274 222854 94769 55 161056 36185 8793 277592 3 1 6746 1 138151 66 37365 1 2729315 1 3 57091 22408 249875 246514 85058 1 20 5463152 1 3 1 45293 1 70488 2792458 461 441 951926 2236205 2 171980 1 1 48 3893009 1 458077 1 268203 1 70005 7 19299 1 278978 1 45286 26 2 1883506 274393 342679 1 1 913722 911600 12688 1 1 115020 1249307 1529878 53426 1 226862 3721440 23537 86033 397433 1 1 1 161423 96343 94496 1 1 1 2 1 111576 1 4039782 1 1 1 5742393 3569 46072 1 1 2 1 1 85335 219988 1 78871 115876 43405 1 300835 1 166684 53134 1 3 111487 6 3 3 77 1 115971 3 205782 10 1932578 356857 43258 47998 1 27648 127096 573939 32650 523906 45193 1 2 128992 1 10144 1 257941 1 19841 5077836 14670 5 3 6 1 1 21 14651 2906084 37942 45032 9 304192 3035905 6214026 2 177952 1 51338 1 65594 46426 553875 2676479 245774 95881 3 216364 3064811 1198509 223982 3 6 1 533254 1 590363 264940 68346 127284 1 7 1 1 4617874 5 45400 1 1 3097950 360274 1 3 1 8421 14 469681 418563 3 1 6 1 1 575766 405239 11 2631108 152667 1 1 1 467383 1 1 775499 1 157998 2 1 143351 92021 1 1 1173046 3636579 1 70635 162303 1 1534876 834682 2 1 1 11981 346908 245124 607794 17 1570641 126995 13 57050 1 2 33731 29739 1 1 35460 1 33716 168190 214704 1 443156 701674 2636870 108081 1604895 1 1 11 115901 23 571891 360680 1 1 35 1 2036975 1 1 2555536 4742615 5 360553 287044 1 1814255 7 59632 1 216 41546 1 540920 353424 2625301 223744 1 1 1 15717 3 429871 1 4 2329632 18 11 1 2 4 1 3905 5 1 1 1 2 5431442 1 859628 1 3 338378 15236 13764 1 3384362 1 15 65293 24 619599 152620 2 189921 35854 16647 7 2 404790 360096 1 2 189459 1097768 191610 1 1 470254 1 12 2 330299 364219 2365542 312023 2273374 2 10527 1 115453 1 2 3845592 52388 913449 1 14695 1 44 37352 90302 1 1 1 233577 51639 3474983 44010 1650727 31 2 2 1 8 7 1 3 5 25603 17799 45630 758457 1 4571839 37 4 3 2 1 1 1351271 196673 12 2880765 263886 2926173 1 2 1 241502 5 6 1 278576 9 7 290722 42749 143391 82753 21771 57887 1 1 60400 1766903 1 296392 1 5 2861787 125560 1 9 199218 1 1 308226 517598 2246753 12 1168981 3 98447 1 488613 9 842865 202108 10 1 238493 1 1523706 5383982 29435 1 1 207071 1 8 4 125742 70531 253135 72207 124291 23364 184376 2 40034 9569353 194109 102854 2 3247153 58313 85995 1 598 63 1 2676692 10 3573233 1 36651 118016 2486962 65456 46760 1 5813 723 178120 2 153305 1 1 2 1 2354413 3 1 17126 132953 437123 299778 3070490 1 6490 403704 2261 511439 1 39 33410 173045 1 1 120970 641346 132042 1 44906 1 33940 132124 467702 45472 9 44 1 1 1 107008 1 46635 1 121431 130760 1 7 3 1 56251 1299306 3 1 1 1 15 2147678 215169 1374943 1 332995 231089 269310 1 7816944 1 1 1 46 134426 1 1 1 2 76112 1 1 30438 299927 25 139373 76048 278757 71 3474997 1 294046 1 3126554 2518019 2 1 6 1 3054393 1 1 1 2 525 96 419528 1 1 154718 233 207879 26 1 6 57436 3 5944942 1 1 318198 147536 1 22 420557 1 1 120938 1 1 167412 4082969 73299 1 11 3557361 1 4 330028 269051 1 2569546 2 1 1 4 1 1 377412 1 1 1 213800 58131 1422177 54 109617 117751 12432 3830664 419046 3 6821 741 919 1 22335 1 1 15069 80694 488809 2389 2308679 145548 51411 115786 110984 107713 1 12 6 1 5 8365 1 2001874 210250 4674015 14 1 1204101 314354 89066 1 1 2438200 68350 1 1575329 5593838 2743787 151670 57 16 5948210 597158 128060 189160 23628 1 1 15 4171774 1 8206 4157492 1 2 315607 1618680 24736 18520 4787225 33842 134431 1 1 1 1 1 1115809 17759 1 33016 123117 1 77322 169633 219091 1 321593 57231 135536 175401 4 1 435702 1 253132 100707 114547 1 119324 6382967 1472898 3 72567 1707408 177958 26 208719 1 27083 74 12 576410 19375 177069 4 3 1 31 507048 2 1 1 2 1 2 1 40 7 99892 95202 60649 241396 232370 1 136579 70649 1 2877 280695 13603 102860 404583 29717 112769 1 54089 1 97579 40819 2 868629 64848 2 63432 5 1 1888426 99623 2 1 7911 53646 3047637 1 2 3 152910 1 3244662 105187 1 1 1 1 8966 200347 1 1 22 302654 6 17 1 10 328150 55259 1016 117291 2 1 224524 23846 74645 1 1 1 1 1 3117394 10847 33976 144613 4 201584 1 1 26959 3 4410588 27019 6 66749 55935 23 4126812 4089989 99959 1 1 1 1 55490 1 4275599 13652 33967 2 8126062 337093 320653 128015 4 1 7729132 1 10594 116651 20990 3046630 1 353731 132989 2066431 4 80 15575 147430 1 621461 3100943 2306122 5 33439 407945 25634 1 2911806 32511 2174235 298281 15159 54125 1 2 3063577 2205013 1 407984 1 319713 1 22171 1 2763843 1 2607606 1 100015 3096036 1 55905 1 1 635265 2890760 1 1 1 1 35854 1 352022 2652014 1 2 274366 1 4 1 602980 4 83828 602270 2816 2 59116 25340 1 11 1 5162051 34 8 218372 1186732 142966 1 1 170557 503302 1 84924 5 1 1350329 1 1 1 130273 78055 902762 1 8581 5 1 3635882 1 1 1 224255 44044 61250 2 438453 8 1 2729357 28 1 17658 82640 1 31809 10 1 33 1 1 45495 5798 5000217 40018 588787 67269 1 12 83512 2798339 1 609271 1 3 1 7 67912 189808 3388775 60961 81311 1167 24939 433791 405306 85934 1 1170651 2 1 66 552579 122985 515363 2188340 1 1 1 3807012 1502582 4 13 149593 1 1 2108196 3 34279 24613 1282047 27 1 2 1 1 584435 27487 1 1 5 33278 1 1 1202843 1 1 1 6 3649820 3100 2 266150 13 164117 10 53163 3295075 1 1 1 1 77890 1 286220 90823 18866 3139039 481826 1 3994676 23 116901 132290 6 3927 84948 1 1 1 1 256310 1 11 8 1 102002 8392 887732 98483 444991 1 1 49408 409967 1158979 1 1 1 81469 189764 3960930 296231 64258 1 1 176030 4 1 2 1 486856 1 1135146 31 2 13112 227077 31
Geometric mean score: 831.185 in 14820 seconds

Основываясь на непроизвольно продолжительном тесте feersum, я считаю, что 2000 прогонов достаточно, чтобы получить приемлемо стабильный результат.
Поскольку мой модифицированный контроллер отображает текущее геометрическое среднее значение после каждого прогона, я визуально подтвердил, что отклонение за последние 50 прогонов было относительно небольшим (+ - 10 баллов).

Что заставляет этих твари тикать

Вместо того, чтобы давать равные приоритеты каждому цвету, я рассматриваю эти возможные значения:

  1. хорошо -> крыса думает, что там можно безопасно
  2. плохо -> крыса туда не пойдет
  3. trap -> крыса сочтет положение ловушки плохим, а клетка, указывающая на ловушку, хорошей .
    Хотя я слишком ленив, чтобы переименовать его, это скорее «детектор опасности», указывающий (предполагаемое) местоположение реальной ловушки, стены, телепорта, ожидающего отправки ничего не подозревающего странника в неприятное место или даже вход мертвого -конец. Короче говоря, место, куда бы мудрая крыса не пошла.

Хорошие или плохие гены хранят только 2 бита (например, 11и 10), но для ловушек требуется 4 бита ( 0tttгде tttпредставляет одно из 8 возможных «опасных» мест).

Для того, чтобы сохранить каждый ген последовательно (т.е. сохранение его значение после того, как были смешаны в совершенно другой геном, который требует каждого цветового кодирования гена , чтобы быть в фиксированном месте), все значения кодируются на 4 бита (так хорошо , кодируется как 11xxи плохо , как 10xx), всего 16 * 4 = 64 бита.

Остальные 36 битов используются как «антистенгеры» (подробнее об этом позже). 25 окружающих цветов хэшируются в индекс из этих 36 битов. Каждый бит указывает предпочтительное вертикальное направление (вверх или вниз), которое используется, когда возможен выбор между двумя ячейками.

Стратегия заключается в следующем:

  • декодировать каждый цвет в соответствии с геномом (или отчет прямого контроллера для «плохих» ячеек вне дорожки)
  • построить карту ближайшего окружения (3х3 клетки, 8 возможных соседей)
  • вычислить сигнатуру окружения (хэш из 25 цветов, кроме ячеек вне маршрута)
  • выберите предпочтительное вертикальное направление из подписи (среди 36 блоков хэша)
  • попробуйте перейти к соседу, намекающему на «хорошо», начиная с ближайших к цели и сначала двигаясь в предпочтительном вертикальном направлении
  • если не удается найти «хорошего» соседа, попробуйте переместить одну ячейку назад (таким образом, возможно, он стал жертвой несчастного случая и, во всяком случае, избегал повышения физической подготовки)

Вы грызуны, вот враги вашего рода

страшная стена телепортационная петля

Худшее, что может случиться с популяцией, - это еще не выиграть ни одного победителя, но множество крыс прилипло либо к стене, либо к бесконечной петле телепортации, достаточно близко к цели, чтобы иметь доминирующий шанс быть выбранным для размножения .
Вопреки тому, что крысы были зажаты в ловушке или телепортированы в стены, эти грызуны будут убиты только к старости.
Они не имеют конкурентного преимущества перед своими кузенами, которые с самого начала застряли на 3 клетки, но у них будет достаточно времени, чтобы размножаться поколение за поколением кретинов, пока их геном не станет доминирующим, что нанесет серьезный вред генетическому разнообразию без веской причины.

Чтобы смягчить это явление, идея состоит в том, чтобы сделать потомков этих плохих, плохих крыс более вероятными, чтобы избежать следования на этапах своего происхождения.
Указатель вертикального направления имеет длину всего 1 бит (в основном, говоря «попробуйте идти вверх или вниз первым в этих условиях»), и довольно много битов, вероятно, окажут влияние на пройденный путь, поэтому мутации и / или пересечения должны иметь значительное влияние.
Многие потомки будут вести себя по-разному и не будут биться головой об одну и ту же стену (среди трупов своих голодных предков).
Тонкость здесь в том, что этот признак не является доминирующим фактором в поведении крысы. Интерпретация цвета все еще будет преобладать в большинстве случаев (выбор вверх / вниз будет иметь значение, только если действительно есть два «хороших»).и то, что крыса считает безвредным цветом, не телепорт, ожидающий, чтобы бросить его в стену).

Почему это (кажется) работает?

Я до сих пор не знаю точно, почему.

Абсолютной удачей, которая остается неразгаданной тайной, является логика картирования ловушек. Без сомнения, это краеугольный камень успеха, но он работает по-своему таинственным образом.

При использовании кодирования случайный геном будет производить 25% «хороших», 25% «плохих» и 50% «ловушечных» идентификаторов цвета.
идентификаторы «ловушки», в свою очередь, будут давать «хорошие» и «плохие» оценки в корреляции с окружением 5x5.
В результате крыса в данном месте «увидит» мир как смесь стабильных и контекстных цветов «не ходи».

Как показывает довольно успешный механизм анти-ударов, наихудшим элементом на трассе является страшная стена (и ее двоюродный брат - телепортационная петля, но я думаю, что они встречаются гораздо реже).

Вывод заключается в том, что успешная программа должна, прежде всего, иметь возможность развивать крыс, способных обнаруживать позиции, которые приведут к медленному голоданию, не достигая цели.

Даже без «угадывания» двух цветов, представляющих стены, цвета «ловушек», по-видимому, способствуют обходу стен, позволяя крысе обходить несколько препятствий не потому, что она «видела» стены, а потому, что оценка «ловушки» исключала их конкретные клеточные стенки в этом конкретном окружении.

Несмотря на то, что крыса пытается двигаться к цели (что может привести к тому, что наиболее «полезными» индикаторами ловушек являются те, которые указывают на опасность впереди), я думаю, что все направления ловушек имеют примерно одинаковое влияние: ловушка, указывающая «опасность позади» «Расположение 2 клеток перед крысой имеет такое же влияние, как и указание на« опасность впереди », когда крыса стоит прямо над ней.

К сожалению, почему эта смесь обладает свойством так успешно сближать геном, это далеко не моя математика.

Я чувствую себя более комфортно с помощью сдерживающего средства. Это сработало, как и планировалось, хотя и намного превзошло мои ожидания (счет был в основном умножен на четыре)

Я сильно взломал контроллер для отображения некоторых данных. Вот несколько прогонов:

Turns:2499 best rat B  B  B  G  B  G  T3 G  T4 B  G  B  B  B  G  G  ^vv^^vv^v^v^^^vvv^v^^^v^^^v^vv^^v^^^ Max fitness: 790 Specimens: 1217 Score: 2800
Turns:4999 best rat B  B  B  G  B  G  T3 G  T4 B  G  B  B  B  G  G  ^vv^^vv^v^v^^^vvv^v^^^v^^^v^vv^^v^^^ Max fitness: 5217 Specimens: 15857 Score: 685986
Turns:7499 best rat B  B  B  G  B  G  T3 G  T4 B  G  B  B  B  G  G  ^vv^^vv^v^v^^^vvv^v^^^vvvvv^^v^v^^^^ Max fitness: 9785 Specimens: 31053 Score: 2695045
Turns:9999 best rat B  B  B  G  B  G  T3 G  T4 B  G  B  B  B  G  G  ^vv^^vv^v^v^^^vvv^v^^^vvvvv^^v^v^^^^ Max fitness: 14377 Specimens: 46384 Score: 6033904
Scored 6035495 in game 146 current mean 466.875

Здесь порода супер-крыс появилась рано (след, вероятно, позволял бегать по прямой линии, и у некоторых счастливых крыс в самых первых поколениях была правильная ДНК, чтобы воспользоваться этим). Количество образцов в конце составляет примерно половину теоретического максимума около 100 000 крыс, что означает, что почти половина тварей приобрела способность выживать на этом конкретном пути бесконечно (!).
Конечно, итоговая оценка просто непристойна, как, впрочем, и время вычислений.

Turns:2499 best rat B  T0 G  B  T7 B  G  B  T6 T0 T3 B  G  G  G  T4 ^v^^^^^v^^v^v^^^^^^^^v^v^v^^vvv^v^^^ Max fitness: 18 Specimens: 772 Score: 1
Turns:4999 best rat T7 G  G  G  G  T7 G  B  T6 T0 T3 T5 G  G  B  T4 ^vvvvvvv^^^vvv^^v^v^^^^^^^^^^^^^v^^^ Max fitness: 26 Specimens: 856 Score: 1
Turns:7499 best rat G  T0 G  T3 G  T0 G  B  T6 T0 T2 B  T4 G  B  T4 ^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^ Max fitness: 55 Specimens: 836 Score: 5
Turns:9999 best rat T6 T0 G  T5 B  T1 G  B  T6 T0 T3 B  T4 G  B  T4 ^^vv^^^^vv^^v^v^^v^^vvv^vv^vvv^^v^^v Max fitness: 590 Specimens: 1223 Score: 10478
Scored 10486 in game 258 current mean 628.564

Здесь мы можем увидеть уточнение генома на работе. Родословная между двумя последними геномами проявляется четко. В хорошие и плохие оценки являются наиболее значимыми. Индикаторы ловушек, кажется, колеблются, пока они не стабилизируются до «полезной» ловушки или не превращаются в хорошие или плохие .

Кажется, у цветовых генов есть несколько полезных характеристик:

  • они имеют автономное значение
    (определенный цвет должен обрабатываться определенным образом).
    Каждое цветовое кодирование может быть добавлено в совершенно другой геном без существенного изменения поведения - если только цвет не является фактически решающим (обычно стена или телепорт, ведущий к бесконечной петле).
    Это в меньшей степени относится к базовому кодированию приоритетов, поскольку наиболее приоритетный цвет - это единственная информация, используемая для определения, куда двигаться. Здесь все «хорошие» цвета равны, поэтому данный цвет, добавленный в «хороший» список, будет иметь меньшее влияние.
  • они относительно устойчивы к мутациям:
    хорошее / плохое кодирование имеет только 2 значащих бита из 4, и местоположение ловушки может изменяться большую часть времени без существенного изменения поведения крысы.
  • они малы (4 бита), поэтому вероятность их разрушения кроссовером очень мала.
  • мутации приводят либо к безвредным значимым изменениям
    . Мутант, изменяющийся на «добро», либо малоэффективен (если, например, он соответствует пустой клетке, он может позволить найти новый, более короткий путь, но это также может привести крысу прямо в ловушка), или драматическая (если цвет представляет стену, новая крыса очень вероятно застрянет где-нибудь).
    Ген, переключающийся на «ловушку», либо лишит крысу существенного цвета, либо не окажет заметного эффекта.
    Мутация местоположения ловушки будет иметь значение только в том случае, если впереди действительно есть ловушка (или что-то вредное), которая имеет относительно небольшую вероятность (я бы сказал, что-то вроде 1/3).

Наконец, я предполагаю, что последние 36 битов способствуют не только предотвращению застревания крыс, но и более равномерному распределению крыс на дорожке, таким образом сохраняя генетическое разнообразие, пока не появится победный геном и не станет доминирующим в части кодирования цвета.

Дальнейшая работа

Я должен сказать, что нахожу этих маленьких тварей очаровательными.
Еще раз спасибо всем, кто внес вклад в этот прекрасный вызов.

Я подумываю о том, чтобы разделить контроллер дальше, чтобы отобразить более важные данные, например, происхождение успешной крысы.

Я также очень хотел бы видеть этих крыс в действии, но этот язык C ++ b ** ch делает создание - не говоря уже о анимации - изображений (среди многих других вещей) грязной рутиной.

В конце я хотел бы дать хотя бы объяснение системы ловушек и, возможно, улучшить ее.

Контроллер взлома

Если кому-то интересно, я могу опубликовать сделанные мной изменения в контроллере.
Они грязные и дешевые, но они делают свою работу.

Я не разбираюсь в GitHub, так что придется пройти через простой пост.


16
Получил счет 208,14 с 10 000 игр. Я пытался проверить его на 1000, но я так и не понял, что набрал дополнительные 0, так что это заняло более 7 часов.
feersum

LOL спасибо все равно. По сравнению с моими двумя 1000 прогонами кажется, что около 2000 прогонов могут дать стабильный результат.

Что вы ^^v^vvv^^^vv^^v^vvv^v^^vvvv^^^^^^^^^значите? Остальное я могу догадаться, но у меня проблемы с этим?
Mooing Duck

Я обдумывал создание отдельного контроллера «отладки», который запускает одну крысу за раз, и каждый раз, когда генерируется новая крыса, он показывает ДНК обоих родителей и ребенка (с помощью некоторой настраиваемой функции). Это намного облегчило бы изучение того, как работает крыса.
Mooing Duck

2
это представляет 36 битов индикатора «вверх / вниз», но в этих примерах победившая ДНК уже стала доминирующей, поэтому они не сильно различаются.

18

Жестокие верующие - C ++ - (улучшенные телепорты): 10.000+ за 2000 запусков

(это эволюция слепой веры , поэтому вы можете подняться на другую стену текста до этой)

#ifndef NDEBUG
#define NDEBUG
#include "./gamelogic.cpp"
#endif // NDEBUG
#include <cassert>

#define NUM_COLORS 16
#define BITS_OFFSET  3
#define BITS_TYPE    2
#define BITS_SUBTYPE 2
#define BITS_COLOR (BITS_TYPE+BITS_OFFSET)

// how our rats see the world
typedef unsigned char enumSupport_t;
typedef unsigned char trapOffset_t;
typedef enum : enumSupport_t {
    danger,   // code      trap detector
    beam,     // code      safe teleporter
    empty,    // code      empty
    block,    // code      wall, pit or teleporter
    trap,     // computed  detected trap
    pit,      // read      off-board cell
} colorType_t;

// color type encoding (4 first bits of a color gene)
// the order is critical. A single block/empty inversion can cost 2000 points or more
const colorType_t type_decoder[16] = {
    /*00xx-*/
    danger,
    empty,
    beam,
    block,
    /*01xx-*/
    beam,
    danger,
    empty,
    block,
    /*10xx-*/
    empty,
    beam,
    block,
    danger,
    /*11xx-*/
    block,
    empty,
    danger,
    beam,
};

// all 8 possible neighbours, carefully ordered
typedef coord_t neighborhood_t[8];
neighborhood_t moves_up =   { { 1, 0 }, { 1,  1 }, { 1, -1 }, { 0,  1 }, { 0, -1 }, { -1, 0 }, { -1,  1 }, { -1, -1 } };  // toward the goal, going up   first
neighborhood_t moves_down = { { 1, 0 }, { 1, -1 }, { 1,  1 }, { 0, -1 }, { 0,  1 }, { -1, 0 }, { -1, -1 }, { -1,  1 } };  // toward the goal, going down first

// using C++ as a macro-assembler to speedup DNA reading
/*
Would work like a charm *if* a well-paid scatterbrain at Microsoft had not defined
std::bitset::operator[] as

bool operator[](size_t _Pos) const
{   // subscript nonmutable sequence
return (test(_Pos));
}

Bounds checking on operator[] violates the spec and defeats the optimization.
Not only does it an unwanted check; it also prevents inlining and thus generates
two levels of function calls where none are necessary.
The fix is trivial, but how long will it take for Microsoft to implement it, if
the bug ever makes it through their thick layer of tech support bullshit artists?
Just one of the many reasons why STL appears not to live up to the dreams of
Mr Stroustrup & friends...
*/
template<size_t BITS> int DNA_read(dna_t dna, size_t base)
{
    const size_t offset = BITS - 1;
    return (dna[base + offset] << offset) | DNA_read<offset>(dna, base);
}
template<> int DNA_read<0>(dna_t, size_t) { return 0; }

// color gene
struct colorGene_t {
    colorType_t  type;
    trapOffset_t offset;  // trap relative location
    colorGene_t() : type(empty) {} // our rats are born optimists
};

// decoded DNA
class dnaInfo_t {
private:
    const dna_t & dna;
    static const size_t
        direction_start = NUM_COLORS*(BITS_TYPE + BITS_OFFSET),
        direction_size = DNA_BITS - direction_start;

public:
    colorGene_t color[NUM_COLORS];
    int         up_down; // anti-wall-banger

    // decode constant informations during construction
    dnaInfo_t(const dna_t & d) : dna(d)
    {
        for (size_t c = 0; c != NUM_COLORS; c++)
        {
            unsigned raw = DNA_read<BITS_COLOR>(d, c * BITS_COLOR);
            color[c].type = type_decoder[raw >> 1];
            if      (color[c].type == danger) color[c].offset = raw & 7;
            else if (color[c].type == beam  ) color[c].offset = raw & 3;
        }
    }

    // update with surroundings signatures
    void update(size_t signature)
    {
        // anti-blocker
        up_down = (direction_size > 0) ? dna[direction_start + signature % direction_size] : 0;
    }
};

// map of the surroundings
class map_t {
    struct cell_t {
        coord_t pos;
        int     color;
    };

    static const size_t size = 5;
    static const int max = size / 2;
    static const int min = -max;

    size_t local_signature[size*size]; // 8 neighbours signatures for teleporters
    cell_t track_cell[size*size]; // on-track cells
    size_t cell_num;
    colorType_t map[size*size];
    size_t raw_index(int x, int y) { size_t res = x * size + y + max + max * size; assert(res < size*size); return res; }
    size_t raw_index(coord_t pos) { return raw_index(pos.x, pos.y); }

    bool is_inside(int x, int y) { return abs(x) <= max && abs(y) <= max; }

public:
    size_t compute_signatures(view_t v, dnaInfo_t genome)
    {
        cell_num = 0;
        size_t signature = 0;
        memset (local_signature, 0, sizeof(local_signature));
        int i = 0;
        for (int x = min; x <= max; x++)
        for (int y = min; y <= max; y++)
        {
            int c = v(x, y);
            if (c == -1)
            {
                (*this)(x, y) = pit; continue;
            }
            track_cell[cell_num++] = { { x, y }, c };
            signature ^= c << (4 * (i++ & 1));

            if (genome.color[c].type == beam)
            {
                int in = 0;
                for (coord_t n : moves_up)
                {
                    coord_t pn = {x+n.x,y+n.y};
                    if (!is_inside(pn)) continue;
                    int cn = v(pn.x, pn.y);
//                    if (cn == -1) continue;
                    local_signature[raw_index(pn.x,pn.y)] ^= cn << (4 * (in++ & 1));
                }
            }
        }
        return signature;
    }

    void build(dnaInfo_t genome)
    {
        coord_t traps[size*size];
        size_t t_num = 0;

        // plot color meanings
        for (size_t c = 0; c != cell_num; c++)
        {
            const cell_t& cell = track_cell[c];
            const colorGene_t& color = genome.color[cell.color];
            (*this)(cell.pos) = (color.type == beam && (local_signature[raw_index(cell.pos.x,cell.pos.y)] % 4) == color.offset)
                    ? block
                    : color.type;

            // build a list of trap locations
            if (color.type == danger)
            {
                coord_t location = cell.pos + moves_up[color.offset];
                if (is_inside(location)) traps[t_num++] = location;
            }
        }

        // plot trap locations
        while (t_num) (*this)(traps[--t_num]) = trap;
    }

    // quick & dirty pathing
    struct candidate_t {
        coord_t pos;
        candidate_t * parent;
        candidate_t() {} // default constructor does not waste time in initializations
        candidate_t(int) : parent(nullptr) { pos.x = pos.y = 0; } // ...this is ugly...
        candidate_t(coord_t pos, candidate_t * parent) : pos(pos), parent(parent) {} // ...but so much fun...
    };

    coord_t path(const neighborhood_t & moves)
    {
        candidate_t pool[size*size]; // private allocation for express garbage collection...
        size_t alloc;

        candidate_t * border[size*size]; // fixed-size FIFO 
        size_t head, tail;

        std::bitset<size*size>closed;

        // breadth first search. A* would be a huge overkill for 25 cells, and BFS is already slow enough.
        alloc = head = tail = 0;
        closed = 0;
        closed[raw_index(candidate_t(0).pos)] = 1;
        border[tail++] = new (&pool[alloc++]) candidate_t(0);
        while (tail > head)
        {
            candidate_t & candidate = *(border[head++]); // FIFO pop
            for (const coord_t move : moves)
            {
                coord_t new_pos = candidate.pos + move;
                if (is_inside(new_pos))
                {
                    size_t signature = raw_index(new_pos);
                    if (closed[signature]) continue;
                    closed[signature] = 1;
                    if ((*this)(new_pos) > empty) continue;
                    if (new_pos.x == 2) goto found_exit; // a path to some location 2 cells forward
                    assert(alloc < size*size);
                    assert(tail < size*size);
                    border[tail++] = new(&pool[alloc++]) candidate_t(new_pos, &candidate); // allocation & FIFO push
                    continue;
                }
                // a path out of the 5x5 grid, though not 2 cells forward
            found_exit:
                if (candidate.parent == nullptr) return move;
                candidate_t * origin;
                for (origin = &candidate; origin->parent->parent != nullptr; origin = origin->parent) {}
                return origin->pos;
            }
        }

        // no escape
        return moves[1]; // one cell forward, either up or down
    }

    colorType_t & operator() (int x, int y) { return map[raw_index(x, y)]; }
    colorType_t & operator() (coord_t pos) { return operator()(pos.x, pos.y); }
    bool is_inside(coord_t pos) { return is_inside(pos.x, pos.y); }
};

std::string trace_DNA(const dna_t d, bool graphics = false)
{
    std::ostringstream res;
    dnaInfo_t genome(d);
    for (size_t c = 0; c != NUM_COLORS; c++)
    {
        if (graphics)
        {
            res << "tbew--"[genome.color[c].type];
            if (genome.color[c].type == danger) res << ' ' << moves_up[genome.color[c].offset].x << ' ' << moves_up[genome.color[c].offset].y;
            if (genome.color[c].type == beam) res << ' ' << genome.color[c].offset << " 0";
            if (c != NUM_COLORS - 1) res << ',';
        }
        else switch (genome.color[c].type)
        {
        case danger: res << "01234567"[genome.color[c].offset]; break;
        case beam  : res <<     "ABCD"[genome.color[c].offset]; break;
        default: res << "!*-#X@"[genome.color[c].type]; break;
        }
    }
    return res.str();
}

coord_t hardBelievers(dna_t d, view_t v)
{
    dnaInfo_t genome(d); // decoded DNA
    map_t     map;       // the surroundings seen by this particular rodent

    // update genome with local context
    genome.update(map.compute_signatures(v, genome));

    // build a map of the surrounding cells
    map.build(genome);

    // move as far to the right as possible, in the contextually preffered direction
    return map.path(genome.up_down ? moves_up : moves_down);
}

int main() {
    time_t start = time(NULL);
    double score = runsimulation(hardBelievers, trace_DNA);
    slog << "Geometric mean score: " << score << " in " << time(NULL) - start << " seconds";
}

Эпизод IV: ориентируемся на решетке

Результаты

Scores: 309371 997080 1488635 1 19 45832 9 94637 2893543 210750 742386 1677242 206614 111809 1 1738598 1 1 342984 2868939 190484 3354458 568267 280796 1 1 1 679704 2858998 1 409584 3823 200724 1 973317 849609 3141119 1 1987305 1 1 57105 245412 1223244 2 1603915 2784761 9 12 1 1839136 1 298951 2 14 138989 501726 1365264 308185 707440 22 772719 17342 63461 3142044 19899 3 409837 48074 3549774 138770 32833 1 1 1184121 67473 310905 1996452 4201 1701954 2799895 2041559 218816 174 433010 51036 1731159 1871641 1 23 2877765 1 127305 27875 626814 142177 2101427 167548 2328741 4 8433 2674119 2990146 466684 1 2 8 83193 388542 2350563 1 1140807 100543 1313548 31949 73117 73300 121364 1899620 1280524 1 10726 12852 7 2165 1 3 44728 2 122725 41 2 1902290 3 1 8581 70598 1148129 429767 1 112335 1931563 521942 3513722 1 2400069 1 3331469 141319 220942 205616 57033 63515 34 6 1419147 1983123 1057929 1 599948 2730727 2438494 5586 268312 1728955 1183258 95241 1537803 11 13 1157309 1750630 1 1 2690947 101211 3463501 1 258589 101615 212924 137664 19624 251591 509429 510302 1878788 1 4045925 1 21598 459159 118663 7 3606309 3 13016 17765 640403 1 72841 695439 1 135297 2380810 1 43 31516 14 1442940 1001957 95903 194951 1 238773 773431 1 1 975692 2 4990979 52016 3261784 2 413095 12 3 420624 7905 60087 760051 2702333 2572405 1 1717432 1 12 3040935 1 1 31787 60114 513777 1 3270813 9639 581868 127091 270 164228 274393 1275008 261419 597715 138913 28923 13059 1848733 2895136 7754 14 1 107592 1 3557771 2067538 147790 112677 119004 1 13791082842974 249727 838699 4067558 6 470799 695141 1 3 1 1276069 23691 831013 5 165142 1236901 1 187522 2599203 1 67179 81345 44111 2909946 94752 7 406018 991024 4 1 3 573689 6 748463 2166290 33865 670769 322844 5657 1131171 1990155 5 4536811 1785704 3226501 2030929 25987 3055355 192547 1761201 433330 27235 2 312244 13203 756723 81459 12 1 1 54142 307858 2 25657 30507 1920292 3945574 1 191775 3748702 3348794 4188197 366019 1540980 3638591 1 1840852 1 26151 2888481 112861 8 11 2 1 27231 1 74 106853 3 173389 2390495 25 1 83116 3238625 75443 1 1 2125260 1 49626 1 6 312084 159735 358268 54351 367201 2868856 5779 172554 119016 141728 3 1 6 9 1 1504011 1 168968 1868493 1 5 1 244563 2 2887999 3144375 1598674 1 1578910 45313 176469 30969 8 127652 1911075 9 1300092 224328 168752 8 1619669 292559 9090 2040459 705819 1852774 10 139217 16 1221670 355060 339599 3 2184244 2546028 1 1 11 70958 242187 1 80737 1 190246 3 1 1 577711 150064 1 1047154 3851461 92399 224270 612237 1 3 3330053 1 1 1192533 615756 267923 144724 2 1 150018 4621881 1 6 299247 115996 2 10 6 185495 76351 465554 178786 1802565 257101 56 2491615 1 24547 1 1203267 32 5741149 541203 11393 1 368082 540534 16167 113481 2004136 13045 17 1 12 333803 14 1955075 1 4 38034 1286203 2382725 26777 1 180312 1 87161 4773392 1244024 1146401 3 80598 2983715 1 63741 1 1 2561436 16 1 1 1807854 1239680 200398 2 46153 1400933 11 5058787 8787 1 98841 89162 1106459 112566 1 4138891 2858906 101835 81375 539485 6587808 1 5359988 1 1 869106 443452 120748 436156 2 2 3944932 1 1875599 2 3081185 733911 447824 1 1 23187 3082414 33 3 1 1 2053904 410824 104571 885952 1946162 2 294773 364169 1 101310 2166548 1177524 2192461 12 4 3457016 90975 2356374 573234 53746 187527 7837 1441335 458407 52139 3387239 2030900 38 1648216 215105 212589 8278 1201586 244282 1 1 1897515 3957343 46 1 134481 1 1 2041785 3 1 37593 163173 1565457 3 1026885 1 34530 4655639 2 18 1940645 1550444 593209 1 2270700 706918 1 1 610113 9 1287883 3 1472134 1998685 1916822 1 296017 2 1 1737607 4155665 1510560 553342 56130 14436 13240604 4025888 1 4253261 174177 2043316 504151 2370989 420666 155232 1 219327 3752236 130062 571247 24 1 29015 31392 1020196 3 1117502 460873 7 1 228 8 133656 1 147008 1 93471 1 1 1 513410 4834094 1 14 1875636 182714 1504903 95263 4418053 1 357853 1135536 3698641 3 239316 4237884 131730 3878724 2158931 55650 1906785 1 26372 32 99217 1645677 379838 1 450352 7329657 112909 1 897980 2114198 308917 126215 1 53839 539997 238036 2 2270000 5 2388928 1668820 519153 58227 347528 1 1 2339954 10 5 2031341 54 2341529 2189774 112731 1 21918 748662 2068921 2 2232504 2923457 97740 3858 16604 398940 388755 1875003 667810 53633 315866 839868 1 7 1 14238 185 4 14 1 2 178947 1965719 398323 120849 48 1397222 961772 34124 2 160652 1 252629 246554 14529 1 299866 135255 490837 2863773 8 10 2 1906405 57 9782 118940 870003 255097 6 4187677 50965 3354376 17611 1804789 183601 158748 1539773 116107 77684 34738 2862836 1 2081903 727739 50328 2740070 17 923524 18 3089706 3144082 1 20 205247 347420 2076952 3725220 39270 2 15 49329 422629 5 1693818 2570558 2146654 1 5 129085 653766 47438 102243 389910 59715 21769 1246783 361571 4 120502 255235 1314165 3 3 5 2902624 76351 3117137 174413 2546645 14534 166054 1013583 1 1 2 9 3027288 3173742 338261 94929 1071263 4659804 1 506576 42798 4 984508 1 4 4 1 18541 7 1 269761 188905 2 1 92011 147031 677955 27484 1291675 2420682 99970 57943 1 4081062 1 250953 704904 4 349180 4273479 30528 2092508 2352781 3700946 1 77799 328993 3684623 3930179 1250080 1975798 54981 1621677 91664 1355832 1084049 721612 56950 197563 246868 5031 1 924076 1328694 58562 1 457662 2445958 1345169 957845 1056809 2485300 1687907 199029 3 9474 86928 1 2419980 3585265 570673 1 1514184 437383 1596697 29709 199606 126031 2 1541777 1 3 2090249 2402438 15 19 1423959 28 37852 4 1652596 1 405512 52 3 1948029 1 2 376 1155902 3 631665 3741991 57673 284026 424787 1 11569 5 1200313 1 20 2360854 1 119994 3889143 673424 797763 1 1 144306 1007659 1231874 75607 1 15 66187 8763 21366 146277 2684501 4458542 162223 3 1 5 94232 3036009 401312 19775 510737 3305062 58905 125783 274094 3089988 118483 1 106213 1 1289180 127905 30 528859 2 1215596 1955900 30 2236528 218643 1 2396631 1598175 1148688 452064 1 1840394 198540 1 1307187 107463 341396 2684981 9602 536871 1 148107 4068 4918434 1 2430254 2066144 88915 3585780 6464 259394 3098337 49601 42 79205 925658 1 2513666 26817 2738302 1 28 345735 5086930 361294 505662 386194 1103890 2653001 412247 4074274 2217918 1 519433 1338570 4289317 140138 18 2519983 168656 4546204 8 1 76545 511580 979214 9318 210013 50508 40 152908 17969 922507 1 7 32 1 388579 1 49886 13319 1066048 4663 27883 38419 1418098 2538216 1 778734 3556791 490764 666880 22746 5666164 4 20 1806284 21142 1 527906 2 12417 182224 49536 105029 206917 2427623 294247 1405136 321480 354137 84225 50 128073 1391176 352835 26074 91159 34229 237942 1 1519676 1 2428669 272681 148689 528951 560736 1 3548197 3833513 1438699 286613 1 1290904 47145 3456135 249648 277045 1012397 271073 1 6 149276 94843 11 177134 32336 2772732 7 22 37065 1 105299 76735 44 2211334 511942 30639 522056 5162 1899842 74 1 1448039 1 88817 21 1027532 555416 1 364383 1335609 167332 283252 49564 220972 1006800 3108886 801258 265596 61651 1 2413276 252747 416606 960925 54 311956 267135 3871698 22581 8978 2 10 1966155 3123429 28 46409 1 18433963725323 1769396 114766 49071 1 1 4228762 3483932 1139490 602592 2700468 770273 3 1 1 212087 281247 27093 156094 286299 1204001 18374 1 330780 1 1 25384 906728 99334 1250819 2161201 34 1027892 1 33449 2 129787 52246 94872 1536841 23470 1 1700323 1 1 3785351 1 95315 1014155 56570 22586 66842 7 156840 48752 1 3143722 1 1168309 2 4 101423 385892 42868 2893851 7 1783109 217499 24 460497 2003214 180135 3503010 131137 2 5240 1621601 2754811 11198 1 1 1105643 1 1671021 3 139611 18268 107229 44582 2211034 1 2880152747163 231008 262504 1 257760 1 1 52992 804418 2 2 4811272 1772250 3 1796530 1918647 1 1934549 1 100550 3448657 1681262 3 604526 320865 1901079 556908 2794800 2472000 637735 123663 1 3213187 118199 2553610 1 1750628 2563806 1 1670872 1 999609 50200 654831 1 164612 2865759 1841739 9 3744159 1331395 3202501 1 7 1 1 239868 1 1 581984 112413 401 1 29656 359367 74532 27226 51752 2583 1 645443 1559731 1 114195 1 85473 229474 111353 1 1521653 1 2568733 444398 2593568 18546 1 158085 1211147 1020006 23407 42514941388799 158442 1 1660358 5 34874 1594789 1551270 386464 502417 32280 170606 1954278 72486 3406066 11 52896 345631 4010742 33307 1951926 1441325 1886066 1 3 402778 3089364 351 28028 4301364 1 431569 5 3054030 375986 404966 1 449317 1230292 1 7 763949 1 2 3197443 1537806 335317 2 1 161263 1 1959902 1664530 139136 447570 1 1 50 158825 222939 1842131 11252 1680094 1017889 71 144808 1 53679 1 41278 1226724 1 1 2 10 2 1 112451 42133 1406662 1 112593 2 2832116 1544488 3579017 3029492 2752014 6 255091 731329 540861 1 426725 440330 212602 202358 173553 4 1189793 11031 84073 2084554 3963 1473295 1 642570 1 1423688 34509 75056 163273 490193 3200250 451777 157797 4156542 2386299 2794795 2735308 1332758 1193296 1131014 1001570 414257 4415511 4 3 1 3499595 536583 16731 93839 92382 1 45890 1 17695 8 867246 18 1607123 3197052 5 40009 1 329895 3497309 2416600 2316390 11 118179 2166659 2 136426 76762 2 14 2 3632525 214889 6 3900942 270409 230143 120414 417489 16706 1563597 31418 2 73 468763 88585 428274 3537347 2 1 491461 2806485 1 7 2950804 115684 4 1 429002 85771 2480 285541 186486 1 1 2430862 6 9 4 1833423 17143 353689 2568741 408890 2929237 208679 2198380 1 2501053 1933666 180843 1 1 2569886 1 17035 3449472 71357 246257 217898 1 47601 589824 401679 362878 13178 34464 1076419 1 554417 1 21248 2136449 1068 23029 8 766649 4 302879 274751 19 1 390259 1899931 233910 1392272 184492 2 2752059 55813 1 6 64674 205205 595508 1714309 582492 4821971 63973 1708726 189200 4548446 479425 2866037 1 1 1 2139319 1 1 3 1572621 2086152 2341038 1 619612 1 78942 772466 18932 1404368 936790 2263929 230200 3009227 251065 835010 88225 642856 824193 5559048 1 36348 2338046 481447 108132 2728223 3539009 1 197164 181408 171634 2172263 2317332 1598340 1318829 1746303 7 59657 1 1415452 122924 915828 1063890 40339 430186 4 2165185 2250922 704568 85138 4417453 255 326360 33541 3 49759 72127 912537 599665 1 29169 168741 349838 996835 1548193 2 28449 803521 4 2 2 3359043 3243259 1 491574 1675000 186105 3203018 11 39127 959876 334480 873131 70262 137080 1076591 1 2155613 74804 893022 2473922 1 1 269835 5 2407308 3 55200 905207 1 1 1245609 65934 7 1372126 530582 1383562 1 1 2718341 1 3947638 4 76837 412551 11 1 1 1208080 3024670 277 46485 1 9 562183 46 2985858 3379885 67816 1896527 1 105478 2035453 3026415 1 189256 2992616 2098002 1099666 775250 5913 13 406948 166773 1 322250 41919 480047 64950 17435 2147428 2336270 3330243 352709 86029 1398723 106236 312951 1 408211 252689 847088 2 17 34088 13128 187366 2 1559482 2349010 1651122 2371088 401005 1715445 1 29483921 1464444 50228 2365851 1651636 768715 226704 23677 83501 1 252623 444628 34 3640316 3602127 45369 1 1 1978261 1 3019189 1 25411 2177552 192839 191146 293712 3840622 182598 4069200 175757 1 2250458 4 1 7 2740824 2753005 1 2836428 1 12 19 2 1788326 3302198122211 3386546 1176663 20847 28 1194294 794665 2630378 13624 722012 2273872 1549353 1 3 1735700 1668388 416 970581 258382 295427 1 121571 3193610 3764806 1 368985 20436 89411 3 16130 2 241879 1 2996216 136958 2382095 510146 1762872 1372194 4215387 346915 4423 1 904153 2004500 248495 836598 3529163 27 2547535 1424181 1885308 1 1056747 289743 176929 2299073 170473 1 1 839941 12382 51457 608526 1684239 4843522 34550 929855 2767014 2979286 1 340808 184830 131077 57298 63854 381689 201998 1715328 118687 69190 123466 1 2 69392 159797 382756 1513430 2506318 457 1
Geometric mean score: 10983.8 in 31214 seconds

Я перешел на g ++ / MinGW и 3 потока.
Код, сгенерированный GNU, более чем в два раза быстрее кода Microsoft.
Не удивительно, что с их ужасающей реализацией STL.

Телепорт

Эффект телепорта сильно зависит от позиции. До сих пор я был рад, что телепортер всегда считался либо хорошим (если смотреть как пустое пространство), либо всегда плохим (если рассматривать его как стену, чтобы ни один грызун никогда его не взял).

Это слишком грубая модель.
Данный телепорт может продвигать крысу вперед на несколько клеток от цели, но когда тот же телепортер может сбросить крысу с доски.
Такой телепорт, скорее всего, будет признан сносным (так как он повышает физическую форму быстрее, чем когда «идет» к одному и тому же месту х), станет частью доминирующего генома и убьет почти всех крыс, которые считают его «всегда безопасным».
Поскольку крысы не имеют возможности узнать свое положение Х, единственное решение для обнаружения этих коварных телепортов - решить, наступать ли на них, основываясь на единственных доступных контекстных данных, то есть на цветовой сетке 5x5.

Для этого я определил 4 типа цветовых генов:

  • детектор опасной ловушки
  • пусто проходимо где-нибудь на трассе
  • блокировка запрещена в любом месте трассы
  • Луч виден как пустой или блок в зависимости от окружающей среды

Идея состоит в том, чтобы попытаться отличить телепорта, посмотрев на его ближайших 8 соседей. Поскольку вероятность наличия 8 идентичных соседей в данном месте очень мала, это должно позволить идентифицировать уникальный экземпляр каждого телепорта.

8 соседних цветов можно объединить, чтобы сформировать локальную подпись, которая инвариантна относительно положения в лабиринте. К сожалению, 8 соседей видны только для ячеек, расположенных в пределах внутреннего квадрата поля зрения 3х3, поэтому подписи на краях поля зрения будут неточными.
Тем не менее, это даст нам постоянную контекстную информацию в непосредственной близости, которой достаточно, чтобы увеличить вероятность успешной навигации по телепортам.

Генные пучки имеют 2-битное переменное поле.
Для данной локальной сигнатуры телепорта существует один шанс из четырех, что ячейка луча будет считаться непроходимой. Каждое значение поля выбирает одну из этих четырех возможностей.
В результате мутация гена луча на этих 2 битах будет циклически проходить через 4 возможных контекстных значения цвета.

Кроме того, самые важные цвета, которые нужно угадать - это стены и ловушки. Это означает, что мы должны разрешить обнаружение телепорта только после того, как крысы узнают, где находятся стены и ловушки.

Это делается путем обновления локальных подписей только спарринг. Текущий критерий для обновления локальной подписи должен быть в непосредственной близости от цвета, определенного как потенциальный телепорт.

Кодирование использует 5 бит на цветовой ген и группирует типы, чтобы освободить 3 менее значимых бита для кодирования значения 0,7:

  • 4 опасности
  • 4 пустых
  • 4 блок
  • 4 луча

Каждый ген луча имеет 1/4 вероятности того, что его считают блоком, и 3/4 вероятности того, что его считают пустым, поэтому 4 луча представляют в среднем 1 блок и 3 пустых.

Таким образом, средняя доля, представленная случайным разбросом в 16 цветов:

  • 4 опасности
  • 7 пустых
  • 5 блок

Этот микс, похоже, пока дает лучшие результаты, но я еще не закончил его настройку.

Изменчивость гена

Одно можно сказать наверняка: значения кода, выбранные для представления типов генов, имеют решающее значение. Обращение двух значений может стоить 2000 и более очков.

Здесь снова причина, почему это за пределами моей математики.

Я предполагаю, что вероятности мутации от одного типа к другому должны быть сбалансированы, или, как в матрице Маркова, совокупные вероятности имеют тенденцию ограничивать значения подмножеством, имеющим самые высокие вероятности входящего перехода.

Путь к спасению

Поиск путей значительно сократит количество посещаемых ячеек, позволяя проверять только наиболее вероятные результаты для достижения цели. Таким образом, не только избегаются некоторые частые тупики, но и неправильные цветовые коды также гораздо чаще обнаруживаются ранее.
В результате время сходимости сильно уменьшается.

Тем не менее, это не помогает решить карты, где геном не может дать правильное представление дорожки.

Что делать с дебилами?

Посмотрев визуально на трассу, я понял, почему стратегия по умолчанию, которая пытается двигаться вперед, даже когда кажется, что перед стенами ничего нет, действительно лучше, чем сдерживаться.
«Стены» в действительности могут быть телепортами, которые производят так много неприятных результатов, что геном отображает их как препятствия, которые никогда не будут преодолены, но в редких случаях конкретный случай этого непослушного телепорта может иметь положительный (или, по крайней мере, не смертельный) эффект таким образом, взятие этого вместо того, чтобы двигаться назад, увеличивает шансы найти путь к победе.

Ранняя конвергенция

Мне кажется, что уровень мутаций немного ниже (по крайней мере, для моих грызунов).

Текущее значение 0,01 дает ДНК 37% шансов выжить в неизменном мутационном процессе. Изменение параметра на 0,0227 снижает эту вероятность примерно до 10%.

Таинственная формула: P 1 битовая мутация = 1-P целый геном в целости и сохранности 1/100 , 100 - длина битового генома.

Например, для вероятности 10%, битовая мутация P 1 = 1 - 0,1 1/100 = 0,0277
Для вероятности 5%, P = 1 - 0,05 1/100 = 0,0295
Обращая формулу, мы обнаруживаем, что 0,01 дает 37% шансов быть без изменений по мутации.

Я повторил тот же тест (используя фиксированную последовательность случайных семян) с вероятностью 10%.
На многих картах предыдущие неудачи превратились в (ограниченные) успехи. С другой стороны, огромных демографических взрывов было меньше (что имело интересный побочный эффект ускорения вычислений).
Несмотря на то, что очень высокие баллы (один миллион +) были менее частыми, количество более успешных пробежек было более чем достаточно, чтобы компенсировать это.
В итоге среднее значение выросло с 1400+ до 2000.

Установка P на 5%, напротив, дала среднее значение около 600.
Я предполагаю, что частота мутаций была настолько высока, что геном победивших крыс слишком часто превращался в менее эффективные варианты.

Как это работает

С добавленными детекторами телепортов количество неудачных игр (оценка <10) значительно сократилось.
На 2000 пробных запусках было только 1/3 отказов.
Среднее геометрическое значение выросло только с 2900 до 3300, но это число не отражает улучшение.

Пустые цвета часто угадываются как лучи и опасности (обычно от 2 до 5). Геном «использует» эти цвета, чтобы заблокировать пути, из-за которых у крыс могут возникнуть проблемы.

Геном довольно хорошо угадывает ловушки (т. Е. Как только крысы способны достичь цели, цвета, представляющие фактические детекторы ловушек, предполагаются примерно в 90% случаев).
Он также использует новые коды лучей для телепортов, хотя и реже (возможно, потому, что «коварные» телепортеры встречаются реже, чем ловушки, а другие цвета лучей / опасности эволюционируют, чтобы заблокировать путь к последним экземплярам этих предателей).

Судя по количеству игр, в которых победивший геном появляется после 5000 ходов или более, я считаю, что эта новая порода сильно выиграет от увеличения частоты мутаций.


Поскольку существует четное количество ловушек, пустых стен и телепортов, вам нужно всего 3 бита, чтобы точно сохранить пропорции (даже если вы рассматриваете ловушки == стены). Кроме того, рассматривали ли вы / отбросили идею использования неиспользуемых битов смещения ловушек в борьбе со стенками? Так как цель состоит в том, чтобы не наследовать от родителей, вы можете фактически использовать все биты в борьбе со стенками. Я бы не подумал, что у них нет причин быть уникальными.
Mooing Duck

1
@MooingDuck Я проверил вашу идею повторного использования смещенных бит, но это не удалось. Как я и опасался, повторное использование информации для двух разных целей, похоже, не работает. Предположим, например, что биты смещения данного цвета необходимы для того, чтобы геном выбрал правильное вертикальное направление в данном пути, этот цвет больше не может представлять значимую ловушку, не разрушая путь, который зависит от тех же данных. Я также пытался использовать 6 битов, но, как я и опасался, оставшихся 4 антистенгеров было слишком мало.

1
Полезно знать, но я предложил две идеи: одну - использовать все биты (повторно использовать некоторые), а другую - использовать неиспользуемые биты смещения ловушек для стен / пустых. Ты пробовал оба? (Я полностью понимаю, что если вы не хотите пытаться, вряд ли вам стоит пытаться, если вы этого не хотите)
Mooing Duck

1
Я попробовал оба, и оба потерпели неудачу. Смещения ловушек важны, даже когда ген их не использует, потому что этот ген все еще может мутировать обратно в цвет ловушки, и в этом случае смещение ловушки, вероятно, будет мутировать в те биты контекста, которые наиболее выгодны, и потеряет свое значение как смещение , Теперь он будет мутировать обратно к выгодному значению смещения и уничтожать пути крыс, которые зависели от него, в качестве контекстных индикаторов. Я думаю, что видел случай таких колебаний с моим графическим инструментом, но не легко продемонстрировать ясный пример этой проблемы.

16

ColorScorePlayer, предварительная оценка ≈ 22

Это бот, которого вы видите на работе в GIF в испытании.

Это был наш тестовый бот на этапе разработки. Он использует геном для хранения показателя качества для каждого из 16 цветов. Затем он делает движение вперед, которое перемещает его в цвет с лучшим счетом (и никогда не перемещается на -1). В случае ничьей выбирается случайный ход между ячейками связывания.

Мы портировали этот проигрыватель на все языки контроллера, поэтому он служит примером того, как их использовать:

питон

class ColorScorePlayer(Player):
    def __init__(self):
        Player.__init__(self)
        self.coords = [Coordinate( 1, 0),
                       Coordinate( 1,-1),
                       Coordinate( 1, 1)]
        self.n_moves = len(self.coords)

    def turn(self):
        max_score = max([self.bit_chunk(6*self.vision_at(c.x, c.y), 6) for c in self.coords if self.vision_at(c.x, c.y)>=0])
        restricted_coords = [c for c in self.coords if self.vision_at(c.x, c.y)>=0 and self.bit_chunk(6*self.vision_at(c.x,c.y), 6) == max_score]

        return random.choice(restricted_coords)

Рубин

class ColorScorePlayer < Player
    def initialize(rng)
        super(rng)
        @coords = [Vector2D.new( 1,-1),
                   Vector2D.new( 1, 0),
                   Vector2D.new( 1, 1)]
    end

    def vision_at(vec2d)
        @vision[vec2d.x+2][vec2d.y+2]
    end

    def turn
        max_score = @coords.map { |c|
            color = vision_at(c)
            color < 0 ? -1 : bit_chunk(6*color, 6)
        }.max

        restricted_coords = @coords.select { |c|
            color = vision_at(c)
            color >= 0 && bit_chunk(6*color, 6) == max_score
        }

        restricted_coords.sample(random: @rng)
    end
end

C ++

coord_t colorScorePlayer(dna_t d, view_t v) {
    const int chunklen = DNA_BITS / N_COLORS;
    int ymax[3], nmax, smax = -1;
    for(int y = -1; y <= 1; y++) {
        if(v(1, y) == OUT_OF_BOUNDS) continue;
        int score = dnarange(d, v(1, y)*chunklen, chunklen);
        if(score > smax) {
            smax = score;
            nmax = 0;
        }
        if(score == smax) ymax[nmax++] = y;
    }
    return {1, ymax[v.rng.rint(nmax)]};
}

C #

public static void ColorScorePlayer(GameLogic.IView v, GameLogic.IGenome g, Random rnd, out int ox, out int oy)
{
    ox = 0;
    oy = 0;

    var max_score = cspcoords.Where(c => v[c.x, c.y] > -1).Select(c => g.cutOutInt(6 * v[c.x, c.y], 6)).Max();
    var restrictedCoords = cspcoords.Where(c => v[c.x, c.y] > -1 && g.cutOutInt(6 * v[c.x, c.y], 6) == max_score).ToArray();

    Coord res = restrictedCoords[rnd.Next(restrictedCoords.Length)];

    ox = res.x;
    oy = res.y; 
}

Ява

package game.players;

import java.awt.*;
import java.util.Map;

public class ColorScorePlayer extends Player{
    private static final Point[] possibleMoves = {new Point(1, 0), new Point(1, -1), new Point(1, 1)};

    @Override
    public Point takeTurn(String genome, Map<Point, Integer> vision) {
        int chunkLength = genome.length()/16;
        int maxSum = -1;
        Point maxSumMove = possibleMoves[0];
        for (Point move: possibleMoves){
            if (vision.get(move) == -1){
                continue;
            }
            int initialPoint = chunkLength*vision.get(move);
            int sum = 0;
            for (int i = initialPoint; i < initialPoint + chunkLength; i++){
                sum = (sum<<1)+Integer.parseInt(genome.charAt(i)+"");
            }
            if (sum > maxSum){
                maxSum = sum;
                maxSumMove = move;
            }
        }
        return maxSumMove;
    }
}

Игрок забивает довольно непоследовательно. Вот 50 случайных прогонов:

Scores: 1 1 1132581 3 43542 1 15 67 57 1 11 8 623162 1 1 1 134347 93198 6 1 2 1 1 245 3 1 1 27 1 31495 65897 9 5 1 2 20 2 117715 1 1 1 20 64616 5 38 1 2 1 2 12

12

ColorFarSeeker, C ++ ≈ 74,7

Эта задача действительно довольно забавная и простая, если вы попробуете ее.

Не откладывайтесь на длинное описание.
Просто зайдите на GitHub и проверьте все ... все будет намного понятнее! :)

Симулятор C ++ настоятельно рекомендуется для его скорости. Даже после того, как я закончил перевод своей программы на C ++, симуляция Python все еще не остановилась.

Это улучшенный вариант ColorScorePlayer. Чтобы эффективно использовать свое представление 5x5, он рассматривает шаги в 2 шагах от него, используя взвешенную функцию. Шаги на 1 шаг впереди имеют больший вес, так как они оказывают более непосредственное влияние на выживание. Движение на 2 шага впереди дает меньший вес.

Пытается двигаться вперед, но если безопасного движения не видно ... то пытается вбок ... а если все остальное терпит неудачу, перемещается назад случайным образом.

coord_t colorFarSeeker(dna_t d, view_t v) {
#define s(w,x,y) (v(x,y)>-1?((b+dnarange(d,l+m+n*v(x,y),n))*w):0)
#define max2(a,b) (((a)>(b))?(a):(b))
#define max3(a,b,c) (max2(a,max2(b,c)))
#define push(vec,maxScore,score,x,y) if(score==maxScore&&v(x,y)>-1)vec.push_back({x,y});
#define tryReturn() if(vec.size()){return vec[v.rng.rint((int)vec.size())];}vec.clear();

    // Some constants to tweak
    int k = 4;
    int l = 3;
    int m = dnarange(d, 0, l);
    int n = 4;
    int b = dnarange(d, l, k) + 10;

    std::vector<coord_t> vec;

    // Looks forward for good moves...
    int upRightScore = s(1,0,-2) + s(1,1,-2) + s(1,2,-2) + s(5,1,-1);
    int forwardScore = s(1,2,-1) + s(1,2,0) + s(1,2,1) + s(5,1,0);
    int downRightScore = s(1,0,2) + s(1,1,2) + s(1,2,2) + s(5,1,1);
    int maxForwardScore = max3(upRightScore,forwardScore,downRightScore);
    push(vec,maxForwardScore,upRightScore,1,-1);
    push(vec,maxForwardScore,forwardScore,1,0);
    push(vec,maxForwardScore,downRightScore,1,1);
    tryReturn();

    // Looks sideways for good moves...
    int upScore = s(1,-1,-2) + s(1,0,-2) + s(1,1,-2) + s(5,0,-1);
    int downScore = s(1,-1,2) + s(1,0,2) + s(1,1,2) + s(5,0,1);
    int maxSideScore = max2(upScore,downScore);
    push(vec,maxSideScore,upScore,0,-1);
    push(vec,maxSideScore,downScore,0,1);
    tryReturn();

    // If all else fails, move backwards randomly.
    // I have tried considering the scores of backmoves,
    // but it seems worse than just randomly moving backwards. 
    vec.push_back({-1,-1});
    vec.push_back({-1,0});
    vec.push_back({-1,1});
    return vec[v.rng.rint((int)vec.size())];

}

Гол:

Есть довольно много единиц ... которые могут быть немного удручающими, когда вы видите консоль, извергающую 1 после друг друга. Как планета со всем необходимым для жизни, но без признаков продвинутой цивилизации крыс ...
Тогда случайный всплеск. :)

Хм ... видимо, мне повезло в моей первой серии пробежек, получив геометрическую форму 300+. Баллы действительно сильно колеблются. Но в любом случае, с большим количеством прогонов симулятора, он, вероятно, ближе к ≈ 74. (Спасибо за помощь в симуляции и его невероятно быстрой программе)

Результаты моих заездов: 6 6 53 1 5 101223 89684 17 2 303418 4 85730 24752 1 1 1 3482515 39752 1 59259 47530 13 554321 1 563794 1 1770329 1 57376 1 123870 4 1 1 79092 69931 594057 1 69664 59 1 6 37857 1733138 556 2 1 51704 1 254006 4 24749 1 117987 49591 220151 26 4292194 23 57616 72 67 1 4 308039 1 1 103 89258 1 286032 1 5 3 1 5 114851 46 143712 5 15 9 80 7425 1 1 7 1 108379 70122 97238 1 1 5 2 23 104794 1 10476 59245 1 204 1 1 12 1 29641 1 314894 18785 13 1 3 1 1 1 2 526001 1 1 1 27559 29285 3 3 128708 70386 30 2 2 1 208531 331 1 2 1 61 114993 1 15 51997 1 2 1 146191 1 31 4 3 1 161422 207 1 64 1 1 1 68594 145434 87763 150187 169 185518 1 1 1 1 24208 2570 1 1 537 1 1 462284 1 2 55 1 1 1 214365 1 40147 2 213952 1 29 3 1 2144435 5 4502444 72111 1 1 1 1 1 774547


1
Я получил среднее геометрическое 74,7 с 1000 игр, отличная работа.
feersum

8

Епископ - Питон, предварительная оценка 1.901

Епископ всегда движется по диагонали, поэтому половина доски недоступна на данном треке по доске, но это означает, что меньше потенциальных ходов для кодирования, поэтому каждый отдельный бит генома может представлять ход (слон никогда не отступает). Какой бит ссылаться, определяется на основе блока квадратов 3х3 впереди (справа) от образца. Лучший ход в данной ситуации - это всего лишь одна мутация.

Этот бот сначала быстро учится, но потом часто достигает потолка, прежде чем достичь финиша, по-видимому, когда возникает одна из следующих двух проблем:

  • Две или более части доски отображаются на один бит, но требуют разных ходов.
  • Некоторые доски не проходимы, используя только диагональные ходы.

Код

class BishopPlayer(Player):
    def __init__(self):
        Player.__init__(self)
        self.coords = [Coordinate(1,-1),
                       Coordinate(1, 1),
                       ]
        self.inputs = [(x,y) for x in (0,1,2) for y in (-1,0,1)]

    def turn(self):
        # Move away from out of bounds areas
        if self.vision_at(0,-1) == -1:
            return self.coords[1]
        if self.vision_at(0,1) == -1:
            return self.coords[0]

        # Move right, and either up or down based on one bit of the genome
        bit_to_use = sum(self.vision_at(self.inputs[i][0],
                                        self.inputs[i][1]
                                        ) * (16 ** i) for i in range(9)
                         ) % 100
        return self.coords[self.bit_at(bit_to_use)]

Несмотря на эти ограничения, в редких случаях епископ преуспевает, с отдельными образцами, заканчивающими несколько кругов доски каждый. Я думал, что на данном круге образец может двигаться только на половине доски (эквивалентно только черным квадратам или только белым квадратам на шахматной доске). Однако, как отметил Мартин Бюттнер, телепортер может перемещать образец из черного квадрата в белый или наоборот, поэтому на большинстве досок они не будут ограничены.

(Есть две пары совпавших типов телепортов, и у каждого есть вероятность 0,5 смещения, которое перемещает образец в другую половину черного и белого квадратов. Таким образом, вероятность того, что на доске есть только телепорты, ограничивающие образец одним половина доски за круг составляет всего 0,25.)

Очки показывают, что случайные победы чередуются с длительными периодами, не дотягивающими до финиша:

Счета: 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 11 1 2 1 1 1 1 1 6 1 8 1 10 15 1 1 12544 1 2 1 1 1 1 3 7554 1 1 1 1 1


8

Игрок с призовым бонусом: среднее геометрическое 50,35 (тест на 5000 игр)

Этот бот подсчитывает квадраты по отдельным цветам на основе 6-битной секции ДНК, как у игрока с цветовой шкалой, но с другой системой счисления. Этот бот был мотивирован мыслью, что довольно произвольно, что один из битов изменяет значение оценки на 32, а другой - только на 1. Он присваивает значение n (n + 1) / 2 серии n последовательных 1 бит. Кроме того, он добавляет механизм рандомизации в попытке избежать застревания. Это сделает случайное движение вперед с вероятностью 1 к 30.

Для сравнения, игрок с цветовой шкалой набрал от 30 до 35 баллов в паре тестов из 1000 игр. Интересно, что максимальный игровой счет игрока с цветовой шкалой находился в диапазоне 3-5 миллионов, а максимальный бонус за бег - всего 200 тысяч. Бонус за прогон выигрывает от логарифмической средней системы подсчета очков, получая ненулевой результат более последовательно.

Запуск 5000 игр занял около 20 минут с 6 потоками на контроллере C ++.

coord_t runbonus(dna_t d, view_t v) {
    int ymax[3], nmax, smax = -1;
    if(!v.rng.rint(30)) {
        int y;
        while(!~v(1, y = v.rng.rint(-1, 1)));
        return {1, y};
    }
    for(int y = -1; y <= 1; y++) {
        if(v(1, y) == OUT_OF_BOUNDS) continue;
        int score = 0;
        int streak = 0;
        for(int i = 0; i < 6; i++) {
            if(d[6*v(1,y) + i])
                score += ++streak;
            else
                streak = 0;
        }
        if(score > smax) {
            smax = score;
            nmax = 0;
        }
        if(score == smax) ymax[nmax++] = y;
    }
    return {1, ymax[v.rng.rint(nmax)]};
}

просто из любопытства, сколько времени занял тест 5000 треков? Моим крысам требуется более часа, чтобы завершить 1000 треков, поэтому я должен был позволить компьютеру работать всю ночь, чтобы воспроизвести ваш тестовый пример.

@kuroineko Ответ на ваш вопрос уже был в моем ответе.
feersum

ой, извините. Затем я попробую ваш код на моем ПК, чтобы увидеть, какую роль играет аппаратное обеспечение в разнице скоростей. И, возможно, попробуйте использовать GCC вместо MSVC. Я заметил повышение производительности на 30% по сравнению с MSVC для пары других битов, требующих большого объема вычислений.

Ваш код работал чуть более 20 минут для 1000 треков на моем i3-2100@3.1GHz с 4 потоками. Счет был около 56 . Похоже, это означает, что мой компьютер в 5 раз медленнее, чем ваш, и мой код будет примерно в 6 раз медленнее на данной машине (но получение лучшего результата механически подразумевает более длительное время вычислений). Поскольку я слишком сломлен, чтобы купить новый ПК, пришло время для небольшой оптимизации ...

8

StarPlayer | C ++ | Оценка: 162 (на основе 500 пройденных игр)

Этот игрок пытается использовать A *, чтобы найти лучший путь вперед. Он назначает веса таким же образом, как ColorScorePlayer, и пытается найти путь к правому краю представления. Реализация не самая красивая, которую я когда-либо делал, но она не слишком медленная, по крайней мере.

#include <utility>

#define IDX(a,b) a[VIEW_DIST + b.x][VIEW_DIST + b.y]

std::pair<coord_t,int> planAhead(int weights[N_COLORS], view_t &v, coord_t target) {
    bool open[VIEW_DIST*2+1][VIEW_DIST*2+1] = {false};
    bool closed[VIEW_DIST*2+1][VIEW_DIST*2+1] = {false};
    int f_score[VIEW_DIST*2+1][VIEW_DIST*2+1] = {0};
    int g_score[VIEW_DIST*2+1][VIEW_DIST*2+1] = {0};
    coord_t came_from[VIEW_DIST*2+1][VIEW_DIST*2+1] = {{0,0}};
    open[VIEW_DIST][VIEW_DIST] = true;
    g_score[VIEW_DIST][VIEW_DIST] = v.rng.rint(5);
    f_score[VIEW_DIST][VIEW_DIST] = (abs(target.x) + abs(target.y)) * 10;
    for (;;) {
        coord_t current{VIEW_DIST+1,0};
        for (int x = 0; x < (VIEW_DIST*2+1); x++)
            for (int y = 0; y < (VIEW_DIST*2+1); y++)
                if (open[x][y] && (current.x > VIEW_DIST || f_score[x][y] < IDX(f_score,current)))
                    current = {x - VIEW_DIST, y - VIEW_DIST};
        if (current.x > VIEW_DIST)
            return {{1,0}, 1000000};
        if (current.x == target.x && current.y == target.y)
            break;
        IDX(open,current) = false;
        IDX(closed,current) = true;
        for (int dx = -1; dx <= 1; dx++) for (int dy = -1; dy <= 1; dy++) {
            if (dx == 0 && dy == 0)
                continue;
            coord_t tentative{current.x + dx, current.y + dy};
            if (abs(tentative.x) > VIEW_DIST || abs(tentative.y) > VIEW_DIST)
                continue;
            if (IDX(closed,tentative))
                continue;
            auto color = v(tentative.x, tentative.y);
            if (color == OUT_OF_BOUNDS)
                continue;
            auto tentative_g = IDX(g_score,current) + weights[color];
            if (!IDX(open,tentative) || tentative_g < IDX(g_score,tentative)) {
                IDX(came_from,tentative) = current;
                auto distance = abs(tentative.x - target.x) + abs(tentative.y - target.y);
                IDX(f_score,tentative) = tentative_g + distance * 10;
                IDX(g_score,tentative) = tentative_g;
                IDX(open,tentative) = true;
            }
        }
    }
    auto prev = target, current = target;
    while (current.x != 0 || current.y != 0)
        prev = current, current = IDX(came_from,current);
    return {prev, IDX(g_score,target)};
}

coord_t starPlayer(dna_t d, view_t v) {
    const int chunklen = DNA_BITS / N_COLORS;
    int weights[N_COLORS];
    for (int i = 0; i < N_COLORS; i++)
        weights[i] = dnarange(d, i*chunklen, chunklen);
    std::pair<coord_t,int> choice{{1,0}, 1000000};
    for (int y = -VIEW_DIST; y <= VIEW_DIST; y++) {
        auto plan = planAhead(weights, v, {VIEW_DIST, y});
        if (plan.second < choice.second)
            choice = plan;
    }
    return choice.first;
}

Примерные оценки:

4 92078 1 10 1 1 3 2 2862314 5 24925 1 3 2 126502 1 24 1097182 39 1 1 1 47728 227625 137944 15 1 30061 1 1 1 3171790 19646 10 345866 1 1 1 829756 425 6699 22 8 1 1 6 6 104889 125608 1


1
В 1000 играх я получил 133,2 балла, приятно.
feersum

7

WallGuesser - набрал 113,266 в тесте 1000 игр

кодирование

Я сделал действительно простое 6-битное / цветное кодирование. Расшифровать цвет [n]

  • Суммируйте каждый n-й бит в геноме до 96
  • Если сумма очков> = 4, скажем, этот квадрат заблокирован
  • Если сумма очков <= 4, то ее окончательный результат равен 2 ^ от суммы очков.

Распределяя биты для цвета по всему геному, я увеличиваю вероятность того, что биты от обоих родителей будут использоваться для каждого цвета.

движение

Я использую (я уверен, что не очень эффективный) поиск на основе *, чтобы найти путь с наименьшей стоимостью к любому из квадратов правого края. Если цвет отображается как «заблокированный», то он никогда не будет введен при поиске. Если поиск не может найти путь, он предполагает, что эта крыса не подходит для воспроизведения, и пытается закончить его, перемещая один влево.

Уменьшение количества непригодных крыс

Поскольку мой геном эффективно угадывает, какие квадраты являются настенными или отсталыми телепортами, крысы, у которых нет догадок (нет цветов, которые отображаются на заблокированные), не очень подходят. Чтобы попытаться удалить этих крыс, если ни один цвет не будет помечен как заблокированный, тогда КАЖДЫЙ цвет помечается как заблокированный, и крыса всегда будет перемещаться на одну влево.

СДЕЛАТЬ

В настоящее время в поведении нет случайности, поэтому крысам легко застрять.

#include "./gamelogic.cpp"

#include <algorithm>
#include <set>
#include <map>
#include <climits>

bool operator< (const coord_t &a, const coord_t &b){
    if(a.x != b.x){ return a.x < b.x; }
    else if (a.y != b.y){ return a.y < b.y; }
    else{ return false; }
}

bool operator== (const coord_t &a, const coord_t &b){
    return (a.x == b.x) && (a.y == b.y);
}

int coordDistance(const coord_t &a, const coord_t &b){
    int xDif = abs(a.x - b.x);
    int yDif = abs(a.y - b.y);
    return xDif > yDif ? xDif : yDif;
}

int coordMinSetDistance(const coord_t &a, const std::set<coord_t> &ends){
    int min = INT_MAX;
    for (auto i : ends){
        int cur = coordDistance(a, i);
        if (cur < min){
            min = cur;
        }
    }
    return min;
}


class ColorMap{
public:
    view_t *v;
    int colors[16] = {};
    const int Blocked = -1;

    ColorMap(dna_t &d, view_t *v){
        this->v = v;

        //Decode the genome
        for (int i = 0; i <= (16*6); i++){
            if (d.at(i) == true){
                colors[i % 16]++;
            }
        }

        //Encode the result
        bool guessedWalls = false;
        for (int i = 0; i < 16; i++){
            if (colors[i] >= 4){
                colors[i] = Blocked;
                guessedWalls = true;
            }
            else{
                colors[i] = pow(2, colors[i]);
            }
        }

        if (guessedWalls == false){
            for (auto i : colors){
                i = Blocked;
            }
        }
    }

    int operator() (coord_t pos){
        if (abs(pos.x) > VIEW_DIST || abs(pos.y) > VIEW_DIST){
            return Blocked;
        }

        int value = (*v)(pos.x, pos.y);
        if (value == OUT_OF_BOUNDS){
            return Blocked;
        }
        else{
            return colors[value];
        }
    }

    void print(){
        int lower = -1 * VIEW_DIST;
        int upper = VIEW_DIST;
        for (int y = lower; y <= upper; y++){
            for (int x = lower; x <= upper; x++){
                std::cout << std::setw(3) << this->operator()({ x, y });
            }
            std::cout << std::endl;
        }
    }
};

class node{
public:
    coord_t pos;
    coord_t cameFrom;
    int gScore;
    int minDistance;

    node(coord_t pos, coord_t cameFrom, int gScore, int minDistance){
        this->pos = pos;
        this->cameFrom = cameFrom;
        this->gScore = gScore;
        this->minDistance = minDistance;
    }

    int fScore() const{ return gScore + minDistance; };

    bool operator< (const node &rhs) const{ return fScore() < rhs.fScore(); }
};

class EditablePriorityQueue{
private:
    //This is reversed so smallest are on top
    struct lesser{
        bool operator()(node *a, node *b) const{
            return (*b) < (*a);
        }
    };

    std::vector<node*> queue; // Use heap functions to maintain the priority queue ourself
    std::map<coord_t, node*> members;

public:
    EditablePriorityQueue(){};

    ~EditablePriorityQueue(){
        for (auto &m : members){
            delete m.second;
        }
    }

    bool empty(){ return members.empty(); }

    node *top(){
        auto top = this->queue.front();
        std::pop_heap(queue.begin(), queue.end(), lesser());
        queue.pop_back();
        members.erase(top->pos);
        return top;
    }

    void set(coord_t target, coord_t cameFrom, int gScore, int minDistance){
        auto targetLocation = members.find(target);

        //If the target isn't a member add it
        if (targetLocation == members.end()){
            auto *newNode = new node(target, cameFrom, gScore, minDistance);
            queue.push_back(newNode);
            std::push_heap(queue.begin(), queue.end(), lesser());
            members[target] = newNode;
        }
        //The target must be updated
        else{
            auto currentNode = targetLocation->second;
            if (currentNode->gScore > gScore){
                currentNode->gScore = gScore;
                currentNode->cameFrom = cameFrom;
                std::make_heap(queue.begin(), queue.end()); //More efficient way to do this?
            }
        }
    }
};

std::pair<coord_t, int> pathCost(ColorMap &m, coord_t start, const std::set<coord_t> &ends){
    EditablePriorityQueue openSet;
    std::set<coord_t> closedSet;
    std::map<coord_t, coord_t> cameFrom;

    openSet.set(start, start, 0, coordMinSetDistance(start, ends));
    while (openSet.empty() == false){
        auto current = openSet.top();
        closedSet.insert(current->pos);
        cameFrom[current->pos] = current->cameFrom;

        //Check if we're done
        if (ends.count(current->pos) != 0){
            //Recover the path
            coord_t path = current->pos;
            int finalScore = current->gScore;
            delete current;
            while (!(cameFrom[path] == start)){
                path = cameFrom[path];
            }

            return{ path, finalScore };
        }               

        //Examine current's neighbours
        for (int x = -1; x <= 1; x++) for (int y = -1; y <= 1; y++){
            coord_t neighbour = { current->pos.x + x, current->pos.y + y };

            if (x == 0 && y == 0){ continue; }

            closedSet.count(neighbour);
            if (closedSet.count(neighbour) != 0){ continue; }

            int neighbourScore = m(neighbour);
            if (neighbourScore == m.Blocked){ continue; }

            int tentativeScore = current->gScore + neighbourScore;
            openSet.set(neighbour, current->pos, tentativeScore, coordMinSetDistance(neighbour, ends));

        }
        delete current;
    }

    return{ { -1, 0 }, INT_MAX }; //Try to end it
}

coord_t myPlayer(dna_t d, view_t v) {
    auto ourMap = ColorMap(d, &v);

    std::set<coord_t> edges;
    for (coord_t edge = { VIEW_DIST, -1 * VIEW_DIST }; edge.y <= VIEW_DIST; edge.y++){
        edges.insert(edge);
    }

    //Move to the neighbor closest to a square on the right
    auto result = pathCost(ourMap, { 0, 0 }, edges);
    auto minMove = result.first;

    return minMove;
}

int main() {
    slog << "Geometric mean score: " << runsimulation(myPlayer) << std::endl;
}

Хм, это не компилируется для меня g++ -std=c++11 .\wallguesser.cpp -O2 -o .\wallguesser.exe. Я получаю много ошибок, но первая.\wallguesser.cpp:47:19: error: 'dna_t' has no member named 'at' if (d.at(i) == true){
Мартин Эндер

Нет проблем, просто меняются, atчтобы []исправить это.
feersum

7

FITTEST - средний геометрический балл: ~ 922 (2K пробежек)

Мой подход заключается в следующем:

  1. Узнайте, что убивает вид и определите желаемое поведение (функционал)
  2. Реализация желаемого поведения в коде (техническая)
  3. Дайте этому приоритет . Является ли это более важным или менее важным, чем другое желаемое поведение.
  4. Оптимизируйте среднюю геометрическую оценку, настраивая параметры решений.

Я протестировал более 2000 наборов параметров с теми же 50 семенами. Наиболее многообещающие наборы были отобраны и были оценены с использованием 250 идентичных семян, а наборы с самым высоким рангом были введены для следующего раунда теста. Поэтому мне удалось создать генетический алгоритм, чтобы найти оптимальный генетический алгоритм для этой проблемы, как это было предложено пользователем mbomb007 .

Желаемое поведение:

  1. Вид должен узнать, какие цвета безопасны, а какие плохи.
  2. Вид должен главным образом сосредоточить свое решение, куда двигаться, основываясь на 3 клетках прямо перед собой, но если нет хороших движений, следует рассмотреть вертикальные или обратные движения
  3. Вид должен также посмотреть, что находится за пределами 8 клеток вокруг него, и использовать это в информации при принятии решений.
  4. Вид должен научиться определять ловушки .
  5. Некоторые виды ошибочно полагают, что стены хороши, и пытаются все время двигаться к ним и поэтому застревают перед стенами. Если они являются видами с наивысшей оценкой в ​​то время, то их ДНК с неверным предположением о стенке многократно дублируется у новорожденных . Через некоторое время все виды застряли перед стенами, и ни один из них не достиг цели, чтобы набрать очки. Как остановить дебилов?

Методы хранения данных:

Мы хотим, чтобы виды научились чему-то, адаптировались к окружающей среде и стали наиболее приспособленными. Это неизбежно работает только в том случае, если обучение можно как-то сохранить. Обучение будет «сохранено» в 100 битах ДНК. Это странный способ хранения, потому что мы не можем изменить ценность нашей ДНК. Итак, мы предполагаем, что ДНК уже хранит информацию о плохих и хороших ходах. Если для определенного вида правильная информация хранится в его ДНК, он будет быстро двигаться вперед и производить много новых видов с его ДНК.

Я выяснил, что среднее геометрическое значение чувствительно к тому, как хранится информация. Предположим, мы читаем первые 4 бита из 100 бит ДНК и хотим сохранить это в целочисленной переменной. Мы можем сделать это несколькими способами:

  1. хранение десятичных данных: с помощью встроенной dnarangeфункции, пример: 4 биты 1011станут `1x2 ^ 3 + 0x2 ^ 2 + 1x2 ^ 1 + 1x2 ^ 0 = 15. Возможные значения (для 4 бит): [0, 1 , 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
  2. хранение данныхdnaStreakRange штрихов : с помощью функции (определенной ниже), например: 4bit 1011 станет 1x1 + 0x1 + 1x1+ 1x2 = 4. Возможные значения (для 4 бит): [0, 1, 2, 3, 6, 10]
int dnaStreakRange(dna_t &d, int start, int chunklen) {
    int score = 0;
    int streak = 0;
    for(int i = 0; i < chunklen; i++) {
        if(d[start + i])
            score += ++streak;
        else
            streak = 0;
    };  
    return score;
}
  1. Хранение данных битовой суммы: с помощью dnaCountRangeфункции (определенной ниже), например: 4bit 1011 станет 1x1 + 0x1 + 1x1 + 1x1 = 3. Возможные значения (для 4 бит): [0, 1, 2, 3, 4]
int dnaCountRange(dna_t &d, int start, int chunklen) {
    int score = 0;
    for(int i = 0; i < chunklen; i++) {
        if(d[start + i])
            score ++;
    };  
    return score;
}

Разница между способами хранения:

  • Метод десятичного хранения уязвим для однобитового изменения ДНК. Когда значение битовой суммы изменяется с 1011 на 0011, его значение изменяется с 3 на 2, что является незначительным изменением.
  • Метод десятичного хранения является однородным . Каждое из возможных значений имеет одно и то же изменение. Вероятность того, что вы прочитаете значение 15 из 4-битного блока памяти, составляет 1/16 = 6%. Метод хранения полос не является однородным . Вероятность того, что значение 4-разрядной последовательности меньше или равно 6, равна (15-3) / 16 = 81% (все 16 комбинаций, кроме 0111,1110,111). Ниже визуальное изображение, которое показывает форму распределения. Как вы можете видеть у синей стрелки, вероятность того, что 4-разрядная полоса будет меньше или равна 6, составляет 81%: визуализация распределения типов хранения десятичных, линейных и битовых сумм для двоичных чисел длиной 4,5 и 6 бит

Приоритет решения.

Когда ColorScorePlayer определил два движения вперед с одинаковыми баллами, делается произвольный выбор. ИМХО, вы никогда не должны использовать функцию случайной v.rng.rint()функции . Вместо этого вы должны использовать эту возможность равных баллов в качестве ловушки для оценки решений для эффектов второго порядка.

Эффект первого порядка получает наивысший приоритет. Если достигнуты равные баллы, преобладает решение с приоритетом 2 и так далее. Изменяя параметры решения, вы можете повлиять на вероятность того, что произойдут равные оценки, и таким образом изменить вес решений с приоритетом 1 и приоритетом 2.

Реализация желаемого поведения

Узнайте, какие цвета безопасны:

  • 33% из 16 цветов плохие, и поэтому, когда оценка хода ниже 63/3, движение не будет разрешено. Следовательно threshold = 63/3=21, где 63 - максимальный балл для 6 битов и 33% = 1/3 (можно посмотреть на графике выше).

Если хороших ходов нет, двигайтесь вертикально или назад:

  • Когда никакие поступательные движения не разрешены, вертикальные движения будут сравниваться друг с другом таким же образом. Если вертикальные ходы также не допускаются, обратные ходы ранжируются. Это достигается с помощью weightMoveпеременной.

Посмотрите, что находится за пределами:

  • Когда 2 или 3 хода имеют одинаковые оценки, поле 3x3 вокруг этих ходов будет определять (через x2и y2зацикливаться), какой вариант лучше (через mainSubScoreпеременную). Самый правый столбец в этом квадрате 3x3 является ведущим.
coord_t adjustedColorPlayer(dna_t d, view_t v) {
    const int chunklen = 6,threshold = 63/3;
    int xBestScore=0, yBestScore=0;
    long bestScore=-1, weightMove, weightMove2, mainScore;
    for(int x = -1; x <= 1; x++) {
        if (x < 0) weightMove = 1000; // moving backward
        if (x== 0) weightMove = 10000; //moving vertical
        if (x > 0) weightMove = 100000; //moving forward
        for(int y = -1; y <= 1; y++) {
            if(v(x, y) == OUT_OF_BOUNDS || (x==0&&y==0) ) continue;
            mainScore = dnarange(d,v(x,y)*chunklen,chunklen);
            if (mainScore<threshold+1) {
                mainScore =  0; //not a suitable move because too low score
            }else{
                mainScore*= weightMove;
                // when equal score, use sub score by examining 5x5 box to rank moves
                for(int x2 = x-1; x2 <= x+1; x2++){     
                    if (x2 < x) weightMove2 = 1; // moving backward
                    if (x2== x) weightMove2 = 10; //moving vertical
                    if (x2 > x) weightMove2 = 100; //moving forward
                    for(int y2 = x-1; y2 <= y+1; y2++){     
                        if(v(x2, y2) != OUT_OF_BOUNDS){
                            long mainSubScore = dnarange(d,v(x2,y2)*chunklen,chunklen);
                            if (mainSubScore>=threshold+1) mainScore+=mainSubScore*weightMove2;
                        }
                    }
                 }
            }
            if(mainScore > bestScore) {
                bestScore = mainScore;              
                xBestScore = x;
                yBestScore = y;
            }
        }
    }
    return{xBestScore,yBestScore};
}

Оценка: 123 (2K пробежек)

Первые 50 очков (18 игр набрали только 1 очко):

1 10 1 79947 3 1 11 125 7333287 23701 310869 53744 1 2 2 2 2 1 1 57556 2 688438 60 1 2 2636261 26306 1 125369 1 1 1 61895 27 1 36 1 91100 87636 1 2 47497 53 16 1 11 222384 1 1 1

Определить ловушки:

Я исследовал ДНК вида с наивысшей оценкой, когда произвольная игра завершилась с использованием хранилища битов (так что цветовая шкала имеет диапазон [0,4]):

  • набрал 0: телепортировался назад, обе стены, 1x сейф
  • 1: ловушка назад (такая безобидная), телепорт назад, 1x сейф
  • набрал 2: ловушка вперед (так опасно), 1x безопасно
  • набрал 3: Телепортируйся вперед, 5x сейф
  • Набрал 4: Телепортируйся вперед, 1х сейф

Из этого можно сделать вывод, что стены и телепорты получают правильную оценку. Ловушки не идентифицируются, поскольку они зависят от направления и цвета источника, тогда как оценка производится по цвету пункта назначения. Поэтому необходимо также хранить данные о цвете происхождения, поэтому v(0,0). В идеальном мире мы хотели бы хранить информацию для 16 цветов x 8 направлений x 3 бита = 384 бита.

К сожалению, доступно только 100 бит, и мы не можем использовать все это, так как нам также нужно немного памяти для решения, описанного выше. Поэтому мы сделаем 4 цветных бункера:

  • 0: цвет 0 - цвет 3,
  • 1: цвет 4 - цвет 7,
  • 2: цвет 8 - цвет 11,
  • 3: цвет 12 - цвет 16

и 4 корзины направления движения

  • 0: двигаться вертикально или назад,
  • 1: двигаться вперед,
  • 2: двигаться вперед,
  • 3: двигаться вперед вниз

Когда десятичная оценка равна 4 или выше (100 101 101 110 111), предполагается, что ловушка связана с этой ячейкой, в результате этот ход не будет выбран при возникновении равных оценок. Таким образом, идентификация ловушек - это эффект второго порядка, а «посмотри, что дальше» будет решением с третьим приоритетом.

int dnaLookup2(dna_t &d, int start, int chunklen, int storageMethod) {
    // Definition of storageMethod: 0=decimal, 1=streak, 2=bitsum
    int score = 0, streak = 0;
    for(int i = start; i < start+chunklen; i++) {
        int value = d[i];
        if (storageMethod==0) {
            score = (score << 1) |value;
        }else{
            if (storageMethod==1){
                if(value) score += ++streak; else streak = 0;
            }else{
                if(value) score ++;         
            }
        }
    };  
    return score;
}

coord_t theTrapFighter(dna_t d, view_t v) {
    // Definition of storageMethod: 0=decimal, 1=streak, 2=bitsum
    const int colorMemStorageMethod = 1, colorMemBlockSize = 3;
    const int trapMemStorageMethod = 0, trapMemBlockSize = 3;
    const int trapMemTopThreshold = 4, nDirBins = 4, nColorBins = 4;

    int xBestScore=0, yBestScore=0;
    long bestScore=-1, weightMove, weightMove2, mainScore;
  for(int x = -1; x <= 1; x++) {
        if (x < 0) weightMove = 1000; // moving backward
        if (x== 0) weightMove = 10000; //moving vertical
        if (x > 0) weightMove = 100000; //moving forward
        for(int y = -1; y <= 1; y++) {          
            int color = v(x, y);
            if(color == OUT_OF_BOUNDS || (x==0&&y==0) ) continue;
            mainScore = dnaLookup2(d,color*colorMemBlockSize,
             colorMemBlockSize,colorMemStorageMethod);
            if (mainScore==0) {
                //not a suitable move because too low score
            }else{
                mainScore*= weightMove;
                //lookup trap likelihood
                int directionBin = 0;
                if (nDirBins==3) directionBin = x>0?y+1:-1;
                if (nDirBins==4) directionBin = x>0?y+2:0;
                // put 16 colors in nColorBins bins
                int colorBin = v(0,0)*nColorBins/N_COLORS; 
                colorBin = colorBin>(nColorBins-1)?(nColorBins-1):colorBin;
                if (directionBin >= 0 &&
                 dnaLookup2(
                   d,
                   colorMemBlockSize*16
                    +trapMemBlockSize*(nColorBins*directionBin+colorBin),
                   trapMemBlockSize,
                   trapMemStorageMethod
                 ) >=trapMemTopThreshold){
                  //suspect a trap therefore no sub score is added                  
                 }else{
                    // when equal score, use sub score by examining 5x5 box to rank moves
                    for(int x2 = x-1; x2 <= x+1; x2++){     
                        if (x2 < x) weightMove2 = 1; // moving backward
                        if (x2== x) weightMove2 = 10; //moving vertical
                        if (x2 > x) weightMove2 = 100; //moving forward
                        for(int y2 = x-1; y2 <= y+1; y2++){     
                            int color2 = v(x2, y2);
                            if(color2 != OUT_OF_BOUNDS){
                                mainScore+=weightMove2 * dnaLookup2(d,color2*colorMemBlockSize,
                                 colorMemBlockSize,colorMemStorageMethod);
                            }
                        }
                    }               
                 }
            }
            if(mainScore > bestScore) {
                bestScore = mainScore;              
                xBestScore = x;
                yBestScore = y;
            }
        }
    }
    return{xBestScore,yBestScore};
}

Оценка: 580 (2K пробежек)

Первые 50 очков (13 игр набрали только 1 очко):

28044 14189 1 2265670 2275942 3 122769 109183 401366 61 643 205949 47563 138680 1 107199 85666 31 2 29 1 89519 22 100908 14794 1 3198300 21 601 14 3405278 1 1 1 2 74167 1 семьдесят одна тысяча двести сорок-две 97331 201080 1 1 102 94444 2734 82948 1 4 19906 1 1 255159

Неправильное предположение о стене многократно повторяется дебилами на новорожденных:

Некоторые виды ошибочно полагают, что стены хороши, и пытаются все время двигаться к ним и поэтому застревают перед стенами. Они также могут застрять в бесконечных петлях телепортов. Эффект одинаков в обоих случаях.

Основная проблема заключается в том, что после нескольких сотен итераций некоторые гены становятся очень доминирующими . Если это «правильные» гены, вы можете получить очень высокие оценки (> 1 млн. Баллов). Если это не так, вы застряли, так как вам нужно разнообразие, чтобы найти «правильные» гены.

Борьба дебилов: Решение 1: инверсия цвета

Первое решение, которое я попробовал, было попыткой использовать часть неиспользуемой памяти, которая все еще очень разнообразна. Предположим, вы выделили 84 бита в свою цветовую память и память поиска ловушек. Остальные 16 бит будут очень разнообразными. Мы можем заполнить 2 десятичные8 переменных, которые имеют значения в интервале [0,255], и они являются однородными, что означает, что каждое значение имеет вероятность 1/256. Будут называться переменные inInverseи inReverse.

Если inInverseравно 255 (шанс 1/256), то мы будем инвертировать интерпретацию цветовых показателей . Таким образом, стена, которую придурок считает безопасной из-за высокой оценки, получит низкую оценку и, следовательно, станет плохим ходом. Недостатком является то, что это также повлияет на гены «прав», поэтому у нас будет меньше очень высоких результатов. Кроме того, этот inInverseвид должен будет размножаться сам, и его дети также получат части доминирующей ДНК. Наиболее важной частью является то, что он возвращает разнообразие.

Если inReverseравно 255 (шанс 1/256), то мы изменим порядок хранения позиций цветовых показателей . Поэтому до того, как цвет 0 был сохранен в битах 0-3. Теперь цвет 15 будет сохранен в этой позиции. Разница с inInverseподходом заключается в том, что inReverseволя отменит проделанную до сих пор работу. Мы вернулись на круги своя. Мы создали вид, который имеет схожие гены, как и в начале игры (за исключением памяти для поиска ловушек)

С помощью оптимизации опробовано , если разумно использовать inInverseи inReverseв то же время. После оптимизации был сделан вывод, что оценка не увеличилась. Проблема в том, что у нас более разнообразная популяция генов, но это также влияет на «правильную ДНК». Нам нужно другое решение.

Борьба дебилов: Решение 2: хэш-код

У вида есть 15 возможных стартовых позиций, и в настоящее время существует слишком большой шанс, что он пойдет точно по тому же пути, если он стартует в той же стартовой позиции. Если он придурок, который любит стены, он будет снова и снова застревать на одной и той же стене. Если ему, по счастливой случайности, удастся достичь далеко впереди стены, он начнет доминировать в пуле ДНК со своими ошибочными предположениями. Нам нужно, чтобы его потомки пошли по несколько иному пути (поскольку для него все равно слишком поздно) и не застрянут на дальней стене, а на более близкой стене . Это может быть достигнуто путем введения хеш-кода .

Хэш - код должен иметь цель , чтобы однозначно идентифицировать и маркировать текущее положение на доске. Цель не в том, чтобы узнать, какова позиция (x, y), а в том, чтобы ответить на вопросы, были ли мои предки раньше в этом месте?

Предположим, у вас перед вами будет полная доска, и вы будете делать jpg каждого возможного квадрата 5 на 5 ячеек. В итоге вы получите (53-5) x (15-5) = 380 изображений. Давайте дадим этим изображениям номера от 1 до 380. Наш хэш-код должен рассматриваться как такой идентификатор, с той разницей, что он не работает от 1 до 330, но в нем отсутствует IDS, например, 563, 3424, 9424, 21245 и т. Д.

unsigned long hashCode=17;
for(int x = -2; x <= 2; x++) {
    for(int y = -2; y <= 2; y++) {
        int color = v(x, y)+2;
        hashCode = hashCode*31+color;
    }
}       

Простые числа 17и 31находятся там, чтобы предотвратить исчезновение информации, добавленной в начале цикла. Позже подробнее о том, как интегрировать наш хэш-код в остальную часть программы.

Заменим механизм подсчета «посмотри, что выходит» на другой механизм подсчета. Когда две или три ячейки имеют одинаковые основные оценки, вероятность того, что верхняя будет выбрана, будет равна 50%, вероятности выбора нижних ячеек - 50%, а средней - 0%. Шанс будет определяться не случайным генератором, а битами из памяти , поскольку таким образом мы гарантируем, что в той же ситуации будет сделан тот же выбор.

В идеальном мире (где у нас бесконечное количество памяти), мы вычислим уникальный хэш-код для нашей текущей ситуации, например, 25881, и перейдем в ячейку памяти 25881 и прочитаем там, если мы должны выбрать верхнюю или нижнюю ячейку (когда там равный счет). Таким образом, мы окажемся в точно такой же ситуации (когда мы, например, переместимся на доску во второй раз и начнем с одной и той же позиции), примем одинаковые решения. Поскольку у нас нет бесконечной памяти, мы будем применять модуль хеш-кода по модулю размера доступной памяти . Текущий хэш-код хорош в том смысле, что распределение после операции по модулю является однородным.

Когда потомок путешествует по той же доске со слегка измененной ДНК, он в большинстве случаев (> 99%) принимает точно такое же решение. Но чем дальше он продвигается, тем больше вероятность того, что его путь будет отличаться от его предков. Так что вероятность того, что он застрянет на этой далеко идущей стене, невелика. Хотя застрять на той же ближайшей стене, что и его предок, относительно велика, но это не так уж и плохо, так как он не будет производить много потомства. Без подхода с использованием хэш-кода вероятность застрять на ближайшей и удаленной стене практически одинакова

оптимизация

После оптимизации был сделан вывод, что таблица идентификации ловушек не нужна и достаточно 2 бит на цвет. Остальная часть памяти 100-2х16 = 68 бит используется для хранения хеш-кода. Кажется, что механизм хеш-кода способен избежать ловушек.

Я оптимизировал для 15 параметров. Этот код включает лучший набор параметров (пока что):

int dnaLookup(dna_t &d, int start, int chunklen, int storageMethod,int inInverse) {
    // Definition of storageMethod: 0=decimal, 1=streak, 2=bitsum
    int score = 0;
    int streak = 0;
    for(int i = start; i < start+chunklen; i++) {
        int value = d[i];
        if (inInverse) value = (1-d[i]);            
        if (storageMethod==0) {
            score = (score << 1) |value;
        }else{
            if (storageMethod==1){
                if(value) score += ++streak; else streak = 0;
            }else{
                if(value) score ++;         
            }
        }
    };  
    return score;
}

coord_t theFittest(dna_t d, view_t v) {
    // Definition of storageMethod: 0=decimal, 1=streak, 2=bitsum
    const int colorMemStorageMethod = 2, colorMemBlockSize = 2, colorMemZeroThreshold = 0;
    const int useTrapMem = 0, trapMemStorageMethod = -1, trapMemBlockSize = -1;
    const int trapMemTopThreshold = -1, nDirBins = -1, nColorBins = -1;
    const int reorderMemStorageMethod = -1, reorderMemReverseThreshold = -1;
    const int reorderMemInverseThreshold = -1;
    // Definition of hashPrority: -1: no hash, 0:hash when 'look beyond' scores equal,
    // 1: hash replaces 'look beyond', 2: hash replaces 'trap finder' and 'look beyond'
    // 3: hash replaces everything ('color finder', 'trap finder' and 'look beyond')
    const int hashPrority = 2;
    int inReverse = reorderMemReverseThreshold != -1 && 
     (dnaLookup(d,92,8,reorderMemStorageMethod,0) >= reorderMemReverseThreshold);
    int inInverse = reorderMemInverseThreshold != -1 && 
     (dnaLookup(d,84,8,reorderMemStorageMethod,0) >= reorderMemInverseThreshold);
    int trapMemStart=N_COLORS*colorMemBlockSize;
    unsigned long hashCode=17;
    int moveUp=0;
    if (hashPrority>0){
        for(int x = -2; x <= 2; x++) {
            for(int y = -2; y <= 2; y++) {
                int color = v(x, y)+2;
                hashCode = hashCode*31+color;
            }
        }       
        unsigned long hashMemStart=N_COLORS*colorMemBlockSize;
        if (useTrapMem==1 && hashPrority<=1) hashMemStart+=nDirBins*nColorBins*trapMemBlockSize;
        if (hashPrority==3) hashMemStart=0;
        int hashMemPos = hashCode % (DNA_BITS-hashMemStart);
        moveUp = dnaLookup(d,hashMemStart+hashMemPos,1,0,inInverse);
    }

    int xBestScore=0, yBestScore=0;
    long bestScore=-1, weightMove, weightMove2, mainScore;
    for(int x = -1; x <= 1; x++) {
        if (x < 0) weightMove = 1000; // moving backward
        if (x== 0) weightMove = 10000; //moving vertical
        if (x > 0) weightMove = 100000; //moving forward
        for(int y = -1; y <= 1; y++) {          
            int color = v(x, y);
            if (inReverse) color = 15-v(x, y);
            if(color == OUT_OF_BOUNDS || (x==0&&y==0) ) continue;
            //when MoveUp=1 -> give move with highest y most points (hashScore=highest)
            //when MoveUp=0 -> give move with lowest y most points (hashScore=lowest)
            int hashScore = (y+2)*(2*moveUp-1)+4; 
            mainScore = dnaLookup(
              d,
              color*colorMemBlockSize,
              colorMemBlockSize,
              colorMemStorageMethod,
              inInverse
             );
            if (mainScore<colorMemZeroThreshold+1) {
                mainScore =  0; //not a suitable move because too low score
            }else{
                mainScore*= weightMove;
                //lookup trap likelihood
                int directionBin = 0;
                if (nDirBins==3) directionBin = x>0?y+1:-1;
                if (nDirBins==4) directionBin = x>0?y+2:0;
                // put 16 colors in nColorBins bins
                int colorBin = v(0,0)*nColorBins/N_COLORS; 
                if (inReverse) colorBin = (15-v(0,0))*nColorBins/N_COLORS; 
                colorBin = colorBin>(nColorBins-1)?(nColorBins-1):colorBin;
                if (useTrapMem && directionBin >= 0 &&
                 dnaLookup(
                   d,
                   trapMemStart+trapMemBlockSize*(nColorBins*directionBin+colorBin),
                   trapMemBlockSize,
                   trapMemStorageMethod,
                   0
                 )>=trapMemTopThreshold){
                  //suspect a trap therefore no sub score is added                  
                 }else{
                    if (hashPrority>=1){
                        mainScore+=hashScore;
                    } else{
                        // when equal score, use sub score by examining 5x5 box to rank moves
                        for(int x2 = x-1; x2 <= x+1; x2++){     
                            if (x2 < x) weightMove2 = 1; // moving backward
                            if (x2== x) weightMove2 = 10; //moving vertical
                            if (x2 > x) weightMove2 = 100; //moving forward
                            for(int y2 = x-1; y2 <= y+1; y2++){     
                                int color2 = v(x2, y2);
                                if (inReverse) color2 = 15-v(x2, y2);
                                if(color2 != OUT_OF_BOUNDS){
                                    long mainSubScore = dnaLookup(
                                      d,
                                      color2*colorMemBlockSize,
                                      colorMemBlockSize,
                                      colorMemStorageMethod,
                                      inInverse
                                    );
                                    if (mainSubScore>=colorMemZeroThreshold+1){
                                        mainScore+=mainSubScore*weightMove2;
                                    }
                                }
                            }
                        }
                    }               
                 }
            }
            if (hashPrority==2 || (useTrapMem<=0 && hashPrority>=1)) mainScore+=hashScore*10;
            if (hashPrority==3) mainScore=hashScore*weightMove;         

            if(mainScore > bestScore) {
                bestScore = mainScore;              
                xBestScore = x;
                yBestScore = y;
            }
        }
    }
    return{xBestScore,yBestScore};
}   

Оценка: 922 (2K пробежек)

Первые 50 очков (9 игр набрали только 1 очко):

112747 3 1 1876965 8 57 214921 218707 2512937 114389 336941 1 6915 2 219471 74289 31 116 133162 1 5 633066 166473 515204 1 86744 17360 2 190697 1 6 122 126399 399045 1 4172168 1 169119 4990 77432 236669 3 30542 1 6 2398027 5 1 2 8

Это моя самая первая программа на C ++. У меня, как и у большинства из вас, сейчас опыт в гномическом анализе. Я хочу поблагодарить организаторов, так как мне очень понравилось работать над этим.

Если у вас есть какие-либо отзывы, пожалуйста, оставьте комментарий ниже. Извиняюсь за длинные тексты.


Я нахожу ваш анализ ловушек довольно интересным.

Вы пробовали другую хеш-функцию, например, ксерокопирование 25 значений цвета, рассматриваемых как 12,5 16-битных слов, и получение по модулю? Я не уверен, что сравнение простых чисел дает лучшую однородность, но я не большой математик.

Кроме того, вы рассматривали возможность добавления алгоритма маршрутизации? Кажется, это огромный фактор улучшения независимо от генома, поскольку он ограничит ходы теми, которые проверяют способность генома только по тем путям, которые с гораздо большей вероятностью приведут к выигрышной позиции.

Курои, спасибо за ваш отзыв. Я не пробовал xoring, так как я не очень знаком с бинарными операциями в c ++. Я полагаю, вы имеете в виду 12,5 8-битных слов? Вы используете ксоринг?
Руут

Вы можете посмотреть на мой код "недоверчивых", чтобы увидеть, какую хеш-функцию я использую. По сути, я пропускаю ячейки вне дорожки и рассматриваю цвета на дорожке как старшие и младшие части 16-битного слова. Все эти слова накапливаются с XOR в регистре, который затем делится на размер хеш-таблицы. Поскольку максимальное значение хеша (65535) намного превышает размер таблицы (<100), модуль имеет хорошую мощность распространения. Я протестировал его на широком наборе случайно сгенерированных сеток, и он, кажется, имеет хорошую однородность.

6

Pathfinder, C ++, предварительная оценка 35,8504 (50 раундов)

Капитальный ремонт! Я перенес свой алгоритм на C ++ и немного подправил его, но результат все еще не очень высок, вероятно потому, что крысы продолжают биться головой о стены. Я устал от попыток улучшить это, поэтому я просто позволю этому быть сейчас.


int dnarange(dna_t &d, int start, int len) {
    int res = 0;
    for(int i = start; i < start+len; i++) {
        res = (res << 1) | d[i];
    }
    return res;
}

int dnasum(dna_t &d, int start, int len) {
    int res = 0;
    for(int i = start; i < start+len; i++) {
        res += d[i];
    }
    return res;
}

int dnaweight(dna_t &d, int start) {
    return d[start] + d[start+1] + 2*d[start+2] + 2*d[start+3] + 3*d[start+4];
}

int trap_d [16] = {1,0,1,1,0,1,-1,1,-1,0,-1,-1,0,-1,1,-1}; //immutable
int nhood [10] = {1,0,1,1,1,-1,0,1,0,-1}; //immutable

coord_t pathfinder(dna_t d, view_t v) {
  int is_trap[16] = {0};
  int pos_or_weight[16] = {0};
  int u_weight = dnaweight(d, 80);
  for (int i = 0; i < 16; i++) {
    int status = dnarange(d, 5*i, 2);
    if (status == 1) {
      is_trap[i] = 1;
      pos_or_weight[i] = dnarange(d, 5*i + 2, 3);
    } else {
      pos_or_weight[i] = dnaweight(d, 5*i);
    }
  }
  int w_area[7][4] = {0};
  for (int j = 0; j < 7; j++) {
    w_area[j][3] = u_weight;
  }
  for (int i = 0; i < 3; i++) {
    w_area[0][i] = u_weight;
    w_area[6][i] = u_weight;
  }
  int d_coeff = dnaweight(d, 85);
  for (int i = 0; i < 3; i++) {
    for (int j = 1; j < 6; j++) {
      int p_or_w, color = v(i, j-3);
      if (color != OUT_OF_BOUNDS) {
    p_or_w = pos_or_weight[color];
      } else {
    p_or_w = 1000;
      }
      if (color != OUT_OF_BOUNDS && is_trap[color] && i+trap_d[2*p_or_w] >= 0) {
    w_area[j + trap_d[2*p_or_w + 1]][i + trap_d[2*p_or_w]] += d_coeff;
      } else {
    w_area[j][i] += p_or_w;
      }
    }
  }
  for (int i = 3; i >= 0; i--) {
    for (int j = 0; j < 7; j++) {
      int min_w = 1000;
      for (int k = std::max(0, j-1); k <= std::min(6, j+1); k++) {
    min_w = std::min(min_w, w_area[k][i + 1]);
      }
      w_area[j][i] += min_w;
    }
  }
  int speed = dnasum(d, 90, 5);
  w_area[2][0] += 2 + speed;
  w_area[4][0] += 2 + speed;
  int goal = dnaweight(d, 95);
  int min_w = 10000;
  int sec_w = 10000;
  int min_x, min_y, sec_x, sec_y, w;
  for (int i = 0; i < 5; i++) {
    w = w_area[nhood[2*i + 1] + 3][nhood[2*i]];
    if (w < min_w) {
      sec_w = min_w;
      sec_x = min_x;
      sec_y = min_y;
      min_w = w;
      min_x = nhood[2*i];
      min_y = nhood[2*i + 1];
    } else if (w < sec_w) {
      sec_w = w;
      sec_x = nhood[2*i];
      sec_y = nhood[2*i + 1];
    }
  }
  if (min_w > goal) {
    int r = v.rng.rint(5);
    return {nhood[2*r], nhood[2*r+1]};
  } else if (sec_w <= goal && v.rng.rint(100) < 2*speed) {
    return {sec_x, sec_y};
  }
  return {min_x, min_y};
}

объяснение

Общая идея состоит в том, чтобы классифицировать каждый цвет как ловушку или нет, затем назначать направления для ловушек и веса для не-ловушек и пытаться следовать по пути минимального веса к правой границе сетки видения.

В первых 80 битах генома каждый цвет классифицируется с использованием 5 битов abcde. Если ab = 01цвет является ловушкой, и cdeкодирует его направление (восемь возможностей). Если ab ≠ 01цвет не ловушка, а его вес a + b + 2*(c + d + e).

Затем мы инициализируем сетку 3х7, которая представляет поле зрения крысы справа, дополненное «неизвестными» цветами. Биты 80-84 кодируют вес неизвестных ячеек аналогично цветам без ловушек, а биты 85-89 кодируют общий вес для ловушек. Мы заполняем сетку весами, вычисляем кратчайшие пути и добавляем некоторый дополнительный вес (закодированный в битах 90-95) в ячейки непосредственно над и под крысой, чтобы препятствовать обходу. Биты 95-99 кодируют целевой вес, Если минимальный вес пути ниже его, крыса, вероятно, где-то застряла и продолжает двигаться случайным образом (но никогда не возвращается). В противном случае следует минимальный весовой путь. С небольшой вероятностью, зависящей от веса, предотвращающего боковые шаги, крыса вместо этого выбирает путь веса со вторым минимальным весом. Это сделано для того, чтобы не застрять на стенах (но сейчас это не очень хорошо работает).


Запустил вашу реализацию на моем компьютере. Прошло несколько часов. Он получает средний балл 7,848433940863856 баллов. pastebin.com/d50GcwnK
Якуб

@Jakube Большое спасибо! Это намного хуже, чем я ожидал, но теперь, когда я снова смотрю на код, я вижу несколько ошибок и других странностей. Я постараюсь перенести это на C ++ позже, чтобы я мог проанализировать это сам.
Згарб

5

LookAheadPlayer C ++ ≈ 89.904

Моей первоначальной мыслью было найти 4 бита, которые соответствуют цвету, который я искал, и использовать следующие несколько бит в качестве оценки. Это оказалось ужасной идеей, вероятно, из-за мутаций.

Поэтому я подумал о способах защиты от мутаций и кроссоверов, и это напомнило мне о работе, которую я проделал над декодированием QR-кода. В QR-кодах данные разбиваются на блоки и чередуются, чтобы избежать ошибок, связанных с уничтожением слишком большой части данных.

Поэтому, как и ColorScorePlayer, я разрезал ДНК на 16 кусков и использовал их в качестве заданного значения. Однако оценки чередуются так, что отдельные биты каждой оценки не являются смежными. Затем я суммирую количество текущих возможных ходов и следующих возможных ходов и выбираю лучший ход, который нужно сделать.

Примечание: это было закодировано / протестировано на MinGW. Он не будет компилироваться с оптимизацией или с многопоточностью. У меня нет реальной установки Linux или Visual Studio, удобной для использования компилятора, где они будут работать. Завтра утром я собираюсь быстро его протестировать, но, пожалуйста, дайте мне знать, если у вас возникнут какие-либо проблемы.

// get striped color score, 6 bits per color. should be
// resistant to getting erased by a crossover
void mapColorsBitwise(dna_t &d, int* color_array) {
    for (int i=0; i<N_COLORS; i++) {
        int score = 0;
        for (int j=0; j<6; j++) {
            score = (score<<1) | d[ j*N_COLORS + i ];
        }
        color_array[i] = score;
    }
}

// label for the lookup tables
enum direction_lut {
    UP_RIGHT=0, RIGHT, DOWN_RIGHT
};

// movement coord_t's to correspond to a direction
static const coord_t direction_lut[3] = {
    { 1, -1 }, { 1, 0 }, { 1, 1 }
};

// indexes into the arrays to denote what should be summed
// for each direction.
static const int sum_lut[3][6] = {
    { 3, 4, 8, 8, 9, 14 }, { 9, 13, 13, 14, 14, 19 },
    { 14, 18, 18, 19, 23, 24 }
};

coord_t lookAheadPlayer(dna_t d, view_t v) {
    int scoreArray[25] = { 0 };
    int colorScores[N_COLORS] = { };

    // Get color mapping for this iteration
    mapColorsBitwise(d, colorScores);

    for (int y=-2; y<=2; y++) {
        for (int x=0; x<=2; x++) {
            // Get the scores for our whole field of view
            color_t color = v(x,y);
            if (color != OUT_OF_BOUNDS)
                scoreArray[ (x+2)+((y+2)*5) ] += colorScores[color];
        }
    }

    // get the best move by summing all of the array indices for a particular
    // direction
    int best = RIGHT;
    int bestScore = 0;
    for (int dir=UP_RIGHT; dir<=DOWN_RIGHT; dir++) {
        if (v(direction_lut[dir].x, direction_lut[dir].y) == OUT_OF_BOUNDS)
            continue;

        int score = 0;
        for (int i=0; i<6; i++) {
            score += scoreArray[ sum_lut[dir][i] ];
        }

        if (score > bestScore) {
            bestScore = score;
            best = dir;
        }
    }

    return direction_lut[best];
}

5

SlowAndSteady C ++ (оценка 9,7)

Мы не можем полагаться на интерпретацию фрагментов генома как чисел, потому что один переворот может иметь радикально разные эффекты в зависимости от его положения. Вот почему я просто использую 16 6-битных сегментов и оцениваю их по количеству 1s. Первоначально 111111было хорошо и 000000было плохо, и хотя это не имеет значения в долгосрочной перспективе (после того, как геном полностью эволюционировал), в начальной конфигурации ДНК большинство сегментов имеют 2-4, поэтому я перешел на использование 9 - (#1 - 3)^2для подсчета, это обеспечивает гораздо большую свободу передвижения в первых раундах и ускоряет эволюцию.

Прямо сейчас я смотрю только на 7 ближайших соседей, добавляю смещение направления к цветовой шкале и двигаюсь в одном из самых высоких направлений случайным образом.

Хотя сам счет не очень высок, мои твари достигают финиша и набирают> 1 в 3/4 случаев.

coord_t SlowAndSteadyPlayer(dna_t d, view_t v) {
    const int chunklen = 6;
    int color_scores[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
    for(int i=0; i<16; i++){ //count ones
        for(int j=0; j<chunklen; j++){
            color_scores[i] += d[i*chunklen + j];
        }
    }

    int moves[7][2] = {
        {-1,1}, {0,1}, {1,1},
                       {1,0},
        {-1,-1},{1,-1},{-1,-1}
    };
    int movescores[7];
    int smax = -1;
    int nmax = 0;
    int best_moves[7];
    for(int m=0; m<7; m++){ //compute the score for each move
        int temp_color = v(moves[m][0], moves[m][1]);
        if(temp_color == OUT_OF_BOUNDS){
            movescores[m] = 0;
            continue;
        }
        int dir_bias[3] = {1,3,6};
        int temp_score = 9-(color_scores[temp_color]-3)*(color_scores[temp_color]-3) + dir_bias[moves[m][0]+1];
        movescores[m] = temp_score;

        if(temp_score > smax) {
            smax = temp_score;
            nmax = 0;
        }
        if(temp_score == smax) best_moves[nmax++] = m;
    }

    int best_chosen = v.rng.rint(nmax);
    return {moves[best_moves[best_chosen]][0], moves[best_moves[best_chosen]][1]};
}

И пример выигрыша на 100 досках

Scores: 5 4 13028 1 1 101 2 24 1 21 1 4 2 44 1 1 24 8 2 5 1 13 10 71 2 19528 6 1 69 74587 1 1 3 138 8 4 1 1 17 23 1 2 2 50 7 7 710 6 231 1 4 3 263 4 1 6 7 20 24 11 1 25 1 63 14 1 2 2 1 27 9 7 1 7 31 20 2 17 8 176 3 1 10 13 3 142 1 9 768 64 6837 49 1 9 3 15 32 10 42 8

Средний геометрический балл: 9,76557


Используется ли указанная вами оценка для одной доски с использованием стандартной частоты мутаций или вашего скорректированного значения?
Трихоплакс

«Мои твари достигают финиша и набирают> 1 в 3/4 случаев». Я желаю, чтобы метрика выигрышей была вознаграждена
Sparr

5

WeightChooser | C # | Результаты: 220,8262 в 1520 играх

Вычисляет вес для возможного следующего хода (синий) на основе среднего веса возможных последующих ходов (желтый)

using ppcggacscontroller;
using System.Linq;
using System;

public class WeightChooser
{
    public static ppcggacscontroller.Program.Coord[] cspcoords = new[] {
            new Program.Coord(1, -1),
            new Program.Coord(1, 0),
            new Program.Coord(1, 1),
        };

    const int dnaBits = 4;

    public static void move(GameLogic.IView v, GameLogic.IGenome g, Random rnd, out int ox, out int oy)
    {
        var gcrds = cspcoords.Where(c => viewVal(v, c) > -1)
            .OrderByDescending(p => getBitsSet(g, viewVal(v, p)))
            .ThenByDescending(gt => weight(v, g, gt));

        Program.Coord nextMove = gcrds.First();
        ox = nextMove.x;
        oy = nextMove.y;
    }

    private static uint getBitsSet(GameLogic.IGenome g, int vVal)
    {
        uint i = g.cutOutInt(dnaBits * vVal, dnaBits);
        i = i - ((i >> 1) & 0x55555555);
        i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
        return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
    }

    private static int viewVal(GameLogic.IView v, Program.Coord c)
    {
        return v[c.x, c.y];
    }

    private static double weight(GameLogic.IView v, GameLogic.IGenome g, Program.Coord toGo)
    {
        double w = 0;

        int y = (toGo.y + v.yd) - 1;
        int i = 0;
        for (; i <= 2; i++)
        {
            int field = v[toGo.x + 1, (y + i) - v.yd];
            if (field > -1)
                w += getBitsSet(g, field);
        }

        return w / i;
    }
}

Scores: 32, 56103, 1361, 3351446, 33027, 23618, 22481, 1172713, 1, 3, 1, 1, 1, 2 88584, 106357, 1, 1232, 1, 1651280, 16690, 1, 1, 23732, 207554, 53, 69424, 1, 1,  79361, 1, 1, 51813, 229624, 25099, 2, 1, 234239, 362531, 1, 1, 19, 7295, 1, 7, 2, 196672, 1654208, 73453, 1, 23082, 1, 8, 5, 1685018, 4, 20, 1, 1, 1, 1, 1, 144 671, 122309, 10, 94752, 100895, 1, 54787, 54315, 252911, 79277, 1159, 241927, 94 347, 1, 318372, 37793, 1, 1, 1345310, 18934, 169700, 1, 1, 3, 186740, 83018, 121 758, 1, 358, 1935741, 88, 1, 1, 1, 1, 7, 21, 51144, 2, 1, 267638, 1, 1, 3, 1, 1,  1, 1, 674080, 47211, 8879, 7, 222766, 67214, 2, 89, 21038, 178463, 92846, 3, 14 0836, 1, 1, 111927, 1, 92165, 1, 192394, 1, 1, 2563722, 1, 42648, 1, 16, 1, 1, 2 85665, 1, 212653, 1, 4, 20513, 3, 135118, 13161, 2, 57, 78355, 3, 3, 44674, 8, 1 , 226472, 1, 1, 31588, 19619, 1, 2931870, 60814, 1, 1, 33867, 60740, 20558, 1, 1 5, 3, 5, 1, 1, 1, 60737, 450636, 468362, 1, 1, 347193, 91248, 551642, 1, 427215,  1, 57859, 17, 15, 66577, 24192, 1, 63560, 6568, 40279, 68216, 23098, 180732, 1,  1, 3041253, 1, 253488, 60535, 1, 1, 150838, 7361, 72855, 290699, 104644, 1, 763 01, 378, 1, 89220, 1, 262257, 2, 2, 1, 117, 105478, 33, 1, 65210, 1, 117588, 1, 1, 24320, 12, 3714568, 81152, 1, 1, 10125, 2, 1, 22, 1, 45201, 1, 1, 10518, 1, 1 , 1, 1, 34, 210021, 1, 1, 1, 65641, 6, 72, 1, 7, 2, 161578, 1, 1, 38378, 1, 4113 741, 1, 34450, 244212, 127660, 1, 256885, 46, 2, 1, 1, 103532, 1, 503965, 114774 , 52450, 124165, 73476, 50250, 1, 3, 3755352, 24928, 1, 1, 51, 11, 1, 210580, 1,  62375, 1, 1, 92745, 341232, 167675, 86, 242, 293710, 454841, 1, 49840, 4456758,  121378, 145323, 74904, 5048, 25459, 1, 57, 116999, 1, 1, 76074, 111447, 95706, 1, 1, 52631, 166756, 2159474, 161216, 1, 2, 3, 11904, 1, 22050, 6, 1, 1, 1, 41, 48908, 6, 80878, 28125, 28, 160516, 1, 4, 1, 8, 1, 1, 7, 362724, 1, 397193, 1, 2 5, 1, 59926, 3, 74548, 2320284, 470189, 1, 108, 1, 1, 16, 1, 496013, 1, 1, 1, 1,  107758, 1, 284144, 146728, 1, 70769, 94215, 1, 1, 9961, 97300, 7, 1, 76263, 1, 27, 294046, 40, 8, 2, 1, 57796, 2, 79800, 1043488, 470547, 1, 1, 1, 6, 69666, 8,  1, 1, 344011, 205325, 3963186, 1141527, 61598, 446029, 1, 1, 1, 1, 625247, 1877 92, 136391, 1, 72519, 1, 141168, 412, 98491, 103995, 297052, 1, 1, 1, 1, 3, 17, 9, 62899, 5, 47810, 254, 26789, 2, 1, 1, 3, 10361, 19615, 40430, 17288, 3, 71831 , 41374, 1, 91317, 409526, 1, 184305, 1, 192552, 3, 3587674, 39, 13, 134500, 41,  42, 672, 559835, 9, 39004, 51452, 1, 1, 12293, 11544, 265766, 8590, 1, 8632, 1,  1, 61849, 35155, 1, 74798, 72773, 1, 89, 37, 4, 4405882, 1, 99, 44397, 5, 4, 6,  1, 1, 1, 515818, 78383, 20, 127829, 1824801, 157, 1, 1, 268561, 19, 2, 230922, 1, 103, 98146, 5029789, 304324, 1, 5, 60516, 1, 139, 28982, 7, 20755, 187083, 1,  1, 143811, 37697, 1, 1, 269819, 83, 1, 202860, 13793, 16438, 113432, 1, 1, 2, 5 134384, 29, 84135, 39035, 2, 125, 1, 30, 129771, 41982, 13548, 61, 1, 2, 1, 82, 102, 2, 105581, 210399, 291204, 3012324, 1, 84763, 1, 1, 442067, 2, 1, 1, 1, 116 , 1, 3, 3, 56, 208807, 1, 2, 1, 14, 29, 31286, 1, 1, 162358, 28856, 46898, 1, 16 2698, 1, 1, 1, 65, 1, 1, 234566, 6, 1, 1, 128, 124, 2167692, 181946, 29, 1, 1, 1 , 1, 17, 162550, 179588, 4, 226480, 28, 1, 158512, 35084, 1, 26160, 17566, 1, 81 826, 2, 33, 1, 1, 11, 1, 230113, 1, 1, 1, 24405, 17, 1, 2, 1, 162365, 2, 1, 1, 8 5225, 1, 15016, 51509, 1, 5, 1, 93, 13, 59, 24548, 1, 3, 2, 2, 1, 64424, 1, 1, 4 , 1, 1, 1, 2, 267115, 139478, 52653, 96225, 1, 1, 35768, 3, 1, 1, 3280017, 8, 80 014, 43095, 112102, 1, 1, 1, 79594, 5, 1, 1, 4, 455714, 19, 15, 1, 233760, 55850 5, 2, 2, 1, 63672, 1, 3732951, 1, 135858, 134256, 452456, 151573, 79057, 638215,  88820, 1, 1, 76517, 13, 314006, 5, 1, 17704, 1, 79589, 1, 18371, 530793, 59020,  1, 1, 1, 4, 1, 1, 1, 71735, 1, 1, 1, 1, 1, 37894, 1, 2, 24054, 1, 8, 26471, 34,  1, 48033, 5, 3, 1, 25, 101, 1, 1, 5, 1, 1, 1, 97521, 1, 682817, 286486, 5, 1472 4, 1, 7805226, 6, 1, 1, 1, 7, 2, 1, 1, 1, 25, 233330, 1, 20899, 3417337, 92793, 23, 80821, 1, 1, 115948, 264191, 3, 79809, 1, 2, 59531, 2, 1, 1, 28684, 97, 1, 2 69433, 98769, 1, 76608, 138124, 1, 1, 325554, 122567, 1, 1, 3, 689604, 4, 85823,  66911, 138091, 169416, 21430, 1, 2, 486654, 108446, 93072, 1, 67907, 4, 1, 1, 5 2260, 67867, 210496, 25157, 1, 1, 1, 5477, 2, 2, 11907, 106, 48404, 1, 1, 1, 787 11, 190304, 112025, 1, 9313, 143055, 40189, 315537, 157581, 70714, 6, 180600, 38 594, 103658, 59444, 7, 31575, 1, 1, 581388, 370430, 1, 114446, 1, 1, 2, 3968, 1,  1, 1, 1, 1, 4523411, 1, 1, 270442, 1, 59, 235631, 3, 110196, 9, 1, 93724, 1, 22 917, 1, 6, 1, 2350266, 1, 1, 20, 4686858, 31, 1, 240180, 10, 470592, 3, 61051, 1 45372, 2831, 64052, 10, 120652, 255971, 479239, 1, 387659, 1, 1, 1, 378379, 7, 3 3218, 55914, 1, 1, 1667456, 6, 2, 74428, 3, 2, 1, 121582, 121274, 19651, 59899, 1, 11, 406670, 137835, 100269, 2, 164361, 98762, 44311, 25817, 178053, 31576, 1,  8, 2539307, 121430, 1, 41001, 1, 4, 1, 116258, 91101, 1, 126857, 1, 8, 49503, 1 , 489979, 12, 500332, 1, 52, 4, 8786, 4, 4878652, 12354, 27480, 89115, 87560, 11 793, 5, 1, 4702325, 301188, 1, 1, 1, 1, 1, 416520, 49357, 230103, 24497, 1, 3, 2 , 57366, 183021, 1, 1, 1, 1, 1, 2, 2, 2546229, 1, 2, 38665, 1, 6903, 1, 89519, 9 5119, 64879, 1, 1, 160380, 474336, 3107, 1, 7, 29099, 28667, 3, 196933, 35979, 1 2924, 7, 1, 99885, 6, 1, 1, 1, 7, 1, 1, 1, 1, 65727, 1, 1, 1, 1, 2108110, 3, 107 811, 23818, 701905, 1, 156034, 32, 1, 29, 143548, 1, 67665, 4612762, 1, 3, 20, 1 , 1, 9, 28543, 1, 1, 1, 30978, 9, 1, 19504, 79412, 15375, 763265, 1, 352373, 193 045, 1, 4570217, 9, 1, 6, 29180, 90030, 1, 1, 1, 1, 1, 93, 1, 100889, 1, 1, 37, 15, 17, 1, 81184, 1, 2, 272831, 1, 137, 1, 9, 42874, 679183, 1, 350027, 12, 1, 2 , 1, 26408, 1, 11182, 1, 30, 139590, 7, 3, 1, 1, 34729, 1, 2, 1, 1, 50343, 66873 , 3891, 1, 148952, 1, 1, 22322, 104176, 1, 3, 20549, 140266, 37827, 30504, 17, 6 8588, 120195, 1, 123353, 2, 64301, 11, 1, 109867, 4, 1, 1, 1, 28671, 1, 50963, 5 4584, 1, 1, 1, 33, 1, 381918, 1, 265823, 4771840, 155179, 314, 134086, 1, 1, 30,  1, 2, 1102665, 18, 132243, 3861, 1, 1, 208906, 60112, 1, 1, 1, 31273, 551, 3490 0, 2, 43606, 1, 1, 1, 1, 5, 2, 88342, 2, 1, 19, 3, 1, 1, 1, 1, 28507, 1, 491467,  1, 1, 22, 1, 1, 1, 1, 9345, 9, 18, 84343, 1, 2, 1, 18, 36816, 1, 1, 513028, 287 88, 5037383, 721932, 170292, 108942, 539115, 1, 575676, 20, 1, 31698, 99797, 205 21, 380986, 1, 1, 14, 2, 1, 201100, 30, 1, 119484, 1, 1, 1, 1, 2214252, 3, 4, 18 179, 9, 4, 542150, 1, 6, 157, 3182099, 4, 1, 1, 6140, 3339847, 498283, 52523, 1,  1, 1, 1, 1, 202054, 263324, 1, 6, 2, 1, 2, 72357, 12, 5, 66, 4, 7368, 1, 30706,  61936, 3945270, 138991, 1, 68247, 1, 1, 30482, 35326, 1, 1, 9, 1, 148, 1, 46985 , 1, 4325093, 1, 1, 2880384, 65173, 1, 56581, 179178, 372369, 56187, 3, 12, 8, 4 00743, 3, 28658, 1, 1, 9, 1, 4, 2, 34357, 1, 42596, 68840, 2, 62638, 158027, 617 34, 71263, 1, 1, 9, 1, 6830309, 3, 1, 1, 157253, 129837, 9, 5008187, 48499, 5981 3, 1, 40320, 233893, 5, 1383, 7732178, 16, 1, 13, 5686145, 84554, 1, 79442, 1, 1 , 256812, 127818, 31, 226113, 1, 4, 1, 1, 4506163, 1, 4, 1, 40176, 19107, 205, 2 7, 1, 448999, 1, 1, 2750, 62723, 1, 12, 1, 1, 79881, 1, 48, 13, 4, 1, 28765, 1, 33, 291330, 30817, 2, 1, 1, 1, 4170949, 16, 1, 1, 118781, 10473, 520797, 1, 8, 1 , 80215, 1, 21759, 5143209, 79141, 40229, 1, 17403, 71680, 1115694, 1, 1, 1, 10,  1, 77149, 382712, 1, 11, 84891, 47633, 1, 2, 39037, 1, 213148, 1607280, 127674,  1, 333207, 1, 78901, 1, 16203, 87580, 1, 1565571, 537902, 53000, 15, 1, 2, 1, 2 13127, 1, 338634, 2469990, 469479, 9519, 51083, 1, 42082, 33179, 1, 1, 32444, 3,  1, 201642, 99724, 377, 1, 2, 1, 36919, 1, 322707, 2, 164765, 82516, 1, 5274643,  1, 36421, 1, 8, 1, 117856, 1, 1, 493342, 1, 36289, 7, 1, 62, 2, 1, 38533, 1, 68 , 45754, 9, 102015, 312941, 1, 99 
Final score is 220.826222910756

5

Крысы в ​​действии (не ответ, а графический инструмент для ботов C ++)

С самого начала этого испытания у меня были трудности с выяснением того, с чем на самом деле сталкивались крысы на трассе.
В конце я взломал контроллер и написал вспомогательный инструмент, чтобы получить некоторое графическое представление трека.
В конце концов я сделал еще несколько взломов и добавил визуализацию возможных путей ДНК данной крысы.

Карта чрезвычайно загромождена и требует некоторого привыкания, но мне было очень полезно понять, как работают мои боты.

Вот пример:

образец трека

Вам, вероятно, понадобится увеличить изображение, чтобы увидеть что-нибудь, так что вот только первая половина:

половина пути (без каламбура)

Сначала давайте посмотрим на пути крысы. Существует один путь для каждого возможного начального местоположения (обычно 15, иногда немного меньше). Обычно они имеют тенденцию сливаться, в идеале приводя к единой победной локации.

Пути представлены большими прямыми стрелками. Цвет описывает результат:

  • зеленый: победа
  • желтый: бесконечный цикл
  • коричневый: стучит по стене
  • красный: несчастный случай

В этом примере у нас 12 выигрышных стартовых позиций, одна из которых ведет к бесконечному циклу, а две - к изнурительной смерти (будучи телепортированным в ловушку, как кажется).

Прерывания пути происходят из-за телепортации, которой вы можете следовать с помощью соответствующих изогнутых стрелок.

Теперь о цветных символах. Они представляют значение 16 цветов (серые представляют то, что видит крыса).

  • стена: квадрат
  • телепорт: 4 разветвленная звезда
  • детектор ловушек: маленький октогон

пустые цвета ... ну ... пустые.

У телепортеров есть исходящие стрелки, указывающие на их пункт назначения.

Детекторы ловушек также имеют стрелки, указывающие ловушку, которая обозначена красным кружком.
В одном случае из 9 ловушка расположена в той же ячейке, что и ее детектор, и в этом случае вы увидите маленький октогон поверх красного круга.

Это случай бледно-желтой ловушки в этом примере.
Вы также можете увидеть лиловые детекторы ловушек, указывающие вниз на указанную ловушку.

Обратите внимание, что иногда красный круг ловушки будет скрыт под стеной. Оба смертельны, поэтому результат в случае телепортации одинаков.
Также обратите внимание, что ловушка может быть расположена на телепорте, и в этом случае телепорт имеет преимущество (т.е. крыса телепортируется перед тем, как попасть в ловушку, что фактически нейтрализует ловушку).

Наконец, серые символы представляют то, что видят мои крысы (то есть значение их геномных атрибутов для цветов).

  • стена: квадрат
  • детектор ловушек: октогон
  • ловушка: X

По сути, все клетки, сидящие на сером квадрате, считаются крысами стенами.
Большие X представляют клетки, рассматриваемые как ловушки, с соответствующими октогонами, указывающими детектор, сообщивший о них.

В этом примере обе стены идентифицированы как таковые, как и бледно-желтая ловушка (действительно указывающая на смертельную клетку, поэтому правильное представление ее как стены).
Детектор сиреневых ловушек был идентифицирован как таковой (он расположен на сером восьмиугольнике), но местоположение ловушки неверно (вы можете видеть, что у некоторых красных кружков нет крестиков под ними).

Из 4 телепортеров 2 считаются стенами (бирюзовый и желто-коричневый), а 2 - пустыми клетками (красноватые и желтоватые).

Несколько пустых ячеек считаются ловушками или стенами. Присмотревшись внимательно, вы можете увидеть, что эти «неисправные детекторы» действительно запрещают вход в клетки, которые могут привести к проблемам у крысы, поэтому даже если они не соответствуют реальным цветам, у них есть определенная цель.

Код

Ну, это беспорядок, но он работает довольно хорошо.

Исходя из кода игрока, я добавил только один интерфейс: функция трассировки, используемая для сообщения о значении данной ДНК. В моем случае я использовал 3 типа (стена, детектор ловушек и пустой), но вы можете в основном выводить что-либо, связанное с цветом (или вообще ничего, если вы не хотите графики, связанной с геномом).

Я взломал контроллер, чтобы сгенерировать огромную строку символов, сопоставляющую описание дорожки и цвета с «пробным прогоном» ДНК крысы из всех возможных мест.

Это означает, что результаты будут действительно значимыми, только если бот не использует случайные значения. В противном случае отображаемые пути будут представлять только один возможный результат.

Наконец, все эти следы помещаются в большой текстовый файл, который позже читается утилитой PHP, которая производит графический вывод.

В текущей версии я делаю снимок каждый раз, когда крыса умирает после достижения новой максимальной пригодности (которая довольно хорошо показывает прогрессивное уточнение генома, не требуя слишком много снимков), и окончательный снимок в конце игры (который показывает самая удачная ДНК).

Если кому-то интересно, я могу опубликовать код.

Очевидно, что это работает только для ботов C ++, и вам нужно будет написать функцию трассировки и, возможно, изменить код PHP, если вы хотите отобразить некоторые специфичные для генома данные (серые цифры в моем случае).
Даже без информации, специфичной для ДНК, вы можете без особых усилий увидеть пути, по которым идет ваша ДНК, на данной карте.

Почему промежуточный выход?

Прежде всего, C ++ не имеет приличной переносимой графической библиотеки, особенно при использовании MSVC. Даже если сборки Win32 обычно доступны, они часто приходят в качестве запоздалой мысли, а количество внешних библиотек, пакетов и других необходимых unix-подобных тонкостей делает написание простого и быстрого графического приложения ужасной болью в части тела, которую порядочность мешает я от имени.

Я рассматривал возможность использования Qt (о единственной среде , что делает портативный GUI / графическое развитие в C ++ просто и даже приятно задач, ИМХО - вероятно , потому что это добавляет систему обмена сообщениями а - ля Objective C , что C ++ катастрофически не хватает , и делает невероятную работу ограничивающего памяти управления до минимума), но это выглядело как излишнее решение стоящей перед нами задачи (и любой, кто хотел бы использовать код, должен был бы установить biggish SDK - вряд ли это стоило бы усилий, я думаю).

Даже если принять переносную библиотеку, не нужно говорить о скорости (примерно одной секунды для создания изображения достаточно), и с ее общеизвестной жесткостью и присущей ей запутанностью C ++, безусловно, не лучший инструмент для работы.

Кроме того, наличие промежуточного текстового вывода добавляет большую гибкость. Как только данные есть, вы можете использовать их для других целей (например, анализируя производительность ботов).

Почему PHP?

Я нахожу язык чрезвычайно простым и адаптируемым, очень удобным для создания прототипов. Я сделал это своим любимым языком для задач кода, которые не требуют экстремальных характеристик.
Это ужасный язык для игры в гольф, но гольф все равно никогда не был моей чашкой чая.

Я полагаю, что python или Ruby было бы так же приятно использовать для тех же целей, но у меня никогда не было возможности поработать с ними серьезно, и в последнее время я работал над веб-сайтами, так что PHP это так.

Даже если вы не знаете язык, не должно быть слишком сложно изменить код в соответствии с вашими потребностями. Только не забывайте $s перед переменными, как в старые добрые базовые дни :).


1
Не могли бы вы поделиться своим инструментом? Я не вижу ни кода, ни ссылки в вашем ответе.
Фрэнки

5

SkyWalker - Python - результаты менее 231 в 50 играх

Итак, сначала код, а затем несколько пояснений. Я надеюсь, что ничего не сломалось при копировании.

class SkyWalker(Player):
    def __init__(self):
        Player.__init__(self)
        self.coords = [#Coordinate(-1,-1),
                       #Coordinate( 0,-1),
                       Coordinate( 1, 0),
                       Coordinate( 1,-1),
                       #Coordinate(-1, 0),
                       #Coordinate( 0, 0),
                       #Coordinate(-1, 1),
                       #Coordinate( 0, 1),
                       Coordinate( 1, 1)]

        self.n_moves = len(self.coords)

    def visionToMove(self, x, y):
        x = x - 2
        y = y - 2

        return (x, y)

    def trapToMove(self, x, y, offx, offy):
        x = x - 2 + (offx % 3) - 1
        y = y - 2 + (offy % 3) - 1
        return (x, y)

    def isNeighbour(self, x1, y1, x2, y2):
        if (x1 == x2) or (x1+1 == x2) or (x2+1 == x1):
            if (y1 == y2) or (y1+1 == y2) or (y2+1 == y1):
                return True
        return False

    def calcMove(self, donots, never, up):
        forwards = {(1, 0): 0, (1, 1): 0, (1, -1): 0, (0, 1): 10, (0, -1): 10}

        for key in forwards:
            if key in never:
                forwards[key] = 100
            for x in donots:
                if (key[0] == x[0]) and (key[1] == x[1]):
                    forwards[key] = 20

        min_value = min(forwards.itervalues())
        min_keys = [k for k in forwards if forwards[k] == min_value]

        return random.choice(min_keys)

    def turn(self):
        trap1 = self.bit_chunk(0, 4)
        trap1_offsetx = self.bit_chunk(4, 2)
        trap1_offsety = self.bit_chunk(6, 2)
        trap2 = self.bit_chunk(8, 4)
        trap2_offsetx = self.bit_chunk(12, 2)
        trap2_offsety = self.bit_chunk(14, 2)
        wall1 = self.bit_chunk(16, 4)
        wall2 = self.bit_chunk(20, 4)
        tel1 = self.bit_chunk(24, 4)
        tel1_good = self.bit_chunk(28, 3)
        tel2 = self.bit_chunk(31, 4)
        tel2_good = self.bit_chunk(35, 3)
        tel3 = self.bit_chunk(38, 4)
        tel3_good = self.bit_chunk(42, 3)
        tel4 = self.bit_chunk(45, 4)
        tel4_good = self.bit_chunk(49, 3)
        up = self.bit_at(100)

        donots = []
        never = []

        for y in range(0, 5):
            for x in range(0, 5):
                c = self.vision[y][x]
                if (c == -1):
                    never += self.visionToMove(x, y),
                elif (c == trap1):
                    donots += self.trapToMove(x, y, trap1_offsetx, trap1_offsety),
                elif (c == trap2):
                    donots += self.trapToMove(x, y, trap2_offsetx, trap2_offsety),
                elif (c == wall1):
                    donots += self.visionToMove(x, y),
                elif (c == wall2):
                    donots += self.visionToMove(x, y),
                elif (c == tel1):
                    if (tel1_good > 3):
                        donots += self.visionToMove(x, y),
                elif (c == tel2):
                    if (tel2_good > 3):
                        donots += self.visionToMove(x, y),
                elif (c == tel3):
                    if (tel3_good > 3):
                        donots += self.visionToMove(x, y),
                elif (c == tel4):
                    if (tel4_good > 3):
                        donots += self.visionToMove(x, y),

        coord = self.calcMove(donots, never, up)

        return Coordinate(coord[0], coord[1])

Некоторое объяснение

На мой взгляд, главное отличие в том, что я не кодирую каждый цвет. Вместо этого я стараюсь сохранить количество цветов, которые важны. На мой взгляд, эти цвета - ловушки, стены и телепорты. Образцу не нужно знать цвет хорошей клетки. Поэтому мой геном структурирован следующим образом.

  • 2 x 8 битов для ловушек, первые 4 бита - номер цвета, остальные 4 - смещение
  • 2 х 4 бит для стен, просто цвет
  • 4 х 7 бит для телепортеров, снова 4 бита для цвета, 3 для выбора хорошего или плохого

Это составляет в общей сложности 52 используемых бита. Однако я использую только первый бит из трех решателей телепортов (я проверяю, больше ли число 3). Таким образом, другие 2 могут быть удалены, в результате чего у меня будет использоваться 44 бита.

На каждом ходу я проверяю каждое поле моего зрения, если это один из плохих цветов (+ вне поля -1), и добавляю его в список полей, в которые образец не хочет перемещаться. В случае ловушки я добавляю поле с сохраненным смещением для этого цвета ловушки.

На основе списка этих плохих полей рассчитывается следующий ход. Порядок предпочтительных полей:

  1. вперед
  2. вверх или вниз
  3. вверх или вниз
  4. назад

Если два поля категории применимы, одно выбирается случайным образом.

Результаты

Individual scores: [192, 53116, 5, 1649, 49, 2737, 35, 5836, 3, 10173, 4604, 22456, 21331, 445, 419, 2, 1, 90, 25842, 2, 712, 4, 1, 14, 35159, 13, 5938, 670, 78, 455, 45, 18, 6, 20095, 1784, 2, 11, 307853, 58171, 348, 2, 4, 190, 7, 29392, 15, 1158, 24549, 7409, 1]
On average, your bot got 231.34522696 points

мысли

  • Я понятия не имею, повезло ли мне с 50 пробежками или есть ли какая-то мудрость в моей стратегии.

  • Мои пробежки, кажется, никогда не взлетают и получают супер высокие баллы, но они также стремятся найти хотя бы несколько раз цель

  • Небольшая случайность хороша, чтобы не застрять в ловушке где-то ближе к концу гонки

  • Я думаю, что не специальные цвета никогда не бывают плохими. Однако их экземпляры могут быть плохими, когда они находятся на смещении ловушки. Таким образом, маркировка цвета плохо, если это не ловушка, стена или плохой телепорт, не имеет смысла.

  • Стены - величайшие враги

улучшения

Во-первых, даже при том, что я не буду видеть, как черные квадраты все ближе и ближе к цели, порт C ++ необходим для проведения большего количества тестов и получения более значимого результата.

Одна из основных проблем заключается в том, что если перед крысой появляются плохие клетки (или те, которые образец считает плохими), он легко начинает двигаться вверх и вниз по кругу. Это может быть остановлено или уменьшено, если смотреть в этих случаях на 2 шага вперед и помешать ему переместиться в поле, где он просто снова вернется.

Часто проходит довольно много времени, пока крыса с хорошими генами достигает цели и начинает распространять ее гены. Может быть, мне нужна стратегия для увеличения разнообразия в этих случаях.

Так как телепорты трудно подсчитать, возможно, мне следует разделить население на тех, кто рискован, и всегда брать хороших телепортов и тех, кто более обеспокоен, и брать их только в том случае, если другого выбора нет.

Я должен как-то использовать вторую половину моего генома.


Я также стараюсь хранить цвета, но в итоге пришел к выводу, что это не работает, потому что вы получите двойные. Например, если self.bit_chunk(16, 4)и self.bit_chunk(20, 4)имеют оба значения, 0010вы фактически сохранили информацию только об одной из двух ловушек.
Руут

Мне нужно было добавить отступ в одну строку, чтобы заставить это работать - я предполагаю, что это потерялось во время копирования и вставки. Я добавил это и сейчас в твой код.
Трихоплакс

Для всех, кто хочет запустить это: он работает в Python 2 и может быть запущен в Python 3, изменив единственное вхождение itervaluesна values.
Трихоплакс

Я получил следующие результаты: [6155, 133, 21, 12194, 8824, 3, 3171, 112, 111425, 3026, 1303, 9130, 2680, 212, 28, 753, 2923, 1, 1, 4140, 107, 1256 , 90, 11, 104, 1538, 63, 917, 8, 1, 709, 11, 304, 212, 2, 43, 5, 4, 206, 8259, 75, 28, 7, 1, 11, 5, 1 , 1244, 1398, 13] Среднее геометрическое 122,9220309940335
трихоплакс

Похоже, нам нужно было запустить более 50 игр, чтобы получить достоверную оценку.
Трихоплакс

3

Python, NeighboursOfNeighbors, счет = 259,84395 более 100 игр

Это вариант ColorScorePlayer. Каждые 6 бит хранят показатель качества для квадрата. Когда бот делает ход, он оценивает каждый из 3 квадратов вперед - по диагонали вверх, вперед и вниз. Оценка - это качество квадрата плюс половина среднего качества следующих 3 квадратов. Это дает боту некоторый взгляд вперед, не подавляя качество первого квадрата. Алгоритм похож на LookAheadPlayer, который я не видел до написания этого решения.

class NeighborsOfNeighbors(Player):
  def __init__(self):
    Player.__init__(self)
    self.coords = [ Coordinate( 1, 0),
                    Coordinate( 1,-1),
                    Coordinate( 1, 1)
                    ]

  def turn(self):
    scores=[self.score(c.x,c.y)+0.5*self.adjacentScore(c.x,c.y) if self.vision_at(c.x,c.y)>-1 else None for c in self.coords ]
    max_score = max(scores)
    return random.choice( [c for s,c in zip(scores,self.coords) if s==max_score] )

  def adjacentScore(self,x,y):
    adjacent = [(x+1,y)]
    if self.vision_at(x,y+1)>-1:
      adjacent+=[(x+1,y+1)]
    if self.vision_at(x,y-1)>-1:
      adjacent+=[(x+1,y-1)]
    adjscores=[self.score(a,b) for a,b in adjacent]
    return sum(adjscores)/float(len(adjscores))

  def score(self,x,y):
    return -1 if self.vision_at(x,y) == -1 else self.bit_chunk(6*self.vision_at(x,y),6)

В одной строке отсутствовал отступ. Я думаю, это потеряно при вставке. Я добавил это.
trichoplax

Работая в python 3, он жаловался на сравнение None при расчете max (баллов). Поэтому я изменил else Noneк else 0на предыдущей строке , чтобы вычислить ваш счет. Надеюсь, это оставит вашу логику без изменений (я не внес изменений в ваш код здесь, на SE, кроме добавления потерянного отступа).
Трихоплакс

Работая на питоне 3, я получил следующие оценки за этот ответ: [1, 13085, 360102, 1, 73713, 1, 189, 1, 1, 193613, 34, 195718, 199, 8, 1, 1, 60006, 66453, 2, 2, 53, 425206, 1, 4, 1, 1, 16, 153556, 1, 18134, 35655, 1, 4211684, 2, 1, 26451, 8, 1, 724635, 69242, 38469, 796553, 111340, 1, 25, 40017, 76064, 66478, 209365, 3925393]
Trichoplax

Геометрический среднее 428.3750848244933
Trichoplax

2

ROUS (Грызун необычного размера), Java, счет = 0

Это окружает, чтобы решить, куда идти. Из-за неработающего контроллера Java у меня нет баллов за это. Это будет очень далеко, только если он найдет несколько телепортов, чтобы помочь ему.Это имеет тенденцию вымирать и сбивать контроллер время от времени. Вероятно, это связано с тем, что природной средой является Огненное Болото.

import java.awt.*;
import java.util.Map;

public class ROUS extends Player{

    private static final int NUMBER_OF_GENES = 33;
    private static final int GENE_SIZE = 3;
    private static final Point[] coords = new Point[]{
        new Point(-1, -1),
        new Point(-1, 0),
        new Point(-1, 1),
        new Point(0, -1),
        new Point(0, 1),
        new Point(1, -1),
        new Point(1, 0),
        new Point(1, 1)
    };

    public Point takeTurn(String dna, Map<Point, Integer> vision){
        Point[] table = decode(dna);
        int hash = hash(vision);
        return table[hash];
    }

    private int hash(Map<Point, Integer> surroundings) {
        return Math.abs(surroundings.hashCode()) % NUMBER_OF_GENES;
    }

    private Point[] decode(String dna) {
        Point[] result = new Point[NUMBER_OF_GENES];

        for (int i = 0; i < NUMBER_OF_GENES; i++){
            int p = Integer.parseInt(dna.substring(i * GENE_SIZE, (i + 1) * GENE_SIZE), 2);
            int x;
            int y;

            result[i] = coords[p];
        }
        return result;
    }
}

1
Контроллер Java работает сейчас.
Мартин Эндер

3
Сначала я подумал, что вы отдаете дань уважения древней Руси, но, похоже, это был Роб Райнер.

Минимально возможный балл 1
трихоплакс

@trichoplax ... сбой контроллера ...
TheNumberOne

О, я понимаю - так часто это случается, что ты не можешь достичь конца пробега?
трихоплакс

2

Серо-цветной взгляд (C ++, ~ 1.35)

Этот не очень хорошо в среднем, но в редких случаях он работает блестяще. К сожалению, мы получаем средние геометрические показатели (1,35), а не максимальные (20077).

Этот алгоритм работает, просто используя 4-битные коды серого для отображения оценки каждого цвета где-то от -2 до 2 (с уклоном в сторону диапазона [-1..1]), и вычисляет оценку плитки каждого хода и его следующих ходов , Он также использует 2-битный серый код, чтобы определить множитель для самого тайла, а также коэффициент смещения для перемещения вправо. (Серые коды гораздо менее восприимчивы к большим скачкам из-за мутаций, хотя на самом деле они не оказывают никакого влияния на пересечение средней точки кода ...)

Он также абсолютно ничего не делает для того, чтобы пытаться обрабатывать ловушки специально, и я подозреваю, что это может привести к падению (хотя я не добавил никаких инструментов в контроллер для проверки этой теории).

Для каждого возможного хода он определяет счет, и среди всех ходов с наибольшим количеством очков он выбирает случайным образом.

coord_t colorTileRanker(dna_t d, view_t v) {
    const int COLOR_OFFSET = 0; // scores for each color (4 bits each)
    const int SELF_MUL_OFFSET = 96; // 2 bits for self-color multiplier
    const int MOVE_MUL_OFFSET = 98; // 2 bits for move-forward multiplier

    static const int gray2[4] = {0, 1, 3, 2};
    static const int gray3[8] = {0, 1, 3, 2, 7, 6, 4, 5};

    // bias factor table
    const int factorTable[4] = {0, 1, 2, 1};

    const int selfMul = factorTable[gray2[dnaRange(d, SELF_MUL_OFFSET, 2)]]*2 + 9;
    const int moveMul = factorTable[gray2[dnaRange(d, MOVE_MUL_OFFSET, 2)]] + 1;

    // scoring table for the color scores
    static const int scoreValue[8] = {0, 1, 2, 3, 4, 3, 2, 1};

    std::vector<coord_t> bestMoves;
    int bestScore = 0;

    for (int x = -1; x <= 1; x++) {
        for (int y = -1; y <= -1; y++) {
            const int color = v(x, y);
            if ((x || y) && (color >= 0)) {
                int score = 0;

                // score for the square itself
                score += selfMul*(scoreValue[gray3[dnaRange(d, COLOR_OFFSET + color*3, 3)]] - 2);

                // score for making forward progress;
                score += moveMul*(x + 1);

                // score for the resulting square's surrounding tiles
                for (int a = -1; a <= 1; a++) {
                    for (int b = -1; b <= 1; b++) {
                        const int color2 = v(x + a, y + b);
                        if (color2 >= 0) {
                            score += scoreValue[gray3[dnaRange(d, COLOR_OFFSET + color2*3, 3)]] - 2;
                        }
                    }
                }

                if (score > bestScore) {
                    bestMoves.clear();
                    bestScore = score;
                }
                if (score >= bestScore) {
                    bestMoves.push_back({x, y});
                }
            }
        }
    }

    if (bestMoves.empty()) {
        return {v.rng.rint(2), v.rng.rint(3) - 1};
    }
    return bestMoves[v.rng.rint(bestMoves.size())];
}

В моем последнем заезде я получил очки: 1 1 1 1 1 1 1 46 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 20077 1 1 1 2 1 1 1 1 1

Я хотел бы получить больше из 20077-х и меньше из 1-х. :)


1
Использование серого кода является идеей серого! ;)
Матович

1
+1 для кодов Грея. Тем не менее, полностью устойчивый к мутациям геном немного повредит разнообразию. И, между прочим, 20.000 баллов - это даже не тот максимум, которого вы можете достичь. Если у какой-нибудь крысы появляется способность бегать по треку из любого возможного стартового места, она становится бессмертной и получает огромный показатель пригодности. Его геном быстро доминирует, приводя к популяции почти до 50 тыс. Крыс и десятку миллионов.

2

C ++, TripleScore, счет: 100 ~ 400

Во-первых, мой результат сильно различается за несколько запусков (в основном из-за количества единиц).

Ядро рассчитывает оценку по 5 направлениям: вверх, вниз, вперед-вверх, вперед и вниз-вниз. Сначала вычисляются оценки «вверх» и «вниз», затем результаты сравниваются со стоимостью пребывания на месте. Если оставаться на месте лучше, чем двигаться вверх или вниз, эти направления не будут выбраны (поэтому они должны идти вперед). Это необходимо для предотвращения подпрыгивания (вверх, вниз, вверх, вниз, ...) между двумя точками.

Теперь оцениваются 3 других направления: вперед-вверх, прямо вперед и вниз-вниз. Со всех исследованных направлений сохраняются те, кто набрал наибольшее количество баллов, и 1 из них выбирается случайным образом.

Скоринг направления: TripleScore рассчитывает счет движения, используя 3 подсчета:

  • Оценка цвета пункта назначения (зависит от днк, как в colorScorePlayer)
  • Оценка движения вперед (зависит от днк)
  • Максимальная оценка за продвижение вперед от пункта назначения (умноженная на коэффициент, который хранится в ДНК)

Как и в случае с другими ответами, оценка в значительной степени зависит от количества возвращаемых 1-баллов.

#define CHUNKSIZE 5 //We have 20 values so 5 bits/value
#define MAXVALUE 32 //2^CHUNKSIZE
#define AVGVALUE MAXVALUE/2

#define DNASEGMENT(dna, i) dnarange(dna, i*CHUNKSIZE, CHUNKSIZE)
#define DNA_COLOR 0
#define DNA_FORWARD 16
#define DNA_LOOKAHEAD 17

//Get the score for a specific move
int calcscore(dna_t dna, view_t view, int x, int y, bool final){
  if (view(x,y) == OUT_OF_BOUNDS){
    //We cant go there
    return -MAXVALUE;
  }
  //The score of the color
  int s = DNASEGMENT(dna, DNA_COLOR+view(x,y))-AVGVALUE;
  //The score of going forward
  s += x*DNASEGMENT(dna, DNA_FORWARD);

  //Get the children or not
  if (!final){
    int max=-MAXVALUE;
    int v;
    //Get the maximum score of the children
    for (int i=-1; i<2; ++i){
        v = calcscore(dna, view, x+1, y+i, true);
        if (v>max){
            max=v;
        }
    }
    //Apply dna factor to the childs score
    s += (max * DNASEGMENT(dna, DNA_LOOKAHEAD))/AVGVALUE;
  }
  return s;
}

coord_t TripleScore(dna_t dna, view_t view) {
  int maxscore = -100;
  int score;
  coord_t choices[5]; //Maximum 5 possible movements
  int maxchoices = 0;
  int zeroscore = calcscore(dna, view, 0, 0, false);

  //Go over all possible moves and keep a list of the highest scores
  for (int x=0; x<2; ++x){
    for (int y=-1; y<2; ++y){
        if (x | y){
            score = calcscore(dna, view, x, y, false);
            if (score > maxscore){
                maxscore = score;
                choices[0] = {x, y};
                maxchoices = 1;
            }else if (score == maxscore){
                choices[maxchoices++] = {x, y};
            }
        }
    }
    if (!x && maxscore <= zeroscore){
        //I will NOT bounce!
        maxscore = -100;
    }
  }

  return choices[view.rng.rint(maxchoices)];
}

2

Ruby - вероятностныйScorePlayer

class ProbabilisticScorePlayer < Player
    Here = Vector2D.new( 0, 0)
    Forward = Vector2D.new( 1, 0)
    Right = Vector2D.new( 0, 1)
    Left = Vector2D.new( 0,-1)

    def vision_at(vec2d)
        v = @vision[vec2d.x+2][vec2d.y+2]
        v==-1?nil:v
    end

    def turn
        coords = [Forward]
        [Here,Forward].each{|x|
            [Here,Right,Left].each{|y|
                c = x+y
                if x!=y && vision_at c > -1
                  coords.push c if bit_at(vision_at c)==1
                  coords.push c if bit_at(vision_at(c+Forward)+16)==1
                  coords.push c if bit_at(vision_at(c+Right)+32)==1
                  coords.push c if bit_at(vision_at(c+Left)+48)==1
                  coords.push c if bit_at(vision_at(c+Forward+Right)+64)==1
                  coords.push c if bit_at(vision_at(c+Forward+Left)+80)==1
                end
            }
        }
        coords.sample(random: @rng)
    end
end

Эта крыса с высокой степенью недетерминированности вычисляет вероятность попадания в пространство по соседству. Первые 16 слотов в геноме представляют 16 цветов. 1 в слоте означает, что цвет хорош для перехода, 0 означает плохой. Следующие 16 держат то же самое для пространства перед вашей целью и так далее.

Основным преимуществом вероятностного подхода является то, что практически невозможно долго застрять за стеной. Недостатком является то, что вы почти никогда не получите почти идеальную крысу.


+1 за оригинальность. Какую оценку вы получили?

Никогда еще не проверял это ...
MegaTom

Вы забыли дать cначальное значение? Кажется, он не определен при первом использовании if.
Мартин Эндер

@ MartinBüttner да, я забыл. Я исправлю это сейчас.
MegaTom

Я не очень хорошо знаю Ruby, но ваш код не работает под Ruby2.1.5. coordsэто не список, вы используете &&вместо него andи забываете скобки, и даже после исправления всего этого вы не ограничиваете значения ГСЧ, поэтому получаете пустое направление. Это псевдокод, или что-то, предназначенное для работы с каким-то Ruby-диалектом?

2

Java, RunningStar, Score = 1817.050970291959 более 1000 игр

Этот бот использует цветовое кодирование Run-Bonus с техникой StarPlayer .

Обновление: исправлен контроллер Java.

Scores: 6, 81533, 1648026, 14, 5, 38841, 1, 76023, 115162, 3355130, 65759, 59, 4, 235023, 1, 1, 1, 3, 2, 1, 1, 14, 50, 1, 306429, 68, 3, 35140, 2, 1, 196719, 162703, 1, 1, 50, 78233, 5, 5, 5209, 1, 2, 60237, 1, 14, 19710, 1528620, 79680, 33441, 58, 1, 4, 45, 105227, 11, 4, 40797, 2, 22594, 9, 2192458, 1954, 294950, 2793185, 4, 1, 1, 112900, 30864, 23839, 19330, 134178, 107920, 5, 122894, 1, 1, 2721770, 8, 175694, 25235, 1, 3109568, 4, 11529, 1, 8766, 319753, 5949, 1, 1856027, 19752, 3, 99071, 67, 198153, 18, 332175, 8, 1524511, 1, 159124, 1, 1917181, 2, 1, 10, 276248, 1, 15, 1, 52, 1159005, 43251, 1, 536150, 75864, 509655, 1126347, 250730, 1548383, 17, 194687, 27301, 2, 1, 207930, 621863, 6065, 443547, 1, 6, 1, 1, 1, 1, 556555, 436634, 25394, 2, 61335, 98076, 1, 190958, 2, 18, 67981, 3, 8, 119447, 1, 1, 1, 19, 28803, 23, 33, 60281, 613151, 1, 65, 20341, 799766, 476273, 105018, 357868, 3, 92325, 2062793, 18, 72097, 30229, 1, 1, 3, 610392, 1, 202149, 887122, 56571, 1, 77788, 61580, 4, 72535, 381846, 148682, 26676, 1, 210, 3556343, 212550, 650316, 33491, 180366, 1, 295685, 46255, 43295, 1006367, 63606, 1, 1, 1, 1, 3094617, 21, 10, 3, 1, 1, 14730, 1585801, 102, 2, 410353, 1570, 1, 17423, 1, 1849366, 5, 1, 357670, 1, 1, 1, 1, 89936, 349048, 15, 7, 6, 2, 121654, 1852897, 19, 1, 103275, 1, 1, 771797, 23, 19, 6700, 1, 135844, 2966847, 3, 2356708, 101515, 1, 17, 1, 996641, 22, 16, 657783, 171744, 9604, 1, 1335166, 1739537, 2365309, 1, 3378711, 11332, 3980, 182951, 609339, 8, 10, 1746504, 61895, 386319, 24216, 331130, 12193, 1, 284, 1, 2, 50369, 38, 8, 1, 1238898, 177435, 124552, 22370, 1418184, 20132, 6, 2, 730842, 1, 1341094, 141638, 534983, 1551260, 31508, 96196, 434312, 3012, 715155, 1, 276172, 214255, 1, 208948, 4, 1631942, 512293, 37, 64474, 1342713, 1, 132634, 13, 2, 61876, 1081704, 160301, 2, 488156, 2414109, 1809831, 5, 74904, 6, 11, 5, 1, 79856, 96, 35421, 229858, 238507, 3838897, 18, 44, 1, 1659126, 9, 33708, 12, 1, 758381, 162742, 256046, 3, 15, 142673, 70953, 58559, 6, 2, 1, 984066, 290404, 1072226, 66415, 4465, 924279, 48133, 319765, 519401, 1, 1, 1201037, 418362, 17022, 68, 213072, 37, 1039025, 1, 2, 6, 4, 45769, 1, 5, 1061838, 54614, 21436, 7149, 1, 1, 1, 35950, 2199045, 1, 379742, 3, 2008330, 238692, 181, 7, 140483, 92278, 214409, 5179081, 1, 1, 334436, 2, 107481, 1142028, 1, 31146, 225284, 1, 14533, 4, 3963305, 173084, 102, 1, 4732, 14, 1, 25, 11032, 224336, 2, 131110, 175764, 81, 5630317, 1, 42, 1, 89532, 621825, 2291593, 210421, 8, 44281, 4, 303126, 2895661, 2672876, 3, 436915, 21025, 1, 4, 49227, 1, 39, 3, 1, 103531, 256423, 2, 1600922, 15, 1, 2, 58933, 1114987, 1, 4, 3, 1, 1544880, 285673, 240, 2, 128, 214387, 3, 1327822, 558121, 5, 2718, 4, 1258135, 7, 37418, 2729691, 1, 346813, 385282, 2, 35674, 513070, 13, 1930635, 117343, 1929415, 52822, 203219, 1, 52407, 1, 1, 1, 3, 2, 37121, 175148, 136893, 2510439, 2140016, 437281, 53089, 40647, 37663, 2579170, 83294, 1597164, 206059, 1, 9, 75843, 773677, 50188, 12, 1, 1067679, 105216, 2452993, 1813467, 3279553, 280025, 121774, 62, 5, 113, 182135, 1, 16, 71853, 4, 557139, 37803, 228249, 6, 32420, 8, 410034, 73889, 1, 2, 96706, 48515, 1, 3, 1314561, 137, 966719, 692314, 80040, 85147, 75291, 1, 1, 30, 38119, 182723, 42267, 3836110, 22, 986685, 2, 37, 1, 3, 26, 43389, 2679689, 1, 1, 57365, 1, 2662599, 2, 72055, 1, 141247, 1, 1, 1122312, 1, 1080672, 4, 266211, 1, 34163, 1490610, 256341, 1, 627753, 32110, 1, 42468, 1, 10746, 1, 9, 1, 46, 1714133, 5, 117, 1, 104340, 218338, 151958, 122407, 211637, 223307, 57018, 74768, 582232, 2, 621279, 4, 1, 11, 196094, 1839877, 167117, 8, 42991, 2199269, 124676, 1, 1, 1, 5, 1, 1, 698083, 1, 76361, 1564154, 67345, 1398411, 9, 11, 105726, 1197879, 1, 2, 62740, 39, 2, 397236, 17057, 267647, 13, 57509, 22954, 1, 12, 747361, 4325650, 21425, 2160603, 144738, 1, 204054, 3113425, 6, 3019210, 30, 3359, 1, 89117, 489245, 1, 218068, 1, 1, 14718, 222722, 1, 1, 216041, 72252, 279874, 183, 89224, 170218, 1549362, 2, 1, 953626, 32, 130355, 30460, 121028, 20, 159273, 5, 2, 30, 1, 76215, 1654742, 2326439, 1, 53836, 1, 6, 4, 72327, 9, 285883, 1, 908254, 698872, 47779, 3, 2293485, 265788, 3766, 1, 1, 83151, 36431, 307577, 256891, 29, 1, 1, 1093544, 145213, 5, 2, 581319, 2911699, 1, 213061, 1359700, 2, 1, 343110, 1, 157592, 1708730, 1, 22703, 32075, 1, 1, 87720, 159221, 2313143, 10, 2266815, 2106917, 1345560, 3146014, 4, 551632, 1066905, 550313, 4069794, 1, 1406178, 38981, 1, 3, 1, 3039372, 241545, 35, 63325, 85804, 1365794, 2, 2143204, 48, 1, 99, 3225633, 7, 4074564, 1023899, 3209940, 2054326, 70880, 2, 1, 284192, 1944519, 84682, 2, 867681, 90022, 378115, 1, 15, 602743, 1337444, 131, 1, 229, 161445, 3, 2, 5591616, 195977, 92415, 637936, 142928, 1, 2310569, 923, 1, 230288, 1300519, 398529, 2233, 100261, 4323269, 81362, 37300, 1, 233775, 32277, 434139, 323797, 19214, 782633, 2881473, 1, 1, 9, 337016, 1, 515612, 44637, 17, 1, 25, 67758, 1737819, 16454, 30613, 692963, 62216, 222062, 344596, 3, 33782, 19, 180441, 23552, 20462, 70740, 10298, 109691, 1, 1729427, 33714, 1770930, 1, 1, 1, 1, 290766, 136688, 688231, 3250223, 30703, 1985963, 527128, 3, 226340, 195576, 30, 1, 3, 1, 793085, 5527, 5, 1, 2188429, 1327399, 5, 6192537, 1445186, 2478313, 2, 16892, 3, 1, 1, 15, 12, 1361157, 4, 1241684, 1, 45008, 1, 505095, 4037314, 14, 8, 1, 16740, 69906, 45, 1, 240949, 3975533, 212705, 2617552, 278884, 1, 24966, 958059, 231886, 22929, 4052071, 51259, 67791, 78739, 1, 165787, 67, 518191, 86923, 437, 1271004, 135941, 244766, 1, 1, 1, 1152745, 1, 3, 406365, 3847357, 476636, 135097, 304368, 8, 1578276, 1, 1, 375, 1, 1, 1298206, 1860743, 2, 35311, 834516, 421428, 2, 66629, 1, 309845, 398756, 33, 907277, 384475, 2267460, 1, 269300, 124525, 34399, 93584, 362186, 811260, 426109, 1, 1009323, 109986, 122181, 1, 1, 3626487, 11452, 1092410, 57233, 6, 2009226, 1, 83333, 4, 1338631, 79114, 2140249, 51813, 1118986, 43514, 1529365, 1, 101, 1, 1,
package game.players;

import java.awt.Point;
import java.util.*;

public class RunningStar extends Player{

    @Override
    public Point takeTurn(String genome, Map<Point, Integer> vision) {
        Map<Integer, Integer> squareCosts = decode(genome);
        Path path = astar(vision, squareCosts);
        return path.get(1);
    }

    private Path astar(Map<Point, Integer> vision, Map<Integer, Integer> squareCosts) {
        Set<Path> closed = new HashSet<>();
        PriorityQueue<Path> open = new PriorityQueue<>();
        open.add(new Path(new Point(0, 0), 0));
        while (!open.isEmpty()){
            Path best = open.remove();
            if (best.head().x == 2 || (best.head().x > 0 && (best.head().y == 2 || best.head().y == -2))){
                return best;
            }
            for (Path path : pathsAround(best, vision, squareCosts)){
                if (!closed.contains(path) && !open.contains(path)){
                    open.add(path);
                }
            }
            closed.add(best);
        }

        Path p = new Path(new Point(0,0), 0);
        return p.add(new Point((int)(random.nextDouble() * 3 - 1), (int)(random.nextDouble() * 3 - 1)), 0);
    }

    private List<Path> pathsAround(Path path, Map<Point, Integer> vision, Map<Integer, Integer> costs) {
        Point head = path.head();
        List<Path> results = new ArrayList<>();
        for (int i = -1; i <= 1; i++){
            for (int j = -1; j <= 1; j++){
                if (i == 0 && j == 0){
                    continue;
                }
                Point p = new Point(head.x + i, head.y + j);
                if (!vision.containsKey(p) || vision.get(p) == -1){
                    continue;
                }
                results.add(path.add(p, costs.get(vision.get(p))));
            }
        }
        return results;
    }

    private Map<Integer, Integer> decode(String genome) {
        int chunkLength = genome.length()/16;
        Map<Integer, Integer> costs = new HashMap<>();
        for (int i = 0; i < 16; i++){
            int runSize = 0;
            int cost = 0;
            for (int j = i * chunkLength; j < (i + 1) * chunkLength; j++){
                switch (genome.charAt(j)){
                    case '0':
                        runSize = 0;
                        break;
                    case '1':
                        cost += ++runSize;
                }
            }
            costs.put(i, cost);
        }
        return costs;
    }

    private class Path implements Comparable<Path>{

        Point head;
        Path parent;
        int length;
        int totalCost;

        private Path(){}

        public Path(Point point, int cost) {
            length = 1;
            totalCost = cost;
            head = point;
            parent = null;
        }

        public Point get(int index) {
            if (index >= length || index < 0){
                throw new IllegalArgumentException(index + "");
            }
            if (index == length - 1){
                return head;
            }
            return parent.get(index);
        }

        public Point head() {
            return head;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Path path = (Path) o;

            if (!head.equals(path.head)) return false;

            return true;
        }

        @Override
        public int hashCode() {
            return head.hashCode();
        }

        @Override
        public int compareTo(Path o) {
            return totalCost - o.totalCost;

        }

        public Path add(Point point, int cost) {
            Path p = new Path();
            p.head = point;
            p.totalCost = totalCost + cost;
            p.length = length + 1;
            p.parent = this;
            return p;
        }
    }
}

2

Прыжок вперед, Python 2

Не особенно новаторский, но это моя единственная попытка, которая прошла хорошо.

class LeapForward(Player):
  def __init__(self):
    Player.__init__(self)
    self.coords = [Coordinate( 1, 0),
                   Coordinate( 1,-1),
                   Coordinate( 1, 1)]
    self.n_moves = len(self.coords)

  def turn(self):
    notOKColors = [self.bit_chunk(4*n,4) for n in range(4,8)]
    notOKMap = [Coordinate(x-2,y-2) for x in range(0,5) for y in range(0,5) if self.vision[y][x] not in notOKColors]
    goTo = [c for c in self.coords if c in notOKMap]
    if not goTo:
      goTo = [Coordinate(1,0)]
    return random.choice(goTo)

В основном, это кодирует четыре цвета (каждый 4 бита), чтобы избежать, в геноме. Затем он переходит к цвету, которого нет в этом списке. Если все цвета плохие, он все еще прыгает вперед к неизвестному.


Наверное, следовало бы назвать его «RedQueen» :)
plannapus

1

Java - IAmARobotPlayer - Оценка 3,7

Я только что создал эту крысу-робота для сравнения с другой (пока не очень интересной) программой, которую я сделал. В целом он не очень хорошо забивает, но если он где-то забьет, он получит много крыс. Идея состоит в том, что он будет смотреть только на три клетки перед собой, каждая клетка хорошая или плохая. Это дает двоичное число. Затем он будет искать это число в своем геноме, брать три последовательных бита, также преобразовывать их в число и выполнять действие, которое хранится под этим номером. Так что он всегда действует одинаково, когда сталкивается с одной и той же ситуацией.

package game.players;
import java.awt.*;
import java.util.Map;
public class IAmARobotPlayer extends Player{
    private static final Point[] possibleMoves = {new Point(1,-1), new Point(1,0), new Point(1,1), new Point(0,-1), new Point(0,1), new Point(1,-1), new Point(1,0), new Point(1,1)};
    private int isGood(int pos,Map<Point,Integer> vision, char[] genomeChar){
        int value = vision.get(new Point(1,pos));
        if(value ==-1){
            return 0;
        } else {
            return genomeChar[84+value]-'0';
        }
    }

    @Override
    public Point takeTurn(String genome, Map<Point, Integer> vision) {

        char[] genomeChar = genome.toCharArray();
        int situation = 4*isGood(1,vision,genomeChar)+2*isGood(0,vision,genomeChar)+1*isGood(-1,vision,genomeChar);
        int reaction = 4*(genomeChar[3*situation+0]-'0')+2*(genomeChar[3*situation+1]-'0')+1*(genomeChar[3*situation+2]-'0');
        return possibleMoves[reaction];

    }
}

Результат:

Individual scores: 1, 1, 332, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 47560, 15457, 1, 
Your final score is 3.7100115087136234

1

Осторожные образцы - C ++ - оценка около 2030 за 200 пробежек

При этом используется цветная часть (16x4 бита) кодировки ДНК из Blind Faith, но остальная часть (36 бит) ДНК полностью не используется.

Кодировка для цвета:

  • 10XX - для безопасных квадратов;
  • 11XX - для смертельных квадратов; а также
  • От 0000 до 0111 - для 8 типов квадратов ловушек.

Где Х обозначает неиспользованные биты. Учитывая, что только 2 из 16 цветов являются ловушками, которые будут использовать все 4 своих бита (и только если ловушка смещена, что будет иметь место 8 из 9 раз), то обычно будет 64 неиспользованных бита - теория состоит в том, что мутации, которые затрагивают любой из этих неиспользованных битов, не повредят геному, и стабильность лучше, чем любые причудливые решения, которые могут использовать эти оставшиеся биты.

Затем образцы используют это, чтобы спланировать безопасный маршрут в пределах сетки 7x7, центрированной на самих себе (5x5, на которую они видят, плюс 1 квадрат с каждой стороны, чтобы учесть смещенные ловушки), отдавая приоритет перемещению наибольшего расстояния вперед после 3 ходов.

Первоначально я начал строить некоторые проверки, чтобы убедиться, что тот факт, что цвет, на котором находится образец, не является летальным, соответствует геному и помечает любые ошибочные цвета как квадраты НЕИЗВЕСТНОЙ безопасности (и смежные с ними квадраты) - однако это добавило значительный сложность из-за незначительного выигрыша по сравнению с тем, чтобы пометить эти квадраты как БЕЗОПАСНЫЕ и убить несколько дополнительных образцов. Я вернусь к этому, если у меня будет время.

#include <initializer_list>
#include <vector>

enum class D { SAFE, LETHAL,TRAP_N, TRAP_NE, TRAP_E, TRAP_SE, TRAP_S, TRAP_SW, TRAP_W, TRAP_NW, UNSURE };
enum class X { SAFE, LETHAL, UNSURE };

inline void checkLocation( color_t color, D (&dna)[16], D check )
{
    if ( color != OUT_OF_BOUNDS && dna[color] == check )
        dna[color] = D::UNSURE;
}

inline void updateMapLocation( X (&map)[7][7], unsigned int x, unsigned int y, const X& safety ){
    if (        ( safety == X::LETHAL && map[x][y] != X::LETHAL )
            || ( safety == X::UNSURE && map[x][y] == X::SAFE ) )
        map[x][y] = safety;
}

inline unsigned int isSafePath( X (&map)[7][7], coord_t p )
{
    return map[p.x][p.y] == X::SAFE ? 1 : 0;
}
inline unsigned int isSafePath(X (&map)[7][7],coord_t p,coord_t q,coord_t r){
    if ( isSafePath( map,p ) )
        if ( isSafePath( map, q ) )
            return isSafePath( map, r );
    return 0;
}

inline unsigned int isSafeEast( X (&map)[7][7], coord_t p )
{
    if ( !isSafePath( map, p ) )
        return 0;
    if ( p.x == 6 )
        return 1;
    return isSafeEast(map,{p.x+1,p.y-1})
            +isSafeEast(map,{p.x+1,p.y+0})
            +isSafeEast(map,{p.x+1,p.y+1});
}

template<typename T> inline T max(T a,T b){return a>=b?a:b;}
template<typename T, typename... A> inline T max(T a,T b,A... c){return max(max(a,b),c...); }

coord_t cautiousSpecimins( dna_t d, view_t v ) {
    X map[7][7] = { { X::SAFE } };
    D dna[16] = { D::UNSURE };
    for ( color_t i = 0; i < 16; i++ )
    {
        if ( d[4*i] == 1 )
        {
            dna[i] = d[4*i + 1] == 1 ? D::LETHAL : D::SAFE;
        }
        else
        {
            switch ( dnarange( d, 4*i + 1, 3 ) )
            {
                case 0: dna[i] = D::TRAP_N; break;
                case 1: dna[i] = D::TRAP_NE; break;
                case 2: dna[i] = D::TRAP_E; break;
                case 3: dna[i] = D::TRAP_SE; break;
                case 4: dna[i] = D::TRAP_S; break;
                case 5: dna[i] = D::TRAP_SW; break;
                case 6: dna[i] = D::TRAP_W; break;
                case 7: dna[i] = D::TRAP_NW; break;
                default: dna[i] = D::UNSURE; break;
            }
        }
    }
    if ( v(-1, 0) != OUT_OF_BOUNDS )
        checkLocation( v( 0, 0), dna, D::LETHAL );

    if ( v(-1, 0) != OUT_OF_BOUNDS )
        for ( unsigned int y = 0; y < 7; ++ y )
            map[2][y] = X::LETHAL;

    if ( v(-2, 0) != OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 2; ++x )
            for ( unsigned int y = 0; y < 7; ++ y )
                map[x][y] = X::LETHAL;

    if ( v( 0, 1) == OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 7; ++x )
                map[x][4] = X::LETHAL;

    if ( v( 0, 2) == OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 7; ++x )
            for ( unsigned int y = 5; y < 7; ++ y )
                map[x][y] = X::LETHAL;

    if ( v( 0,-1) == OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 7; ++x )
                map[x][2] = X::LETHAL;

    if ( v( 0,-2) == OUT_OF_BOUNDS )
        for ( unsigned int x = 0; x < 7; ++x )
            for ( unsigned int y = 0; y < 2; ++ y )
                map[x][y] = X::LETHAL;

    checkLocation( v( 1, 1), dna, D::TRAP_SW );
    checkLocation( v( 1, 0), dna, D::TRAP_W  );
    checkLocation( v( 1,-1), dna, D::TRAP_NW );
    checkLocation( v( 0,-1), dna, D::TRAP_N  );
    checkLocation( v(-1,-1), dna, D::TRAP_NE );
    checkLocation( v(-1, 0), dna, D::TRAP_E  );
    checkLocation( v(-1, 1), dna, D::TRAP_SE );
    checkLocation( v( 0, 1), dna, D::TRAP_S  );

    for ( int x = 1; x <= 5; ++x )
    {
        for ( int y = 1; y <= 5; ++y )
        {
            switch( dna[v(x-3,y-3)] )
            {
                case D::LETHAL : updateMapLocation( map, x+0, y+0, X::LETHAL ); break;
                case D::TRAP_N : updateMapLocation( map, x+0, y+1, X::LETHAL ); break;
                case D::TRAP_NE: updateMapLocation( map, x+1, y+1, X::LETHAL ); break;
                case D::TRAP_E : updateMapLocation( map, x+1, y+0, X::LETHAL ); break;
                case D::TRAP_SE: updateMapLocation( map, x+1, y-1, X::LETHAL ); break;
                case D::TRAP_S : updateMapLocation( map, x+0, y-1, X::LETHAL ); break;
                case D::TRAP_SW: updateMapLocation( map, x-1, y-1, X::LETHAL ); break;
                case D::TRAP_W : updateMapLocation( map, x-1, y+0, X::LETHAL ); break;
                case D::TRAP_NW: updateMapLocation( map, x-1, y+1, X::LETHAL ); break;
//              case D::UNSURE : updateMapLocation( map, x+0, y+0, X::SAFE );
//                               updateMapLocation( map, x+0, y+1, X::UNSURE );
//                               updateMapLocation( map, x+1, y+1, X::UNSURE );
//                               updateMapLocation( map, x+1, y+0, X::UNSURE );
//                               updateMapLocation( map, x+1, y-1, X::UNSURE );
//                               updateMapLocation( map, x+0, y-1, X::UNSURE );
//                               updateMapLocation( map, x-1, y-1, X::UNSURE );
//                               updateMapLocation( map, x-1, y+0, X::UNSURE );
//                               updateMapLocation( map, x-1, y+1, X::UNSURE );
//                               break;
                default        : break;
            }           
        }
    }

    unsigned int north = isSafeEast(map,{4,4});
    unsigned int east  = isSafeEast(map,{4,3});
    unsigned int south = isSafeEast(map,{4,2});
    unsigned int mx    = max( north, east, south );
    unsigned int sz;
    std::vector<coord_t> dir;
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+1,+1} );
        if ( east  == mx ) dir.push_back( {+1,+0} );
        if ( south == mx ) dir.push_back( {+1,-1} );

        return dir[v.rng.rint(dir.size())];
    }


    north = isSafePath(map,{4,4},{5,5},{5,6})
            + isSafePath(map,{4,4},{4,5},{5,6});
    south = isSafePath(map,{4,2},{5,1},{5,0})
            + isSafePath(map,{4,2},{4,1},{5,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+1,+1} );
        if ( south == mx ) dir.push_back( {+1,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{3,4},{4,5},{5,6});
    south = isSafePath(map,{3,2},{4,1},{5,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+0,+1} );
        if ( south == mx ) dir.push_back( {+0,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = 2*isSafePath(map,{4,4},{4,5},{4,6})
            + 1*isSafePath(map,{4,4},{3,5},{4,6});
    south = 2*isSafePath(map,{4,2},{4,1},{4,0})
            + 1*isSafePath(map,{4,2},{3,1},{4,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+1,+1} );
        if ( south == mx ) dir.push_back( {+1,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{3,4},{4,5},{4,6})
            + isSafePath(map,{3,4},{3,5},{4,6});
    south = isSafePath(map,{3,2},{4,1},{4,0})
            + isSafePath(map,{3,2},{3,1},{4,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+0,+1} );
        if ( south == mx ) dir.push_back( {+0,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{2,4},{3,5},{4,6});
    south = isSafePath(map,{2,2},{3,1},{4,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {-1,+1} );
        if ( south == mx ) dir.push_back( {-1,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{3,4},{3,5},{3,6})
            + isSafePath(map,{3,4},{2,5},{3,6});
    south = isSafePath(map,{3,2},{3,1},{3,0})
            + isSafePath(map,{3,2},{2,1},{3,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {+0,+1} );
        if ( south == mx ) dir.push_back( {+0,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    north = isSafePath(map,{2,4},{3,5},{4,6});
    south = isSafePath(map,{2,2},{3,1},{4,0});
    mx = max( north, south );
    if ( mx > 0 )
    {
        if ( north == mx ) dir.push_back( {-1,+1} );
        if ( south == mx ) dir.push_back( {-1,-1} );

        return dir[v.rng.rint(dir.size())];
    }

    return {-1,-1};
}

Примерные результаты:

Scores: 421155 2 129418 71891 90635 1 211 1111987 29745 7 2200750 41793 50500 45 2012072 2 485698 1 110061 1554720 210308 249336 2 1 262110 17 3 19 1719139 23859 45118 3182784 318 2 1 15572 14 2822954 18 11 2 3 15954 1331392 2296280 135015 1 360826 1 692367 4 244775 4814645 3749144 3 1 660000 1 11 3688002 3920202 3428464 123053 1 243520 86 9 6 289576 195966 549120 220918 9 1 43 71046 5213 118177 150678 54639 3 200839 1 3 6 1978584 1514393 119502 1 1 137695 184889 337956 1 1 441405 133902 991 1 4137428 1 1427115 3340977 1 2 1 55559 11 1 94886 30270 1 6 3 69394 264780 6877 47758 128568 1 116672 130539 163747 96253 1 2654354 1 141 58212 1613661 27 9504 1 2474022 843890 1 59 3110814 2353731 150296 313748 2590241 6 5970407 1434171 2 334715 141277 1 56810 2964306 51544 61973 715590 1 106 900384 50948 2 34652 108096 391006 1 2969764 47625 1 24 30481 44 8 1 18 2094036 106461 3080432 75 620651 16 71730 282145 275031 17 1 8 15 121731 18 2 1 1 495868 3252390 6 1 63712 7 3733149 13380 1 1
Geometric mean score: 2030.17

Максимальный балл за время тестирования: 8 150 817 экземпляров сохранено.


Теперь вы сделали это ... Я хотел сохранить путь на потом, но я не мог оставить без внимания ваших осторожных грызунов :) Как оказалось, путь работает еще лучше с более эффективным кодированием. Ваша идея расширить область прохождения до 7x7 также выглядит многообещающей. Я посмотрю, смогу ли я использовать это.

В настоящее время я делаю 2000 прогонов для этого ... после первых 900 среднее значение, по-видимому, составляет около 600, что довольно далеко от 2000. Не могли бы вы повторить его и на своем конце, чтобы увидеть, был ли 2000 просто случайность?
Мартин Эндер
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.