Lab Rat Race: вправа на генетичні алгоритми


113

Це Fortnightly Challenge # 3. Тема: Генетичні алгоритми

Цей виклик - трохи експеримент. Ми хотіли подивитися генетичні алгоритми, що ми можемо зробити, з викликом складності. Не все може бути оптимальним, але ми намагалися зробити це доступним. Якщо це вийде, хто знає, що ми можемо побачити в майбутньому. Може, генетичний Король гори?

Специфікація досить довга! Ми намагалися відокремити специфікацію на «Основи» - найголовніший мінімум, який потрібно знати, щоб почати грати з фреймворком та подати відповідь - та «The Gory Details» - повну специфікацію із усіма подробицями про контролер, на основі яких ви міг написати свій.
Якщо у вас є якісь питання, сміливо приєднуйтесь до нас у чаті!

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

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

Приклад зображення використовуваної дошки

† 84 465 екземплярів було завдано шкоди при здійсненні цього виклику.

Основи

Це гра з одиночним гравцем (ви та ваші колеги не хотіли змішувати популяцію, тому кожен побудував власну гоночну доріжку). Гоночна доріжка - це прямокутна сітка, 15 комірок заввишки та 50 комірок завширшки. Ви починаєте з 15 зразків на випадкових (не обов'язково виразних) клітинках з лівого краю (де х = 0 ). Ваші екземпляри повинні намагатися досягти мети, яка є будь-якою клітиною при x ≥ 49 та 0 ≤ y ≤ 14 (екземпляри можуть перевернути доріжку праворуч). Щоразу, коли це відбувається, ви отримуєте бал. Ви також починаєте гру з 1 очка. Вам слід спробувати максимізувати свої очки після 10000 оборотів.

Кілька зразків можуть займати одну клітинку і не взаємодіють.

На кожному кроці кожен зразок бачить 5х5 сітку свого оточення (із самим собою в центрі). Кожна комірка цієї сітки буде містити колір -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 . Контролер генерує дошки, запускає гру та забезпечує основу для генетичного алгоритму. Все, що вам потрібно зробити - це забезпечити функцію переміщення.

Зачекайте, так що саме я роблю з геномом?

Завдання - це зрозуміти!

Оскільки зразки не мають пам’яті, все, що ви отримали за певну чергу, є сіткою кольорів 5x5, які для вас нічого не означають. Тож вам доведеться використовувати геном для досягнення мети. Загальна ідея полягає в тому, що ви використовуєте частини геному для зберігання інформації про кольори або компонування сітки, а ваш бот базує свої рішення на додатковій інформації, що зберігається в геномі.

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

Це має бути достатньо інформації для початку роботи. Якщо ви хочете, ви можете пропустити наступний розділ і виберіть контролера за вибором зі списку контролерів у нижній частині (який також містить інформацію про те, як використовувати саме цей контролер).

Читайте далі, якщо ви хочете все ...

Деталі Горі

Ця специфікація завершена. Усі контролери повинні виконувати ці правила.

У всіх випадкових випадках використовується рівномірний розподіл, якщо не зазначено інше.

Генерація треку:

  • Доріжка - це прямокутна сітка, X = 53 комірки в ширину і Y = 15 комірок у висоту. Клітини з x ≥ 49 - це клітини цілі (де x заснований на нулі).
  • Кожна клітина має один колір і може бути або не бути летальною - клітини не є летальними, якщо не визначено одним із типів клітин нижче.
  • Є 16 різних кольорів клітин, позначених міткою від 0до 15, значення яких буде змінюватися від гри до гри. Крім того, -1являє собою клітини, що знаходяться поза межами - це летально .
  • Виберіть 8 випадкових кольорів . Це будуть порожні клітини (які не мають ефекту).
  • Виберіть ще 4 випадкових кольори . Це телепортери. Для двох із цих кольорів виберіть ненульове зміщення в районі 9x9 (від (-4, -4) до (4,4), крім (0,0)). Для двох інших кольорів переверніть ці компенсації. Якщо зразок наступає на телепортатор, він негайно переміщується цим зсувом.
  • Виберіть ще 2 випадкових кольори . Це пастки. Для кожного з цих кольорів виберіть зміщення в районі 3x3 (від (-1, -1) до (1,1)). Пастка вказує, що клітина при цьому зміщенні є летальною . Примітка. Сама клітина пастки не обов'язково є летальною.
  • Два решти кольору - це стіни, які перешкоджають руху. Спроба перейти на стінну камеру перетворить цей крок у нерухоме. Самі клітини стінки є летальними .
  • Для кожної нецільової комірки сітки виберіть випадковий колір. Для кожної цільової комірки виберіть випадковий порожній колір.
  • Для кожної комірки з лівого краю колії визначте, чи можна досягти мети протягом 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.
  • Досить повільно.

Java

  • Не особливо повільно. Не особливо швидко.

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

Усі оцінки попередні. Тим не менш, якщо щось не так або застаріло, будь ласка, повідомте мене про це. Наш приклад подано для порівняння, але не заперечує.

  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. Концепція виклику з Короля-Хілл перетворилася на щурячу гонку.
  • трихоплакс: ігрові тести. Працював на контролері Python.
  • feersum: Написав контролер C ++.
  • VisualMelon: Написав контролер C #.
  • Мартін Бюттнер: Концепція. Написав контролер Ruby. Ігрове тестування. Працював на контролері Python.
  • T Авраам: Playtesting. Протестований Python та переглянуті контролери C # і C ++.

Усі перераховані вище користувачі (і, мабуть, ще пару я забув) внесли свій внесок у загальний дизайн завдання.

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

Якщо ви використовуєте C ++ з Visual Studio і багатопотоковою програмою, вам слід отримати останнє оновлення через помилку з висіванням їх генератора випадкових чисел, що дозволяє виробляти дублюючі дошки.


3
Не міг би хтось просто скласти генетичний генетичний алгоритм, щоб знайти оптимальний генетичний алгоритм для цієї проблеми?
mbomb007

1
@ anon3202 Ну, це, звичайно, дасть вам більше інформації про компонування доріжок, оскільки ви могли оцінити, де ви. По суті, ми хотіли, щоб інтерфейс для ботів був простим і робив це чисто локальною проблемою, де вам знадобиться геном, щоб дізнатися, яке місцеве рішення є найбільш корисним для вашого глобального прогресу.
Мартін Ендер

1
@matovitch Дивіться розділ 5 розділу " Порядок повороту " деталей про Горі (повна специфікація):'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.'
трихоплакс

1
Я підробив код C ++, щоб показати середнє значення вибірки, stddev, stderr та 99% конф-інтервал (перед вашим "геометричним" журналом / досвідом), і зробив вражаюче відкриття. Відповідь "Сліпої віри" мала "Середнє значення зразка 116529 + - 2,78337e + 010 (99%) stddev = 7,77951e + 010" після 50 циклів. "Переміщення довірчого інтервалу до 50% суттєво не покращує питання. Геометричне середнє було стабільнішим, хоча: "Середнє значення 159,458 + - 117262 (99%) stddev = 32,6237" (До його оновлення на 800 балів)
Mooing Duck

1
Я зробив кілька експериментів зі швидкістю мутації, і я думаю, що виклик був би цікавішим (і контролери працювали б набагато швидше), якби ймовірність була підвищена з .01 до .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. пастка -> щур вважатиме позицію пастки поганою, а клітина вказує на пастку хорошу .
    Хоча я лінивий перейменувати це, це швидше "детектор небезпеки", що вказує на (передбачуване) місце фактичної пастки, стіни, телепортатора, що чекає, щоб відправити нічого підозрюючого мандрівника в неприємне місце або навіть вхід мертвого -закінчити. Одним словом, місце, куди б не ходив мудрий щур.

хороші або погані гени мають лише 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.

Телепортери

Ефект телепортера дуже залежить від позиції. До цих пір я із задоволенням вважав телепортера як завжди хорошим (сприймається як порожнє місце) або завжди поганим (розглядається як стіна, щоб жоден гризун не сприймав його).

Це занадто груба модель.
Даний телепортер може просунути щура вперед до декількох осередків від мети, але одного разу той самий телепортер може відкинути щура з дошки.
Такий телепортатор, швидше за все, буде визнаний прохідним (оскільки він збільшує придатність швидше, ніж при «ходьбі» до того ж місця x), стане частиною домінуючого геному і вб'є майже всіх щурів, які вважають його «завжди безпечним».
Оскільки щури не мають можливості знати своє положення Х, єдине рішення для виявлення цих підступних телепортерів - це вирішити, чи слід наступати на них, грунтуючись на єдиних наявних контекстних даних, тобто кольоровій сітці 5x5.

Для цього я визначив 4 типи кольорових генів:

  • детектор пастки небезпеки
  • порожній прохідний кудись на трасі
  • блокувати заборонено будь-де на трасі
  • промінь розглядається як порожній або блоковий залежно від оточення

Ідея полягає у тому, щоб спробувати розрізнити телепортатора, поглянувши на своїх найближчих 8 сусідів. Оскільки ймовірність наявності 8 однакових сусідів у даному місці дуже низька, це повинно дозволяти ідентифікувати унікальний примірник кожного телепортатора.

8 сусідніх кольорів можна комбінувати для формування локального підпису, який є інваріантним відповідно до положення в лабіринті. На жаль, 8 сусідів видно лише для комірок, розташованих у внутрішньому квадраті поля зору 3x3, тому підписи будуть неточними на краю поля зору.
Тим не менш, це дасть нам постійну контекстну інформацію в безпосередньому сусідстві, що достатньо для збільшення ймовірності успішного навігації телепортерів.

пучки генів мають 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; 
}

Java

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 ++ дуже рекомендується для його швидкості. Навіть після того, як я закінчив перекладати свою програму python на C ++, моделювання пітона все ще не припинилося.

Це вдосконалений варіант 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-х ..., що може бути гнітюче, коли ви бачите, як консоль виписує один за одним. Як планета з усіма необхідними для життя, але ніякими ознаками передових цивілізацій щурів ...
Тоді випадковий сплеск. :)

Хм ... мабуть, мені пощастило за мою 1-ю партію пробіжок, отримуючи геометричну 300+. Оцінки дійсно коливаються зовсім небагато. Але в будь-якому випадку, з більшою кількістю запусків симулятора, напевно, це ближче до ≈ 74. (Thx feersum за допомогу мені імітувати та його палаючу швидку програму)

Оцінки з моїх пробігів: 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 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 68594 145434 87763 150187 169 185518 1 1 1 1 24208 2570 1 1 537 1 1 462284 1 2 55 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

Єпископ завжди рухається по діагоналі, тому половина дошки є недоступною для певного шляху по всій дошці, але це означає, що менша кількість потенційних рухів для кодування, тому кожен окремий біт геному може представляти рух (єпископ ніколи не відступає). Котрий біт для посилання визначається на основі блоку 3x3 квадратів вперед (праворуч) зразка. Найкращий крок для даної ситуації - це лише колись одна мутація.

Цей бот спочатку швидко вчиться, але потім часто потрапляє в стелю, перш ніж досягти фінішу, імовірно, там, де виникає одна з наступних двох проблем:

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

Код

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.1 ГГц з 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 (пробіг 2К)

Мій підхід:

  1. Дізнайтеся, що вбиває видів, і визначте бажану поведінку (функціональну)
  2. Реалізація бажаної поведінки в коді (технічному)
  3. Дайте пріоритет . Це важливіше чи менш важливо, ніж інша бажана поведінка.
  4. Оптимізуйте середній геометричний бал, налаштувавши параметри розчинів.

Я випробував понад 2000 наборів параметрів з тими ж 50 насінням. Були відібрані найперспективніші набори, які були забиті за допомогою 250 однакових насінин, а ті, що мають найвищий ранг, були подачею для наступного раунду тесту. Тому мені вдалося створити генетичний алгоритм, щоб знайти оптимальний генетичний алгоритм для цієї проблеми, як це запропонував користувач mbomb007 .

Бажана поведінка:

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

Способи зберігання даних:

Ми хочемо, щоб вид навчився речам, пристосувався до його оточення, став найбільш придатним. Це неминуче працює лише, якщо навчання можна якось зберігати. Навчання буде «зберігатися» у 100 бітах ДНК. Це дивний спосіб зберігання, оскільки ми не можемо змінити значення нашої ДНК. Отже, ми припускаємо, що ДНК вже зберігає інформацію про погані та добрі кроки. Якщо для певного виду правильна інформація зберігається в його ДНК, він буде рухатися швидко вперед і виробляти багато нових видів з його ДНК.

Я з’ясував, що середній геометричний бал чутливий до того, як зберігається інформація. Давайте припустимо, що ми читаємо перші 4 біти зі 100 біт ДНК і хочемо зберігати це в цілій змінній. Це можна зробити кількома способами:

  1. десяткове зберігання даних: використовуючи вбудовану dnarangeфункцію, приклад: 10114 біти стануть `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функції (визначеної нижче), приклад: 4 біт 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змінну). Найправіший стовпець у цьому полі 3х3 веде.
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 (2К пробіжки)

Перші 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

Визначте пастки:

Я досліджував ДНК виду з найвищим балом, коли довільна гра закінчилася з використанням накопичувача bitsum4 (тому оцінка кольорів має діапазон [0,4]):

  • оцінка 0: Телепорт назад, обидві стіни, 1x сейф
  • забив 1: Пастка назад (так нешкідливо), Телепорт назад, 1x сейф
  • забив 2: Пастка вперед (настільки небезпечно), 1x безпечно
  • забив 3: Телепорт вперед, 5-кратний сейф
  • забив 4: Телепорт вперед, 1x сейф

З цього можна зробити висновок, що стіни та телепорти отримують правильну оцінку. Пастки не визначаються, оскільки вони залежать від напрямку та кольору походження, в той час як оцінка проводиться за кольором призначення. Тому є необхідність також зберігати дані про колір походження, так v(0,0). В ідеальному світі ми хотіли б зберігати інформацію для 16 кольорів х 8 напрямків х 3 біт = 384 біта.

На жаль, доступно лише 100 біт, і ми не можемо використовувати це все, оскільки нам також потрібна пам'ять для рішення, поясненого вище. Тому ми зробимо 4 кольорових бункера:

  • 0: колір 0 - колір 3,
  • 1: колір 4 - колір 7,
  • 2: колір 8 - колір 11,
  • 3: колір 12 - колір 16

і 4 бункери для напрямку руху

  • 0: переміщення вертикально або назад,
  • 1: рухатися вперед,
  • 2: рухатися вперед,
  • 3: рухатися вперед вниз

Коли десяткова оцінка дорівнює 4 або вище (100,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 (2К пробіжки)

Перші 50 балів (13 ігор набрали лише 1 бал):

28,044 14,189 1 2,265,670 2,275,942 3 122,769 109,183 401,366 61,643 205,949 47,563 138,680 1 107,199 85,666 31 2 29 1 89,519 22 100,908 14,794 1 3,198,300 21,601 14 3,405,278 1 1 1 2 74,167 1 71,242 97,331 201,080 1 1 102 1 104 94,444 2 95 1 44 94 444

Неправильне припущення про стіну дублюється багато разів новонародженим дебілами:

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

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

Бійці дебілів: Рішення 1: інверсія кольору

Першим рішенням я спробував використовувати частину невикористаної пам'яті, яка все ще дуже різноманітна. Припустимо, ви виділили 84 біт на вашу кольорову пам'ять і пам'ять пошуку пастки. Решта 16 біт будуть дуже різноманітними. Ми можемо заповнити 2 десяткових змінних, які мають значення на проміжку [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-2x16 = 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 (2К пробіжки)

Перші 50 балів (9 ігор набрали лише 1 бал):

112 747 3 1 1,876,965 8 57 214,921 218,707 2,512,937 114,389 336,941 1 6,915 2 219,471 74,289 31 116 133,162 1 5 633,066 166,473 515,204 1 86,744 17,360 2 190,697 1 6 122 126,399 399,045 1 4,172,168 1 169,119 4,990 1,99 1,99 1, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 419, 1

Це моя найперша програма C ++. Я зараз, як і більшість із вас, займаюся аналізом на гноми. Хочу подякувати організаторам, оскільки мені дуже сподобалось працювати над цим.

Якщо у вас є відгуки, будь ласка, залиште коментар нижче. Вибачення за довгі тексти.


Я вважаю ваш аналіз пасток досить цікавим.

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

Також ви розглядали можливість додавання алгоритму трасування? Це, здається, є величезним фактором поліпшення незалежно від геному, оскільки він обмежить переходи до тих, які перевіряють здатність геному лише по трасах, які набагато частіше призводять до виграшної позиції.

kuroi, дякую за відгук. Я не пробував xoring, оскільки я не такий знайомий з бінарними операціями в c ++. Я припускаю, що ви маєте на увазі 12,5 8 біт слів? Ви використовуєте xoring?
Руут

Ви можете подивитися на мій код "твердих віруючих", щоб побачити, яку функцію хешу я використовую. В основному я пропускаю позашляхові комірки і розглядаю кольори на треку як частини високого та низького порядку 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

@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 випадків". Я хочу, щоб показник оцінювання був нагороджений цим
Спарр

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

В основному всі клітини, що сидять на сірому квадраті, щуром вважаються стінами.
Великі Х представляють клітини, що розглядаються як пастки, з відповідними восьмикутниками, що вказують на детектор, який повідомляв про них.

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

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

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

Код

Ну це безлад, але він працює досить добре.

Поглянувши на код гравця, я додав лише один інтерфейс: функцію відстеження, яка використовується для повідомлення значення ДНК. У моєму випадку я використовував 3 типи (настінний, детектор пасток і порожній), але ви можете виводити будь-що, що стосується кольорів (або взагалі нічого, якщо ви не хочете графіки, пов'язаної з геномом).

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

Це означає, що результати будуть дійсно значущими лише в тому випадку, якщо бот не використовує випадкових значень. В іншому випадку відображені шляхи представлятимуть лише один можливий результат.

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

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

Якщо хтось зацікавлений, я можу опублікувати код.

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

Чому проміжний вихід?

Перш за все, C ++ не має пристойної портативної графічної бібліотеки, про яку можна говорити, особливо при використанні MSVC. Навіть якщо побудови Win32 зазвичай доступні, вони часто надходять задумливо, а кількість зовнішніх бібліотек, пакунків та інших уніфік-подібних приємностей робить написання швидкої та простої графічної програми жахливим болем у тілі свого тіла, що порядність перешкоджає мене від називання.

Я вважав, що використання Qt (про єдине середовище, яке робить портативний графічний інтерфейс / графічний розвиток в C ++ простою і навіть приємною задачею, IMHO - можливо, тому що він додає систему обміну повідомленнями a la Objective C, якої C ++ дуже не вистачає і робить неймовірну роботу з обмеження пам'яті управління до найменшого мінімуму), але це виглядало як надмірне завдання для підручного завдання (і кожен, хто хоче скористатися кодом, повинен був би встановити великий пакет SDK - напевно, варті зусиль).

Навіть якщо припустити переносну бібліотеку, не потрібно говорити про швидкість (однієї секунди або близько того, щоб створити картину, в основному достатньо), і Cver зі своєю прихильною жорсткістю та властивою безладністю, безумовно, не є найкращим інструментом для роботи.

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

Чому 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 х 8 біт для пасток, перші 4 біти - кольорове число, інші 4 - зміщення
  • 2 х 4 біти для стін, просто колір
  • 4 х 7 біт для телепортерів, знову 4 біти для кольору, 3 для вирішення добра чи поганої

Це складає 52 біти. Однак я використовую лише перший біт з 3 дешифрів телепортера (я перевіряю, чи число більше 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 ++ необхідний, щоб провести більше тестів і отримати більш значущий результат.

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

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

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

Я повинен якось використовувати другу половину свого геному.


Я також намагаюся зберігати кольори, але, врешті-решт, дійшов висновку, що це не працює, тому що ви отримаєте парні. Наприклад, якщо 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, NeighborsOfNeighbors, оцінка = 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, крім додавання втраченого відступу).
трихоплакс

Працюючи в python 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]
трихоплакс

Середнє геометричне значення 428.3750848244933
трихоплакс

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

Сірий колір Lookahead (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 2 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

Я б хотів, щоб я міг отримати більше 2007-х і менше 1-х. :)


1
Використання сірого коду - ідея сірого! ;)
matovitch

1
+1 для сірих кодів. Однак повністю стійкий до мутації геном зашкодить різноманітності досить сильно. А Btw бал у 20 000 навіть не є максимумом, який ви можете досягти. Якщо якийсь щур розвиває здатність бігти доріжку з будь-якого можливого стартового місця, він стає фактично безсмертним і набуває величезний бал у фітнесі. Її геном швидко домінує, що призводить до популяції до майже 50 КТ щурів і рахунку в кілька мільйонів.

2

C ++, TripleScore, оцінка: 100 ~ 400

Перш за все, моя оцінка сильно варіюється за кілька пробігів (головним чином через кількість 1-х).

Ядро обчислює бал з 5 напрямків: вгору, вниз, вперед, вперед і вперед-вниз. Спочатку підраховується оцінка вгору і вниз, ніж результати порівнюються зі значенням перебування на місці. Якщо перебування на місці краще, ніж рух вгору або вниз, ці напрямки не вибиратимуться (тому він повинен рухатися вперед). Це потрібно для запобігання підстрибування (вгору, вниз, вгору, вниз, ...) між 2 плямами.

Тепер набрані 3 інші напрямки: вперед, прямо вперед і вперед. З усіх досліджуваних напрямків зберігаються ті, що мають найвищий бал, і 1 з них вибирається навмання.

Оцінка напряму: TripleScore обчислює результат руху за допомогою 3 підскорень:

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

Як і в інших відповідях, оцінка значною мірою залежить від кількості повернених 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та забуті дужки, і навіть після виправлення всього цього ви не обмежуєте значення RNG, тому ви отримуєте порожній напрямок. Це псевдокод, чи щось покликане виконувати з якимось діалектом Ruby?

2

Java, RunningStar, оцінка = 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

LeapForward, 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

Обережні екземпляри - С ++ - набирає приблизно 2030 року за 200 пробіжок

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

Кодування кольору є або:

  • 10XX - для безпечних квадратів;
  • 11XX - для летальних квадратів; і
  • 0000 - 0111 - для пасток з 8 типів.

Де X позначає невикористані біти. Зважаючи на те, що лише 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.